libzypp  17.29.4
TargetImpl.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <string>
16 #include <list>
17 #include <set>
18 
19 #include <sys/types.h>
20 #include <dirent.h>
21 
22 #include <zypp/base/LogTools.h>
23 #include <zypp/base/Exception.h>
24 #include <zypp/base/Iterator.h>
25 #include <zypp/base/Gettext.h>
26 #include <zypp/base/IOStream.h>
27 #include <zypp/base/Functional.h>
28 #include <zypp-core/base/UserRequestException>
29 #include <zypp/base/Json.h>
30 
31 #include <zypp/ZConfig.h>
32 #include <zypp/ZYppFactory.h>
33 #include <zypp/PathInfo.h>
34 
35 #include <zypp/PoolItem.h>
36 #include <zypp/ResObjects.h>
37 #include <zypp/Url.h>
38 #include <zypp/TmpPath.h>
39 #include <zypp/RepoStatus.h>
40 #include <zypp/ExternalProgram.h>
41 #include <zypp/Repository.h>
42 #include <zypp/ShutdownLock_p.h>
43 
44 #include <zypp/ResFilters.h>
45 #include <zypp/HistoryLog.h>
46 #include <zypp/target/TargetImpl.h>
51 
54 
55 #include <zypp/sat/Pool.h>
56 #include <zypp/sat/detail/PoolImpl.h>
57 #include <zypp/sat/SolvableSpec.h>
58 #include <zypp/sat/Transaction.h>
59 
60 #include <zypp-core/base/String.h>
61 #include <zypp-core/base/StringV.h>
62 #include <zypp-core/zyppng/base/EventLoop>
63 #include <zypp-core/zyppng/io/AsyncDataSource>
64 #include <zypp-core/zyppng/io/Process>
65 #include <zypp-core/base/IOTools.h>
66 #include <zypp-core/zyppng/rpc/rpc.h>
67 #include <zypp-core/zyppng/base/private/linuxhelpers_p.h>
68 #include <zypp-core/zyppng/base/EventDispatcher>
69 #include <zypp-proto/commit.pb.h>
70 #include <zypp-proto/envelope.pb.h>
71 #include <zypp-core/zyppng/rpc/zerocopystreams.h>
72 
74 
75 #include <zypp/PluginExecutor.h>
76 
77 // include the error codes from zypp-rpm
78 #include "tools/zypp-rpm/errorcodes.h"
79 #include <rpm/rpmlog.h>
80 
81 #include <optional>
82 
83 using std::endl;
84 
86 extern "C"
87 {
88 #include <solv/repo_rpmdb.h>
89 #include <solv/chksum.h>
90 }
91 namespace zypp
92 {
93  namespace target
94  {
95  inline std::string rpmDbStateHash( const Pathname & root_r )
96  {
97  std::string ret;
98  AutoDispose<void*> state { ::rpm_state_create( sat::Pool::instance().get(), root_r.c_str() ), ::rpm_state_free };
99  AutoDispose<Chksum*> chk { ::solv_chksum_create( REPOKEY_TYPE_SHA1 ), []( Chksum *chk ) -> void {
100  ::solv_chksum_free( chk, nullptr );
101  } };
102  if ( ::rpm_hash_database_state( state, chk ) == 0 )
103  {
104  int md5l;
105  const unsigned char * md5 = ::solv_chksum_get( chk, &md5l );
106  ret = ::pool_bin2hex( sat::Pool::instance().get(), md5, md5l );
107  }
108  else
109  WAR << "rpm_hash_database_state failed" << endl;
110  return ret;
111  }
112 
113  inline RepoStatus rpmDbRepoStatus( const Pathname & root_r )
114  { return RepoStatus( rpmDbStateHash( root_r ), Date() ); }
115 
116  } // namespace target
117 } // namespace
119 
121 namespace zypp
122 {
124  namespace
125  {
126  // HACK for bnc#906096: let pool re-evaluate multiversion spec
127  // if target root changes. ZConfig returns data sensitive to
128  // current target root.
129  inline void sigMultiversionSpecChanged()
130  {
132  }
133  } //namespace
135 
137  namespace json
138  {
139  // Lazy via template specialisation / should switch to overloading
140 
141  template<>
142  inline std::string toJSON( const ZYppCommitResult::TransactionStepList & steps_r )
143  {
144  using sat::Transaction;
145  json::Array ret;
146 
147  for ( const Transaction::Step & step : steps_r )
148  // ignore implicit deletes due to obsoletes and non-package actions
149  if ( step.stepType() != Transaction::TRANSACTION_IGNORE )
150  ret.add( step );
151 
152  return ret.asJSON();
153  }
154 
156  template<>
157  inline std::string toJSON( const sat::Transaction::Step & step_r )
158  {
159  static const std::string strType( "type" );
160  static const std::string strStage( "stage" );
161  static const std::string strSolvable( "solvable" );
162 
163  static const std::string strTypeDel( "-" );
164  static const std::string strTypeIns( "+" );
165  static const std::string strTypeMul( "M" );
166 
167  static const std::string strStageDone( "ok" );
168  static const std::string strStageFailed( "err" );
169 
170  static const std::string strSolvableN( "n" );
171  static const std::string strSolvableE( "e" );
172  static const std::string strSolvableV( "v" );
173  static const std::string strSolvableR( "r" );
174  static const std::string strSolvableA( "a" );
175 
176  using sat::Transaction;
177  json::Object ret;
178 
179  switch ( step_r.stepType() )
180  {
181  case Transaction::TRANSACTION_IGNORE: /*empty*/ break;
182  case Transaction::TRANSACTION_ERASE: ret.add( strType, strTypeDel ); break;
183  case Transaction::TRANSACTION_INSTALL: ret.add( strType, strTypeIns ); break;
184  case Transaction::TRANSACTION_MULTIINSTALL: ret.add( strType, strTypeMul ); break;
185  }
186 
187  switch ( step_r.stepStage() )
188  {
189  case Transaction::STEP_TODO: /*empty*/ break;
190  case Transaction::STEP_DONE: ret.add( strStage, strStageDone ); break;
191  case Transaction::STEP_ERROR: ret.add( strStage, strStageFailed ); break;
192  }
193 
194  {
195  IdString ident;
196  Edition ed;
197  Arch arch;
198  if ( sat::Solvable solv = step_r.satSolvable() )
199  {
200  ident = solv.ident();
201  ed = solv.edition();
202  arch = solv.arch();
203  }
204  else
205  {
206  // deleted package; post mortem data stored in Transaction::Step
207  ident = step_r.ident();
208  ed = step_r.edition();
209  arch = step_r.arch();
210  }
211 
212  json::Object s {
213  { strSolvableN, ident.asString() },
214  { strSolvableV, ed.version() },
215  { strSolvableR, ed.release() },
216  { strSolvableA, arch.asString() }
217  };
218  if ( Edition::epoch_t epoch = ed.epoch() )
219  s.add( strSolvableE, epoch );
220 
221  ret.add( strSolvable, s );
222  }
223 
224  return ret.asJSON();
225  }
226  } // namespace json
228 
230  namespace target
231  {
233  namespace
234  {
237  class AssertProcMounted
238  {
239  NON_COPYABLE(AssertProcMounted);
240  NON_MOVABLE(AssertProcMounted);
241  public:
242 
243  AssertProcMounted( Pathname root_r )
244  {
245  root_r /= "/proc";
246  if ( ! PathInfo(root_r/"self").isDir() ) {
247  MIL << "Try to make sure proc is mounted at" << _mountpoint << endl;
248  if ( filesystem::assert_dir(root_r) == 0
249  && execute({ "mount", "-t", "proc", "proc", root_r.asString() }) == 0 ) {
250  _mountpoint = std::move(root_r); // so we'll later unmount it
251  }
252  else {
253  WAR << "Mounting proc at " << _mountpoint << " failed" << endl;
254  }
255  }
256  }
257 
258  ~AssertProcMounted( )
259  {
260  if ( ! _mountpoint.empty() ) {
261  // we mounted it so we unmount...
262  MIL << "We mounted " << _mountpoint << " so we unmount it" << endl;
263  execute({ "umount", "-l", _mountpoint.asString() });
264  }
265  }
266 
267  private:
268  int execute( ExternalProgram::Arguments && cmd_r ) const
269  {
270  ExternalProgram prog( cmd_r, ExternalProgram::Stderr_To_Stdout );
271  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
272  { DBG << line; }
273  return prog.close();
274  }
275 
276  private:
277  Pathname _mountpoint;
278  };
279  } // namespace
281 
283  namespace
284  {
285  SolvIdentFile::Data getUserInstalledFromHistory( const Pathname & historyFile_r )
286  {
287  SolvIdentFile::Data onSystemByUserList;
288  // go and parse it: 'who' must constain an '@', then it was installed by user request.
289  // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
290  std::ifstream infile( historyFile_r.c_str() );
291  for( iostr::EachLine in( infile ); in; in.next() )
292  {
293  const char * ch( (*in).c_str() );
294  // start with year
295  if ( *ch < '1' || '9' < *ch )
296  continue;
297  const char * sep1 = ::strchr( ch, '|' ); // | after date
298  if ( !sep1 )
299  continue;
300  ++sep1;
301  // if logs an install or delete
302  bool installs = true;
303  if ( ::strncmp( sep1, "install|", 8 ) )
304  {
305  if ( ::strncmp( sep1, "remove |", 8 ) )
306  continue; // no install and no remove
307  else
308  installs = false; // remove
309  }
310  sep1 += 8; // | after what
311  // get the package name
312  const char * sep2 = ::strchr( sep1, '|' ); // | after name
313  if ( !sep2 || sep1 == sep2 )
314  continue;
315  (*in)[sep2-ch] = '\0';
316  IdString pkg( sep1 );
317  // we're done, if a delete
318  if ( !installs )
319  {
320  onSystemByUserList.erase( pkg );
321  continue;
322  }
323  // now guess whether user installed or not (3rd next field contains 'user@host')
324  if ( (sep1 = ::strchr( sep2+1, '|' )) // | after version
325  && (sep1 = ::strchr( sep1+1, '|' )) // | after arch
326  && (sep2 = ::strchr( sep1+1, '|' )) ) // | after who
327  {
328  (*in)[sep2-ch] = '\0';
329  if ( ::strchr( sep1+1, '@' ) )
330  {
331  // by user
332  onSystemByUserList.insert( pkg );
333  continue;
334  }
335  }
336  }
337  MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
338  return onSystemByUserList;
339  }
340  } // namespace
342 
344  namespace
345  {
346  inline PluginFrame transactionPluginFrame( const std::string & command_r, ZYppCommitResult::TransactionStepList & steps_r )
347  {
348  return PluginFrame( command_r, json::Object {
349  { "TransactionStepList", steps_r }
350  }.asJSON() );
351  }
352  } // namespace
354 
357  {
358  unsigned toKeep( ZConfig::instance().solver_upgradeTestcasesToKeep() );
359  MIL << "Testcases to keep: " << toKeep << endl;
360  if ( !toKeep )
361  return;
362  Target_Ptr target( getZYpp()->getTarget() );
363  if ( ! target )
364  {
365  WAR << "No Target no Testcase!" << endl;
366  return;
367  }
368 
369  std::string stem( "updateTestcase" );
370  Pathname dir( target->assertRootPrefix("/var/log/") );
371  Pathname next( dir / Date::now().form( stem+"-%Y-%m-%d-%H-%M-%S" ) );
372 
373  {
374  std::list<std::string> content;
375  filesystem::readdir( content, dir, /*dots*/false );
376  std::set<std::string> cases;
377  for_( c, content.begin(), content.end() )
378  {
379  if ( str::startsWith( *c, stem ) )
380  cases.insert( *c );
381  }
382  if ( cases.size() >= toKeep )
383  {
384  unsigned toDel = cases.size() - toKeep + 1; // +1 for the new one
385  for_( c, cases.begin(), cases.end() )
386  {
387  filesystem::recursive_rmdir( dir/(*c) );
388  if ( ! --toDel )
389  break;
390  }
391  }
392  }
393 
394  MIL << "Write new testcase " << next << endl;
395  getZYpp()->resolver()->createSolverTestcase( next.asString(), false/*no solving*/ );
396  }
397 
399  namespace
400  {
401 
412  std::pair<bool,PatchScriptReport::Action> doExecuteScript( const Pathname & root_r,
413  const Pathname & script_r,
415  {
416  MIL << "Execute script " << PathInfo(Pathname::assertprefix( root_r,script_r)) << endl;
417 
418  HistoryLog historylog;
419  historylog.comment(script_r.asString() + _(" executed"), /*timestamp*/true);
420  ExternalProgram prog( script_r.asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
421 
422  for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() )
423  {
424  historylog.comment(output);
425  if ( ! report_r->progress( PatchScriptReport::OUTPUT, output ) )
426  {
427  WAR << "User request to abort script " << script_r << endl;
428  prog.kill();
429  // the rest is handled by exit code evaluation
430  // in case the script has meanwhile finished.
431  }
432  }
433 
434  std::pair<bool,PatchScriptReport::Action> ret( std::make_pair( false, PatchScriptReport::ABORT ) );
435 
436  if ( prog.close() != 0 )
437  {
438  ret.second = report_r->problem( prog.execError() );
439  WAR << "ACTION" << ret.second << "(" << prog.execError() << ")" << endl;
440  std::ostringstream sstr;
441  sstr << script_r << _(" execution failed") << " (" << prog.execError() << ")" << endl;
442  historylog.comment(sstr.str(), /*timestamp*/true);
443  return ret;
444  }
445 
446  report_r->finish();
447  ret.first = true;
448  return ret;
449  }
450 
454  bool executeScript( const Pathname & root_r,
455  const Pathname & script_r,
456  callback::SendReport<PatchScriptReport> & report_r )
457  {
458  std::pair<bool,PatchScriptReport::Action> action( std::make_pair( false, PatchScriptReport::ABORT ) );
459 
460  do {
461  action = doExecuteScript( root_r, script_r, report_r );
462  if ( action.first )
463  return true; // success
464 
465  switch ( action.second )
466  {
468  WAR << "User request to abort at script " << script_r << endl;
469  return false; // requested abort.
470  break;
471 
473  WAR << "User request to skip script " << script_r << endl;
474  return true; // requested skip.
475  break;
476 
478  break; // again
479  }
480  } while ( action.second == PatchScriptReport::RETRY );
481 
482  // THIS is not intended to be reached:
483  INT << "Abort on unknown ACTION request " << action.second << " returned" << endl;
484  return false; // abort.
485  }
486 
492  bool RunUpdateScripts( const Pathname & root_r,
493  const Pathname & scriptsPath_r,
494  const std::vector<sat::Solvable> & checkPackages_r,
495  bool aborting_r )
496  {
497  if ( checkPackages_r.empty() )
498  return true; // no installed packages to check
499 
500  MIL << "Looking for new update scripts in (" << root_r << ")" << scriptsPath_r << endl;
501  Pathname scriptsDir( Pathname::assertprefix( root_r, scriptsPath_r ) );
502  if ( ! PathInfo( scriptsDir ).isDir() )
503  return true; // no script dir
504 
505  std::list<std::string> scripts;
506  filesystem::readdir( scripts, scriptsDir, /*dots*/false );
507  if ( scripts.empty() )
508  return true; // no scripts in script dir
509 
510  // Now collect and execute all matching scripts.
511  // On ABORT: at least log all outstanding scripts.
512  // - "name-version-release"
513  // - "name-version-release-*"
514  bool abort = false;
515  std::map<std::string, Pathname> unify; // scripts <md5,path>
516  for_( it, checkPackages_r.begin(), checkPackages_r.end() )
517  {
518  std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
519  for_( sit, scripts.begin(), scripts.end() )
520  {
521  if ( ! str::hasPrefix( *sit, prefix ) )
522  continue;
523 
524  if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
525  continue; // if not exact match it had to continue with '-'
526 
527  PathInfo script( scriptsDir / *sit );
528  Pathname localPath( scriptsPath_r/(*sit) ); // without root prefix
529  std::string unifytag; // must not stay empty
530 
531  if ( script.isFile() )
532  {
533  // Assert it's set as executable, unify by md5sum.
534  filesystem::addmod( script.path(), 0500 );
535  unifytag = filesystem::md5sum( script.path() );
536  }
537  else if ( ! script.isExist() )
538  {
539  // Might be a dangling symlink, might be ok if we are in
540  // instsys (absolute symlink within the system below /mnt).
541  // readlink will tell....
542  unifytag = filesystem::readlink( script.path() ).asString();
543  }
544 
545  if ( unifytag.empty() )
546  continue;
547 
548  // Unify scripts
549  if ( unify[unifytag].empty() )
550  {
551  unify[unifytag] = localPath;
552  }
553  else
554  {
555  // translators: We may find the same script content in files with different names.
556  // Only the first occurence is executed, subsequent ones are skipped. It's a one-line
557  // message for a log file. Preferably start translation with "%s"
558  std::string msg( str::form(_("%s already executed as %s)"), localPath.asString().c_str(), unify[unifytag].c_str() ) );
559  MIL << "Skip update script: " << msg << endl;
560  HistoryLog().comment( msg, /*timestamp*/true );
561  continue;
562  }
563 
564  if ( abort || aborting_r )
565  {
566  WAR << "Aborting: Skip update script " << *sit << endl;
567  HistoryLog().comment(
568  localPath.asString() + _(" execution skipped while aborting"),
569  /*timestamp*/true);
570  }
571  else
572  {
573  MIL << "Found update script " << *sit << endl;
574  callback::SendReport<PatchScriptReport> report;
575  report->start( make<Package>( *it ), script.path() );
576 
577  if ( ! executeScript( root_r, localPath, report ) ) // script path without root prefix!
578  abort = true; // requested abort.
579  }
580  }
581  }
582  return !abort;
583  }
584 
586  //
588 
589  inline void copyTo( std::ostream & out_r, const Pathname & file_r )
590  {
591  std::ifstream infile( file_r.c_str() );
592  for( iostr::EachLine in( infile ); in; in.next() )
593  {
594  out_r << *in << endl;
595  }
596  }
597 
598  inline std::string notificationCmdSubst( const std::string & cmd_r, const UpdateNotificationFile & notification_r )
599  {
600  std::string ret( cmd_r );
601 #define SUBST_IF(PAT,VAL) if ( ret.find( PAT ) != std::string::npos ) ret = str::gsub( ret, PAT, VAL )
602  SUBST_IF( "%p", notification_r.solvable().asString() );
603  SUBST_IF( "%P", notification_r.file().asString() );
604 #undef SUBST_IF
605  return ret;
606  }
607 
608  void sendNotification( const Pathname & root_r,
609  const UpdateNotifications & notifications_r )
610  {
611  if ( notifications_r.empty() )
612  return;
613 
614  std::string cmdspec( ZConfig::instance().updateMessagesNotify() );
615  MIL << "Notification command is '" << cmdspec << "'" << endl;
616  if ( cmdspec.empty() )
617  return;
618 
619  std::string::size_type pos( cmdspec.find( '|' ) );
620  if ( pos == std::string::npos )
621  {
622  ERR << "Can't send Notification: Missing 'format |' in command spec." << endl;
623  HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
624  return;
625  }
626 
627  std::string formatStr( str::toLower( str::trim( cmdspec.substr( 0, pos ) ) ) );
628  std::string commandStr( str::trim( cmdspec.substr( pos + 1 ) ) );
629 
630  enum Format { UNKNOWN, NONE, SINGLE, DIGEST, BULK };
631  Format format = UNKNOWN;
632  if ( formatStr == "none" )
633  format = NONE;
634  else if ( formatStr == "single" )
635  format = SINGLE;
636  else if ( formatStr == "digest" )
637  format = DIGEST;
638  else if ( formatStr == "bulk" )
639  format = BULK;
640  else
641  {
642  ERR << "Can't send Notification: Unknown format '" << formatStr << " |' in command spec." << endl;
643  HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
644  return;
645  }
646 
647  // Take care: commands are ececuted chroot(root_r). The message file
648  // pathnames in notifications_r are local to root_r. For physical access
649  // to the file they need to be prefixed.
650 
651  if ( format == NONE || format == SINGLE )
652  {
653  for_( it, notifications_r.begin(), notifications_r.end() )
654  {
655  std::vector<std::string> command;
656  if ( format == SINGLE )
657  command.push_back( "<"+Pathname::assertprefix( root_r, it->file() ).asString() );
658  str::splitEscaped( notificationCmdSubst( commandStr, *it ), std::back_inserter( command ) );
659 
660  ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
661  if ( true ) // Wait for feedback
662  {
663  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
664  {
665  DBG << line;
666  }
667  int ret = prog.close();
668  if ( ret != 0 )
669  {
670  ERR << "Notification command returned with error (" << ret << ")." << endl;
671  HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
672  return;
673  }
674  }
675  }
676  }
677  else if ( format == DIGEST || format == BULK )
678  {
679  filesystem::TmpFile tmpfile;
680  std::ofstream out( tmpfile.path().c_str() );
681  for_( it, notifications_r.begin(), notifications_r.end() )
682  {
683  if ( format == DIGEST )
684  {
685  out << it->file() << endl;
686  }
687  else if ( format == BULK )
688  {
689  copyTo( out << '\f', Pathname::assertprefix( root_r, it->file() ) );
690  }
691  }
692 
693  std::vector<std::string> command;
694  command.push_back( "<"+tmpfile.path().asString() ); // redirect input
695  str::splitEscaped( notificationCmdSubst( commandStr, *notifications_r.begin() ), std::back_inserter( command ) );
696 
697  ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
698  if ( true ) // Wait for feedback otherwise the TmpFile goes out of scope.
699  {
700  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
701  {
702  DBG << line;
703  }
704  int ret = prog.close();
705  if ( ret != 0 )
706  {
707  ERR << "Notification command returned with error (" << ret << ")." << endl;
708  HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
709  return;
710  }
711  }
712  }
713  else
714  {
715  INT << "Can't send Notification: Missing handler for 'format |' in command spec." << endl;
716  HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
717  return;
718  }
719  }
720 
721 
727  void RunUpdateMessages( const Pathname & root_r,
728  const Pathname & messagesPath_r,
729  const std::vector<sat::Solvable> & checkPackages_r,
730  ZYppCommitResult & result_r )
731  {
732  if ( checkPackages_r.empty() )
733  return; // no installed packages to check
734 
735  MIL << "Looking for new update messages in (" << root_r << ")" << messagesPath_r << endl;
736  Pathname messagesDir( Pathname::assertprefix( root_r, messagesPath_r ) );
737  if ( ! PathInfo( messagesDir ).isDir() )
738  return; // no messages dir
739 
740  std::list<std::string> messages;
741  filesystem::readdir( messages, messagesDir, /*dots*/false );
742  if ( messages.empty() )
743  return; // no messages in message dir
744 
745  // Now collect all matching messages in result and send them
746  // - "name-version-release"
747  // - "name-version-release-*"
748  HistoryLog historylog;
749  for_( it, checkPackages_r.begin(), checkPackages_r.end() )
750  {
751  std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
752  for_( sit, messages.begin(), messages.end() )
753  {
754  if ( ! str::hasPrefix( *sit, prefix ) )
755  continue;
756 
757  if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
758  continue; // if not exact match it had to continue with '-'
759 
760  PathInfo message( messagesDir / *sit );
761  if ( ! message.isFile() || message.size() == 0 )
762  continue;
763 
764  MIL << "Found update message " << *sit << endl;
765  Pathname localPath( messagesPath_r/(*sit) ); // without root prefix
766  result_r.rUpdateMessages().push_back( UpdateNotificationFile( *it, localPath ) );
767  historylog.comment( str::Str() << _("New update message") << " " << localPath, /*timestamp*/true );
768  }
769  }
770  sendNotification( root_r, result_r.updateMessages() );
771  }
772 
776  void logPatchStatusChanges( const sat::Transaction & transaction_r, TargetImpl & target_r )
777  {
779  if ( changedPseudoInstalled.empty() )
780  return;
781 
782  if ( ! transaction_r.actionEmpty( ~sat::Transaction::STEP_DONE ) )
783  {
784  // Need to recompute the patch list if commit is incomplete!
785  // We remember the initially established status, then reload the
786  // Target to get the current patch status. Then compare.
787  WAR << "Need to recompute the patch status changes as commit is incomplete!" << endl;
788  ResPool::EstablishedStates establishedStates{ ResPool::instance().establishedStates() };
789  target_r.load();
790  changedPseudoInstalled = establishedStates.changedPseudoInstalled();
791  }
792 
793  HistoryLog historylog;
794  for ( const auto & el : changedPseudoInstalled )
795  historylog.patchStateChange( el.first, el.second );
796  }
797 
799  } // namespace
801 
802  void XRunUpdateMessages( const Pathname & root_r,
803  const Pathname & messagesPath_r,
804  const std::vector<sat::Solvable> & checkPackages_r,
805  ZYppCommitResult & result_r )
806  { RunUpdateMessages( root_r, messagesPath_r, checkPackages_r, result_r ); }
807 
809 
810  IMPL_PTR_TYPE(TargetImpl);
811 
813  //
814  // METHOD NAME : TargetImpl::TargetImpl
815  // METHOD TYPE : Ctor
816  //
817  TargetImpl::TargetImpl( const Pathname & root_r, bool doRebuild_r )
818  : _root( root_r )
819  , _requestedLocalesFile( home() / "RequestedLocales" )
820  , _autoInstalledFile( home() / "AutoInstalled" )
821  , _hardLocksFile( Pathname::assertprefix( _root, ZConfig::instance().locksFile() ) )
822  , _vendorAttr( Pathname::assertprefix( _root, ZConfig::instance().vendorPath() ) )
823  {
824  _rpm.initDatabase( root_r, doRebuild_r );
825 
827 
829  sigMultiversionSpecChanged(); // HACK: see sigMultiversionSpecChanged
830  MIL << "Initialized target on " << _root << endl;
831  }
832 
836  static std::string generateRandomId()
837  {
838  std::ifstream uuidprovider( "/proc/sys/kernel/random/uuid" );
839  return iostr::getline( uuidprovider );
840  }
841 
847  void updateFileContent( const Pathname &filename,
848  boost::function<bool ()> condition,
849  boost::function<std::string ()> value )
850  {
851  std::string val = value();
852  // if the value is empty, then just dont
853  // do anything, regardless of the condition
854  if ( val.empty() )
855  return;
856 
857  if ( condition() )
858  {
859  MIL << "updating '" << filename << "' content." << endl;
860 
861  // if the file does not exist we need to generate the uuid file
862 
863  std::ofstream filestr;
864  // make sure the path exists
865  filesystem::assert_dir( filename.dirname() );
866  filestr.open( filename.c_str() );
867 
868  if ( filestr.good() )
869  {
870  filestr << val;
871  filestr.close();
872  }
873  else
874  {
875  // FIXME, should we ignore the error?
876  ZYPP_THROW(Exception("Can't openfile '" + filename.asString() + "' for writing"));
877  }
878  }
879  }
880 
882  static bool fileMissing( const Pathname &pathname )
883  {
884  return ! PathInfo(pathname).isExist();
885  }
886 
888  {
889  // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
890  if ( root() != "/" )
891  return;
892 
893  // Create the anonymous unique id, used for download statistics
894  Pathname idpath( home() / "AnonymousUniqueId");
895 
896  try
897  {
898  updateFileContent( idpath,
899  boost::bind(fileMissing, idpath),
901  }
902  catch ( const Exception &e )
903  {
904  WAR << "Can't create anonymous id file" << endl;
905  }
906 
907  }
908 
910  {
911  // create the anonymous unique id
912  // this value is used for statistics
913  Pathname flavorpath( home() / "LastDistributionFlavor");
914 
915  // is there a product
917  if ( ! p )
918  {
919  WAR << "No base product, I won't create flavor cache" << endl;
920  return;
921  }
922 
923  std::string flavor = p->flavor();
924 
925  try
926  {
927 
928  updateFileContent( flavorpath,
929  // only if flavor is not empty
930  functor::Constant<bool>( ! flavor.empty() ),
932  }
933  catch ( const Exception &e )
934  {
935  WAR << "Can't create flavor cache" << endl;
936  return;
937  }
938  }
939 
941  //
942  // METHOD NAME : TargetImpl::~TargetImpl
943  // METHOD TYPE : Dtor
944  //
946  {
948  sigMultiversionSpecChanged(); // HACK: see sigMultiversionSpecChanged
949  MIL << "Targets closed" << endl;
950  }
951 
953  //
954  // solv file handling
955  //
957 
959  {
960  return Pathname::assertprefix( _root, ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoAlias() );
961  }
962 
964  {
965  Pathname base = solvfilesPath();
967  }
968 
970  {
971  Pathname base = solvfilesPath();
972  Pathname rpmsolv = base/"solv";
973  Pathname rpmsolvcookie = base/"cookie";
974 
975  bool build_rpm_solv = true;
976  // lets see if the rpm solv cache exists
977 
978  RepoStatus rpmstatus( rpmDbRepoStatus(_root) && RepoStatus(_root/"etc/products.d") );
979 
980  bool solvexisted = PathInfo(rpmsolv).isExist();
981  if ( solvexisted )
982  {
983  // see the status of the cache
984  PathInfo cookie( rpmsolvcookie );
985  MIL << "Read cookie: " << cookie << endl;
986  if ( cookie.isExist() )
987  {
988  RepoStatus status = RepoStatus::fromCookieFile(rpmsolvcookie);
989  // now compare it with the rpm database
990  if ( status == rpmstatus )
991  build_rpm_solv = false;
992  MIL << "Read cookie: " << rpmsolvcookie << " says: "
993  << (build_rpm_solv ? "outdated" : "uptodate") << endl;
994  }
995  }
996 
997  if ( build_rpm_solv )
998  {
999  // if the solvfile dir does not exist yet, we better create it
1000  filesystem::assert_dir( base );
1001 
1002  Pathname oldSolvFile( solvexisted ? rpmsolv : Pathname() ); // to speedup rpmdb2solv
1003 
1005  if ( !tmpsolv )
1006  {
1007  // Can't create temporary solv file, usually due to insufficient permission
1008  // (user query while @System solv needs refresh). If so, try switching
1009  // to a location within zypps temp. space (will be cleaned at application end).
1010 
1011  bool switchingToTmpSolvfile = false;
1012  Exception ex("Failed to cache rpm database.");
1013  ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
1014 
1015  if ( ! solvfilesPathIsTemp() )
1016  {
1017  base = getZYpp()->tmpPath() / sat::Pool::instance().systemRepoAlias();
1018  rpmsolv = base/"solv";
1019  rpmsolvcookie = base/"cookie";
1020 
1021  filesystem::assert_dir( base );
1022  tmpsolv = filesystem::TmpFile::makeSibling( rpmsolv );
1023 
1024  if ( tmpsolv )
1025  {
1026  WAR << "Using a temporary solv file at " << base << endl;
1027  switchingToTmpSolvfile = true;
1028  _tmpSolvfilesPath = base;
1029  }
1030  else
1031  {
1032  ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
1033  }
1034  }
1035 
1036  if ( ! switchingToTmpSolvfile )
1037  {
1038  ZYPP_THROW(ex);
1039  }
1040  }
1041 
1042  // Take care we unlink the solvfile on exception
1044 
1046  cmd.push_back( "rpmdb2solv" );
1047  if ( ! _root.empty() ) {
1048  cmd.push_back( "-r" );
1049  cmd.push_back( _root.asString() );
1050  }
1051  cmd.push_back( "-D" );
1052  cmd.push_back( rpm().dbPath().asString() );
1053  cmd.push_back( "-X" ); // autogenerate pattern/product/... from -package
1054  // bsc#1104415: no more application support // cmd.push_back( "-A" ); // autogenerate application pseudo packages
1055  cmd.push_back( "-p" );
1056  cmd.push_back( Pathname::assertprefix( _root, "/etc/products.d" ).asString() );
1057 
1058  if ( ! oldSolvFile.empty() )
1059  cmd.push_back( oldSolvFile.asString() );
1060 
1061  cmd.push_back( "-o" );
1062  cmd.push_back( tmpsolv.path().asString() );
1063 
1065  std::string errdetail;
1066 
1067  for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1068  WAR << " " << output;
1069  if ( errdetail.empty() ) {
1070  errdetail = prog.command();
1071  errdetail += '\n';
1072  }
1073  errdetail += output;
1074  }
1075 
1076  int ret = prog.close();
1077  if ( ret != 0 )
1078  {
1079  Exception ex(str::form("Failed to cache rpm database (%d).", ret));
1080  ex.remember( errdetail );
1081  ZYPP_THROW(ex);
1082  }
1083 
1084  ret = filesystem::rename( tmpsolv, rpmsolv );
1085  if ( ret != 0 )
1086  ZYPP_THROW(Exception("Failed to move cache to final destination"));
1087  // if this fails, don't bother throwing exceptions
1088  filesystem::chmod( rpmsolv, 0644 );
1089 
1090  rpmstatus.saveToCookieFile(rpmsolvcookie);
1091 
1092  // We keep it.
1093  guard.resetDispose();
1094  sat::updateSolvFileIndex( rpmsolv ); // content digest for zypper bash completion
1095 
1096  // system-hook: Finally send notification to plugins
1097  if ( root() == "/" )
1098  {
1099  PluginExecutor plugins;
1100  plugins.load( ZConfig::instance().pluginsPath()/"system" );
1101  if ( plugins )
1102  plugins.send( PluginFrame( "PACKAGESETCHANGED" ) );
1103  }
1104  }
1105  else
1106  {
1107  // On the fly add missing solv.idx files for bash completion.
1108  if ( ! PathInfo(base/"solv.idx").isExist() )
1109  sat::updateSolvFileIndex( rpmsolv );
1110  }
1111  return build_rpm_solv;
1112  }
1113 
1115  {
1116  load( false );
1117  }
1118 
1120  {
1121  Repository system( sat::Pool::instance().findSystemRepo() );
1122  if ( system )
1123  system.eraseFromPool();
1124  }
1125 
1126  void TargetImpl::load( bool force )
1127  {
1128  bool newCache = buildCache();
1129  MIL << "New cache built: " << (newCache?"true":"false") <<
1130  ", force loading: " << (force?"true":"false") << endl;
1131 
1132  // now add the repos to the pool
1133  sat::Pool satpool( sat::Pool::instance() );
1134  Pathname rpmsolv( solvfilesPath() / "solv" );
1135  MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoAlias() << ")" << endl;
1136 
1137  // Providing an empty system repo, unload any old content
1138  Repository system( sat::Pool::instance().findSystemRepo() );
1139 
1140  if ( system && ! system.solvablesEmpty() )
1141  {
1142  if ( newCache || force )
1143  {
1144  system.eraseFromPool(); // invalidates system
1145  }
1146  else
1147  {
1148  return; // nothing to do
1149  }
1150  }
1151 
1152  if ( ! system )
1153  {
1154  system = satpool.systemRepo();
1155  }
1156 
1157  try
1158  {
1159  MIL << "adding " << rpmsolv << " to system" << endl;
1160  system.addSolv( rpmsolv );
1161  }
1162  catch ( const Exception & exp )
1163  {
1164  ZYPP_CAUGHT( exp );
1165  MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1166  clearCache();
1167  buildCache();
1168 
1169  system.addSolv( rpmsolv );
1170  }
1171  satpool.rootDir( _root );
1172 
1173  // (Re)Load the requested locales et al.
1174  // If the requested locales are empty, we leave the pool untouched
1175  // to avoid undoing changes the application applied. We expect this
1176  // to happen on a bare metal installation only. An already existing
1177  // target should be loaded before its settings are changed.
1178  {
1180  if ( ! requestedLocales.empty() )
1181  {
1183  }
1184  }
1185  {
1186  if ( ! PathInfo( _autoInstalledFile.file() ).isExist() )
1187  {
1188  // Initialize from history, if it does not exist
1189  Pathname historyFile( Pathname::assertprefix( _root, ZConfig::instance().historyLogFile() ) );
1190  if ( PathInfo( historyFile ).isExist() )
1191  {
1192  SolvIdentFile::Data onSystemByUser( getUserInstalledFromHistory( historyFile ) );
1193  SolvIdentFile::Data onSystemByAuto;
1194  for_( it, system.solvablesBegin(), system.solvablesEnd() )
1195  {
1196  IdString ident( (*it).ident() );
1197  if ( onSystemByUser.find( ident ) == onSystemByUser.end() )
1198  onSystemByAuto.insert( ident );
1199  }
1200  _autoInstalledFile.setData( onSystemByAuto );
1201  }
1202  // on the fly removed any obsolete SoftLocks file
1203  filesystem::unlink( home() / "SoftLocks" );
1204  }
1205  // read from AutoInstalled file
1206  sat::StringQueue q;
1207  for ( const auto & idstr : _autoInstalledFile.data() )
1208  q.push( idstr.id() );
1209  satpool.setAutoInstalled( q );
1210  }
1211 
1212  // Load the needreboot package specs
1213  {
1214  sat::SolvableSpec needrebootSpec;
1215  needrebootSpec.addProvides( Capability("installhint(reboot-needed)") );
1216  needrebootSpec.addProvides( Capability("kernel") );
1217  needrebootSpec.addIdent( IdString("kernel-firmware") );
1218 
1219  Pathname needrebootFile { Pathname::assertprefix( root(), ZConfig::instance().needrebootFile() ) };
1220  if ( PathInfo( needrebootFile ).isFile() )
1221  needrebootSpec.parseFrom( needrebootFile );
1222 
1223  Pathname needrebootDir { Pathname::assertprefix( root(), ZConfig::instance().needrebootPath() ) };
1224  if ( PathInfo( needrebootDir ).isDir() )
1225  {
1226  static const StrMatcher isRpmConfigBackup( "\\.rpm(new|save|orig)$", Match::REGEX );
1227 
1229  [&]( const Pathname & dir_r, const char *const str_r )->bool
1230  {
1231  if ( ! isRpmConfigBackup( str_r ) )
1232  {
1233  Pathname needrebootFile { needrebootDir / str_r };
1234  if ( PathInfo( needrebootFile ).isFile() )
1235  needrebootSpec.parseFrom( needrebootFile );
1236  }
1237  return true;
1238  });
1239  }
1240  satpool.setNeedrebootSpec( std::move(needrebootSpec) );
1241  }
1242 
1243  if ( ZConfig::instance().apply_locks_file() )
1244  {
1245  const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
1246  if ( ! hardLocks.empty() )
1247  {
1248  ResPool::instance().setHardLockQueries( hardLocks );
1249  }
1250  }
1251 
1252  // now that the target is loaded, we can cache the flavor
1254 
1255  MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
1256  }
1257 
1259  //
1260  // COMMIT
1261  //
1264  {
1265  // ----------------------------------------------------------------- //
1266  ZYppCommitPolicy policy_r( policy_rX );
1267  bool explicitDryRun = policy_r.dryRun(); // explicit dry run will trigger a fileconflict check, implicit (download-only) not.
1268 
1269  ShutdownLock lck("Zypp commit running.");
1270 
1271  // Fake outstanding YCP fix: Honour restriction to media 1
1272  // at installation, but install all remaining packages if post-boot.
1273  if ( policy_r.restrictToMedia() > 1 )
1274  policy_r.allMedia();
1275 
1276  if ( policy_r.downloadMode() == DownloadDefault ) {
1277  if ( root() == "/" )
1278  policy_r.downloadMode(DownloadInHeaps);
1279  else {
1280  if ( policy_r.singleTransModeEnabled() )
1281  policy_r.downloadMode(DownloadInAdvance);
1282  else
1283  policy_r.downloadMode(DownloadAsNeeded);
1284  }
1285  }
1286  // DownloadOnly implies dry-run.
1287  else if ( policy_r.downloadMode() == DownloadOnly )
1288  policy_r.dryRun( true );
1289  // ----------------------------------------------------------------- //
1290 
1291  MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
1292 
1294  // Compute transaction:
1296  ZYppCommitResult result( root() );
1297  result.rTransaction() = pool_r.resolver().getTransaction();
1298  result.rTransaction().order();
1299  // steps: this is our todo-list
1301  if ( policy_r.restrictToMedia() )
1302  {
1303  // Collect until the 1st package from an unwanted media occurs.
1304  // Further collection could violate install order.
1305  MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
1306  for_( it, result.transaction().begin(), result.transaction().end() )
1307  {
1308  if ( makeResObject( *it )->mediaNr() > 1 )
1309  break;
1310  steps.push_back( *it );
1311  }
1312  }
1313  else
1314  {
1315  result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() );
1316  }
1317  MIL << "Todo: " << result << endl;
1318 
1320  // Prepare execution of commit plugins:
1322  PluginExecutor commitPlugins;
1323  if ( root() == "/" && ! policy_r.dryRun() )
1324  {
1325  commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
1326  }
1327  if ( commitPlugins )
1328  commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
1329 
1331  // Write out a testcase if we're in dist upgrade mode.
1333  if ( pool_r.resolver().upgradeMode() || pool_r.resolver().upgradingRepos() )
1334  {
1335  if ( ! policy_r.dryRun() )
1336  {
1338  }
1339  else
1340  {
1341  DBG << "dryRun: Not writing upgrade testcase." << endl;
1342  }
1343  }
1344 
1346  // Store non-package data:
1348  if ( ! policy_r.dryRun() )
1349  {
1351  // requested locales
1353  // autoinstalled
1354  {
1355  SolvIdentFile::Data newdata;
1356  for ( sat::Queue::value_type id : result.rTransaction().autoInstalled() )
1357  newdata.insert( IdString(id) );
1358  _autoInstalledFile.setData( newdata );
1359  }
1360  // hard locks
1361  if ( ZConfig::instance().apply_locks_file() )
1362  {
1363  HardLocksFile::Data newdata;
1364  pool_r.getHardLockQueries( newdata );
1365  _hardLocksFile.setData( newdata );
1366  }
1367  }
1368  else
1369  {
1370  DBG << "dryRun: Not storing non-package data." << endl;
1371  }
1372 
1374  // First collect and display all messages
1375  // associated with patches to be installed.
1377  if ( ! policy_r.dryRun() )
1378  {
1379  for_( it, steps.begin(), steps.end() )
1380  {
1381  if ( ! it->satSolvable().isKind<Patch>() )
1382  continue;
1383 
1384  PoolItem pi( *it );
1385  if ( ! pi.status().isToBeInstalled() )
1386  continue;
1387 
1388  Patch::constPtr patch( asKind<Patch>(pi.resolvable()) );
1389  if ( ! patch ||patch->message().empty() )
1390  continue;
1391 
1392  MIL << "Show message for " << patch << endl;
1394  if ( ! report->show( patch ) )
1395  {
1396  WAR << "commit aborted by the user" << endl;
1398  }
1399  }
1400  }
1401  else
1402  {
1403  DBG << "dryRun: Not checking patch messages." << endl;
1404  }
1405 
1407  // Remove/install packages.
1409 
1410  bool singleTransMode = policy_r.singleTransModeEnabled();
1411 
1412  DBG << "commit log file is set to: " << HistoryLog::fname() << endl;
1413  if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly || singleTransMode )
1414  {
1415  // Prepare the package cache. Pass all items requiring download.
1416  CommitPackageCache packageCache;
1417  packageCache.setCommitList( steps.begin(), steps.end() );
1418 
1419  bool miss = false;
1420  if ( policy_r.downloadMode() != DownloadAsNeeded || singleTransMode )
1421  {
1422  // Preload the cache. Until now this means pre-loading all packages.
1423  // Once DownloadInHeaps is fully implemented, this will change and
1424  // we may actually have more than one heap.
1425  for_( it, steps.begin(), steps.end() )
1426  {
1427  switch ( it->stepType() )
1428  {
1431  // proceed: only install actionas may require download.
1432  break;
1433 
1434  default:
1435  // next: no download for or non-packages and delete actions.
1436  continue;
1437  break;
1438  }
1439 
1440  PoolItem pi( *it );
1441  if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() )
1442  {
1443  ManagedFile localfile;
1444  try
1445  {
1446  localfile = packageCache.get( pi );
1447  localfile.resetDispose(); // keep the package file in the cache
1448  }
1449  catch ( const AbortRequestException & exp )
1450  {
1451  it->stepStage( sat::Transaction::STEP_ERROR );
1452  miss = true;
1453  WAR << "commit cache preload aborted by the user" << endl;
1455  break;
1456  }
1457  catch ( const SkipRequestException & exp )
1458  {
1459  ZYPP_CAUGHT( exp );
1460  it->stepStage( sat::Transaction::STEP_ERROR );
1461  miss = true;
1462  WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1463  continue;
1464  }
1465  catch ( const Exception & exp )
1466  {
1467  // bnc #395704: missing catch causes abort.
1468  // TODO see if packageCache fails to handle errors correctly.
1469  ZYPP_CAUGHT( exp );
1470  it->stepStage( sat::Transaction::STEP_ERROR );
1471  miss = true;
1472  INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1473  continue;
1474  }
1475  }
1476  }
1477  packageCache.preloaded( true ); // try to avoid duplicate infoInCache CBs in commit
1478  }
1479 
1480  if ( miss )
1481  {
1482  ERR << "Some packages could not be provided. Aborting commit."<< endl;
1483  }
1484  else
1485  {
1486  // single trans mode does a test install via rpm
1487  if ( policy_r.singleTransModeEnabled() ) {
1488  commitInSingleTransaction( policy_r, packageCache, result );
1489  } else {
1490  if ( ! policy_r.dryRun() )
1491  {
1492  // if cache is preloaded, check for file conflicts
1493  commitFindFileConflicts( policy_r, result );
1494  commit( policy_r, packageCache, result );
1495  }
1496  else
1497  {
1498  DBG << "dryRun/downloadOnly: Not installing/deleting anything." << endl;
1499  if ( explicitDryRun ) {
1500  // if cache is preloaded, check for file conflicts
1501  commitFindFileConflicts( policy_r, result );
1502  }
1503  }
1504  }
1505  }
1506  }
1507  else
1508  {
1509  DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
1510  if ( explicitDryRun ) {
1511  // if cache is preloaded, check for file conflicts
1512  commitFindFileConflicts( policy_r, result );
1513  }
1514  }
1515 
1516  {
1517  // NOTE: Removing rpm in a transaction, rpm removes the /var/lib/rpm compat symlink.
1518  // We re-create it, in case it was lost to prevent legacy tools from accidentally
1519  // assuming no database is present.
1520  if ( ! PathInfo(_root/"/var/lib/rpm",PathInfo::LSTAT).isExist()
1521  && PathInfo(_root/"/usr/lib/sysimage/rpm").isDir() ) {
1522  WAR << "(rpm removed in commit?) Inject missing /var/lib/rpm compat symlink to /usr/lib/sysimage/rpm" << endl;
1523  filesystem::assert_dir( _root/"/var/lib" );
1524  filesystem::symlink( "../../usr/lib/sysimage/rpm", _root/"/var/lib/rpm" );
1525  }
1526  }
1527 
1529  // Send result to commit plugins:
1531  if ( commitPlugins )
1532  commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
1533 
1535  // Try to rebuild solv file while rpm database is still in cache
1537  if ( ! policy_r.dryRun() )
1538  {
1539  buildCache();
1540  }
1541 
1542  MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
1543  return result;
1544  }
1545 
1547  //
1548  // COMMIT internal
1549  //
1551  namespace
1552  {
1553  struct NotifyAttemptToModify
1554  {
1555  NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
1556 
1557  void operator()()
1558  { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
1559 
1560  TrueBool _guard;
1561  ZYppCommitResult & _result;
1562  };
1563  } // namespace
1564 
1565  void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
1566  CommitPackageCache & packageCache_r,
1567  ZYppCommitResult & result_r )
1568  {
1569  // steps: this is our todo-list
1571  MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1572 
1574 
1575  // Send notification once upon 1st call to rpm
1576  NotifyAttemptToModify attemptToModify( result_r );
1577 
1578  bool abort = false;
1579 
1580  // bsc#1181328: Some systemd tools require /proc to be mounted
1581  AssertProcMounted assertProcMounted( _root );
1582 
1583  RpmPostTransCollector postTransCollector( _root );
1584  std::vector<sat::Solvable> successfullyInstalledPackages;
1585  TargetImpl::PoolItemList remaining;
1586 
1587  for_( step, steps.begin(), steps.end() )
1588  {
1589  PoolItem citem( *step );
1590  if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
1591  {
1592  if ( citem->isKind<Package>() )
1593  {
1594  // for packages this means being obsoleted (by rpm)
1595  // thius no additional action is needed.
1596  step->stepStage( sat::Transaction::STEP_DONE );
1597  continue;
1598  }
1599  }
1600 
1601  if ( citem->isKind<Package>() )
1602  {
1603  Package::constPtr p = citem->asKind<Package>();
1604  if ( citem.status().isToBeInstalled() )
1605  {
1606  ManagedFile localfile;
1607  try
1608  {
1609  localfile = packageCache_r.get( citem );
1610  }
1611  catch ( const AbortRequestException &e )
1612  {
1613  WAR << "commit aborted by the user" << endl;
1614  abort = true;
1615  step->stepStage( sat::Transaction::STEP_ERROR );
1616  break;
1617  }
1618  catch ( const SkipRequestException &e )
1619  {
1620  ZYPP_CAUGHT( e );
1621  WAR << "Skipping package " << p << " in commit" << endl;
1622  step->stepStage( sat::Transaction::STEP_ERROR );
1623  continue;
1624  }
1625  catch ( const Exception &e )
1626  {
1627  // bnc #395704: missing catch causes abort.
1628  // TODO see if packageCache fails to handle errors correctly.
1629  ZYPP_CAUGHT( e );
1630  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1631  step->stepStage( sat::Transaction::STEP_ERROR );
1632  continue;
1633  }
1634 
1635  // create a installation progress report proxy
1636  RpmInstallPackageReceiver progress( citem.resolvable() );
1637  progress.connect(); // disconnected on destruction.
1638 
1639  bool success = false;
1640  rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1641  // Why force and nodeps?
1642  //
1643  // Because zypp builds the transaction and the resolver asserts that
1644  // everything is fine.
1645  // We use rpm just to unpack and register the package in the database.
1646  // We do this step by step, so rpm is not aware of the bigger context.
1647  // So we turn off rpms internal checks, because we do it inside zypp.
1648  flags |= rpm::RPMINST_NODEPS;
1649  flags |= rpm::RPMINST_FORCE;
1650  //
1651  if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE;
1652  if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1653  if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
1654  if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
1655 
1656  attemptToModify();
1657  try
1658  {
1660  if ( postTransCollector.collectScriptFromPackage( localfile ) )
1661  flags |= rpm::RPMINST_NOPOSTTRANS;
1662  rpm().installPackage( localfile, flags );
1663  HistoryLog().install(citem);
1664 
1665  if ( progress.aborted() )
1666  {
1667  WAR << "commit aborted by the user" << endl;
1668  localfile.resetDispose(); // keep the package file in the cache
1669  abort = true;
1670  step->stepStage( sat::Transaction::STEP_ERROR );
1671  break;
1672  }
1673  else
1674  {
1675  if ( citem.isNeedreboot() ) {
1676  auto rebootNeededFile = root() / "/run/reboot-needed";
1677  if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
1678  filesystem::touch( rebootNeededFile );
1679  }
1680 
1681  success = true;
1682  step->stepStage( sat::Transaction::STEP_DONE );
1683  }
1684  }
1685  catch ( Exception & excpt_r )
1686  {
1687  ZYPP_CAUGHT(excpt_r);
1688  localfile.resetDispose(); // keep the package file in the cache
1689 
1690  if ( policy_r.dryRun() )
1691  {
1692  WAR << "dry run failed" << endl;
1693  step->stepStage( sat::Transaction::STEP_ERROR );
1694  break;
1695  }
1696  // else
1697  if ( progress.aborted() )
1698  {
1699  WAR << "commit aborted by the user" << endl;
1700  abort = true;
1701  }
1702  else
1703  {
1704  WAR << "Install failed" << endl;
1705  }
1706  step->stepStage( sat::Transaction::STEP_ERROR );
1707  break; // stop
1708  }
1709 
1710  if ( success && !policy_r.dryRun() )
1711  {
1713  successfullyInstalledPackages.push_back( citem.satSolvable() );
1714  step->stepStage( sat::Transaction::STEP_DONE );
1715  }
1716  }
1717  else
1718  {
1719  RpmRemovePackageReceiver progress( citem.resolvable() );
1720  progress.connect(); // disconnected on destruction.
1721 
1722  bool success = false;
1723  rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1724  flags |= rpm::RPMINST_NODEPS;
1725  if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1726 
1727  attemptToModify();
1728  try
1729  {
1730  rpm().removePackage( p, flags );
1731  HistoryLog().remove(citem);
1732 
1733  if ( progress.aborted() )
1734  {
1735  WAR << "commit aborted by the user" << endl;
1736  abort = true;
1737  step->stepStage( sat::Transaction::STEP_ERROR );
1738  break;
1739  }
1740  else
1741  {
1742  success = true;
1743  step->stepStage( sat::Transaction::STEP_DONE );
1744  }
1745  }
1746  catch (Exception & excpt_r)
1747  {
1748  ZYPP_CAUGHT( excpt_r );
1749  if ( progress.aborted() )
1750  {
1751  WAR << "commit aborted by the user" << endl;
1752  abort = true;
1753  step->stepStage( sat::Transaction::STEP_ERROR );
1754  break;
1755  }
1756  // else
1757  WAR << "removal of " << p << " failed";
1758  step->stepStage( sat::Transaction::STEP_ERROR );
1759  }
1760  if ( success && !policy_r.dryRun() )
1761  {
1763  step->stepStage( sat::Transaction::STEP_DONE );
1764  }
1765  }
1766  }
1767  else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
1768  {
1769  // Status is changed as the buddy package buddy
1770  // gets installed/deleted. Handle non-buddies only.
1771  if ( ! citem.buddy() )
1772  {
1773  if ( citem->isKind<Product>() )
1774  {
1775  Product::constPtr p = citem->asKind<Product>();
1776  if ( citem.status().isToBeInstalled() )
1777  {
1778  ERR << "Can't install orphan product without release-package! " << citem << endl;
1779  }
1780  else
1781  {
1782  // Deleting the corresponding product entry is all we con do.
1783  // So the product will no longer be visible as installed.
1784  std::string referenceFilename( p->referenceFilename() );
1785  if ( referenceFilename.empty() )
1786  {
1787  ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
1788  }
1789  else
1790  {
1791  Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
1792  if ( ! rpm().hasFile( referencePath.asString() ) )
1793  {
1794  // If it's not owned by a package, we can delete it.
1795  referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
1796  if ( filesystem::unlink( referencePath ) != 0 )
1797  ERR << "Delete orphan product failed: " << referencePath << endl;
1798  }
1799  else
1800  {
1801  WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
1802  }
1803  }
1804  }
1805  }
1806  else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
1807  {
1808  // SrcPackage is install-only
1809  SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1810  installSrcPackage( p );
1811  }
1812 
1814  step->stepStage( sat::Transaction::STEP_DONE );
1815  }
1816 
1817  } // other resolvables
1818 
1819  } // for
1820 
1821  // process all remembered posttrans scripts. If aborting,
1822  // at least log omitted scripts.
1823  if ( abort || (abort = !postTransCollector.executeScripts()) )
1824  postTransCollector.discardScripts();
1825 
1826  // Check presence of update scripts/messages. If aborting,
1827  // at least log omitted scripts.
1828  if ( ! successfullyInstalledPackages.empty() )
1829  {
1830  if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
1831  successfullyInstalledPackages, abort ) )
1832  {
1833  WAR << "Commit aborted by the user" << endl;
1834  abort = true;
1835  }
1836  // send messages after scripts in case some script generates output,
1837  // that should be kept in t %ghost message file.
1838  RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
1839  successfullyInstalledPackages,
1840  result_r );
1841  }
1842 
1843  // jsc#SLE-5116: Log patch status changes to history
1844  // NOTE: Should be the last action as it may need to reload
1845  // the Target in case of an incomplete transaction.
1846  logPatchStatusChanges( result_r.transaction(), *this );
1847 
1848  if ( abort )
1849  {
1850  HistoryLog().comment( "Commit was aborted." );
1852  }
1853  }
1854 
1855 
1862  struct SendSingleTransReport : public callback::SendReport<rpm::SingleTransReport>
1863  {
1865  void sendLogline( const std::string & line_r, ReportType::loglevel level_r = ReportType::loglevel::msg )
1866  {
1867  callback::UserData data { ReportType::contentLogline };
1868  data.set( "line", std::cref(line_r) );
1869  data.set( "level", level_r );
1870  report( data );
1871  }
1873  void sendLoglineRpm( const std::string & line_r, unsigned rpmlevel_r )
1874  {
1875  auto u2rpmlevel = []( unsigned rpmlevel_r ) -> ReportType::loglevel {
1876  switch ( rpmlevel_r ) {
1877  case RPMLOG_EMERG: [[fallthrough]]; // system is unusable
1878  case RPMLOG_ALERT: [[fallthrough]]; // action must be taken immediately
1879  case RPMLOG_CRIT: // critical conditions
1880  return ReportType::loglevel::crt;
1881  case RPMLOG_ERR: // error conditions
1882  return ReportType::loglevel::err;
1883  case RPMLOG_WARNING: // warning conditions
1884  return ReportType::loglevel::war;
1885  default: [[fallthrough]];
1886  case RPMLOG_NOTICE: [[fallthrough]]; // normal but significant condition
1887  case RPMLOG_INFO: // informational
1888  return ReportType::loglevel::msg;
1889  case RPMLOG_DEBUG:
1890  return ReportType::loglevel::dbg;
1891  }
1892  };
1893  sendLogline( line_r, u2rpmlevel( rpmlevel_r ) );
1894  }
1895 
1896  private:
1897  void report( const callback::UserData & userData_r )
1898  { (*this)->report( userData_r ); }
1899  };
1900 
1902 
1908 
1910  {
1911  namespace zpt = zypp::proto::target;
1912 
1913  SendSingleTransReport report; // active throughout the whole rpm transaction
1914 
1915  // steps: this is our todo-list
1917  MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1918 
1920 
1921  // Send notification once upon calling rpm
1922  NotifyAttemptToModify attemptToModify( result_r );
1923 
1924  // let zypper know we executed in one big transaction so in case of failures it can show extended error information
1925  result_r.setSingleTransactionMode( true );
1926 
1927  // bsc#1181328: Some systemd tools require /proc to be mounted
1928  AssertProcMounted assertProcMounted( _root );
1929 
1930  // Why nodeps?
1931  //
1932  // Because zypp builds the transaction and the resolver asserts that
1933  // everything is fine, or the user decided to ignore problems.
1934  rpm::RpmInstFlags flags( policy_r.rpmInstFlags()
1936  // skip signature checks, we did that already
1939  // ignore untrusted keys since we already checked those earlier
1941 
1942  zpt::Commit commit;
1943  commit.set_flags( flags );
1944  commit.set_ignorearch( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
1945  commit.set_arch( ZConfig::instance().systemArchitecture().asString() );
1946  commit.set_dbpath( rpm().dbPath().asString() );
1947  commit.set_root( rpm().root().asString() );
1948 
1949  bool abort = false;
1950  zypp::AutoDispose<std::unordered_map<int, ManagedFile>> locCache([]( std::unordered_map<int, ManagedFile> &data ){
1951  for ( auto &[_, value] : data ) {
1952  (void)_; // unsused; for older g++ versions
1953  value.resetDispose();
1954  }
1955  data.clear();
1956  });
1957 
1958  // fill the transaction
1959  for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; ++stepId ) {
1960  auto &step = steps[stepId];
1961  PoolItem citem( step );
1962  if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
1963  if ( citem->isKind<Package>() )
1964  {
1965  // for packages this means being obsoleted (by rpm)
1966  // thius no additional action is needed.
1967  step.stepStage( sat::Transaction::STEP_DONE );
1968  continue;
1969  }
1970  }
1971 
1972  if ( citem->isKind<Package>() ) {
1973  Package::constPtr p = citem->asKind<Package>();
1974  if ( citem.status().isToBeInstalled() )
1975  {
1976  try {
1977  locCache.value()[stepId] = packageCache_r.get( citem );
1978 
1979  zpt::TransactionStep tStep;
1980  tStep.set_stepid( stepId );
1981  tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
1982  tStep.mutable_install()->set_multiversion( p->multiversionInstall() );
1983 
1984  *commit.mutable_steps()->Add( ) = std::move(tStep);
1985  }
1986  catch ( const AbortRequestException &e )
1987  {
1988  WAR << "commit aborted by the user" << endl;
1989  abort = true;
1990  step.stepStage( sat::Transaction::STEP_ERROR );
1991  break;
1992  }
1993  catch ( const SkipRequestException &e )
1994  {
1995  ZYPP_CAUGHT( e );
1996  WAR << "Skipping package " << p << " in commit" << endl;
1997  step.stepStage( sat::Transaction::STEP_ERROR );
1998  continue;
1999  }
2000  catch ( const Exception &e )
2001  {
2002  // bnc #395704: missing catch causes abort.
2003  // TODO see if packageCache fails to handle errors correctly.
2004  ZYPP_CAUGHT( e );
2005  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2006  step.stepStage( sat::Transaction::STEP_ERROR );
2007  continue;
2008  }
2009  } else {
2010 
2011  zpt::TransactionStep tStep;
2012  tStep.set_stepid( stepId );
2013  tStep.mutable_remove()->set_name( p->name() );
2014  tStep.mutable_remove()->set_version( p->edition().version() );
2015  tStep.mutable_remove()->set_release( p->edition().release() );
2016  tStep.mutable_remove()->set_arch( p->arch().asString() );
2017 
2018  *commit.mutable_steps()->Add() = std::move(tStep);
2019 
2020  }
2021  } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
2022  // SrcPackage is install-only
2023  SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2024 
2025  try {
2026  // provide on local disk
2027  locCache.value()[stepId] = provideSrcPackage( p );
2028 
2029  zpt::TransactionStep tStep;
2030  tStep.set_stepid( stepId );
2031  tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
2032  tStep.mutable_install()->set_multiversion( false );
2033  *commit.mutable_steps()->Add() = std::move(tStep);
2034 
2035  } catch ( const Exception &e ) {
2036  ZYPP_CAUGHT( e );
2037  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2038  step.stepStage( sat::Transaction::STEP_ERROR );
2039  continue;
2040  }
2041  }
2042  }
2043 
2044  std::vector<sat::Solvable> successfullyInstalledPackages;
2045 
2046  if ( commit.steps_size() ) {
2047 
2048  // create the event loop early
2049  auto loop = zyppng::EventLoop::create();
2050 
2051  attemptToModify();
2052 
2053 
2054  // transaction related variables:
2055  //
2056  // the index of the step in the transaction list that we currenty execute.
2057  // this can be -1
2058  int currentStepId = -1;
2059 
2060  // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2061  // the script fd, once we receive it we set this flag to true and ignore all output
2062  // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2063  // and start a new one
2064  bool gotEndOfScript = false;
2065 
2066  // the possible reports we emit during the transaction
2067  std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2068  std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2069  std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2070  std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2071  std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2072 
2073  // this will be set if we receive a transaction error description
2074  std::optional<zpt::TransactionError> transactionError;
2075 
2076  // infos about the currently executed script, empty if no script is currently executed
2077  std::string currentScriptType;
2078  std::string currentScriptPackage;
2079 
2080  // buffer to collect rpm output per report, this will be written to the log once the
2081  // report ends
2082  std::string rpmmsg;
2083 
2084  // maximum number of lines that we are buffering in rpmmsg
2085  constexpr auto MAXRPMMESSAGELINES = 10000;
2086 
2087  // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2088  unsigned lineno = 0;
2089 
2090  // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2091  auto msgSource = zyppng::AsyncDataSource::create();
2092  auto scriptSource = zyppng::AsyncDataSource::create();
2093 
2094 
2095  // helper function that sends RPM output to the currently active report, writing a warning to the log
2096  // if there is none
2097  const auto &sendRpmLineToReport = [&]( const std::string &line ){
2098 
2099  const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2100  callback::UserData cmdout(cType);
2101  if ( currentStepId >= 0 )
2102  cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2103  cmdout.set( "line", line );
2104  report->report(cmdout);
2105  };
2106 
2107  if ( installreport ) {
2108  sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2109  } else if ( uninstallreport ) {
2110  sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2111  } else if ( scriptreport ) {
2112  sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2113  } else if ( transactionreport ) {
2114  sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2115  } else if ( cleanupreport ) {
2116  sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2117  } else {
2118  WAR << "Got rpm output without active report " << line; // no endl! - readLine does not trim
2119  }
2120 
2121  // remember rpm output
2122  if ( lineno >= MAXRPMMESSAGELINES ) {
2123  if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2124  return;
2125  }
2126  rpmmsg += line;
2127  if ( line.back() != '\n' )
2128  rpmmsg += '\n';
2129  };
2130 
2131 
2132  // callback and helper function to process data that is received on the script FD
2133  const auto &processDataFromScriptFd = [&](){
2134 
2135  while ( scriptSource->canReadLine() ) {
2136 
2137  if ( gotEndOfScript )
2138  return;
2139 
2140  std::string l = scriptSource->readLine().asString();
2141  if( str::endsWith( l, endOfScriptTag ) ) {
2142  gotEndOfScript = true;
2143  std::string::size_type rawsize { l.size() - endOfScriptTag.size() };
2144  if ( not rawsize )
2145  return;
2146  l = l.substr( 0, rawsize );
2147  }
2148  L_DBG("zypp-rpm") << "[rpm> " << l; // no endl! - readLine does not trim
2149  sendRpmLineToReport( l );
2150  }
2151  };
2152  scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2153 
2154  // helper function that just waits until the end of script tag was received on the scriptSource
2155  const auto &waitForScriptEnd = [&]() {
2156 
2157  // nothing to wait for
2158  if ( gotEndOfScript )
2159  return;
2160 
2161  // we process all available data
2162  processDataFromScriptFd();
2163 
2164  // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2165  while ( scriptSource->canRead() && !gotEndOfScript ) {
2166  // readyRead will trigger processDataFromScriptFd so no need to call it again
2167  // we still got nothing, lets wait for more
2168  scriptSource->waitForReadyRead( 100 );
2169  }
2170  };
2171 
2172  const auto &aboutToStartNewReport = [&](){
2173 
2174  if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2175  ERR << "There is still a running report, this is a bug" << std::endl;
2176  assert(false);
2177  }
2178 
2179  gotEndOfScript = false;
2180  };
2181 
2182  const auto &writeRpmMsgToHistory = [&](){
2183  if ( rpmmsg.size() == 0 )
2184  return;
2185 
2186  if ( lineno >= MAXRPMMESSAGELINES )
2187  rpmmsg += "[truncated]\n";
2188 
2189  std::ostringstream sstr;
2190  sstr << "rpm output:" << endl << rpmmsg << endl;
2191  HistoryLog().comment(sstr.str());
2192  };
2193 
2194  // helper function that closes the current report and cleans up the ressources
2195  const auto &finalizeCurrentReport = [&]() {
2196  sat::Transaction::Step *step = nullptr;
2197  Resolvable::constPtr resObj;
2198  if ( currentStepId >= 0 ) {
2199  step = &steps.at(currentStepId);
2200  resObj = makeResObject( step->satSolvable() );
2201  }
2202 
2203  if ( installreport ) {
2204  waitForScriptEnd();
2205  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2206 
2207  HistoryLog().comment(
2208  str::form("%s install failed", step->ident().c_str()),
2209  true /*timestamp*/);
2210 
2211  writeRpmMsgToHistory();
2212 
2213  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2214  } else {
2215  ( *installreport)->progress( 100, resObj );
2216  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2217 
2218  if ( currentStepId >= 0 )
2219  locCache.value().erase( currentStepId );
2220  successfullyInstalledPackages.push_back( step->satSolvable() );
2221 
2222  PoolItem citem( *step );
2223  if ( !( flags & rpm::RPMINST_TEST ) ) {
2224  // @TODO are we really doing this just for install?
2225  if ( citem.isNeedreboot() ) {
2226  auto rebootNeededFile = root() / "/run/reboot-needed";
2227  if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2228  filesystem::touch( rebootNeededFile );
2229  }
2231  HistoryLog().install(citem);
2232  }
2233 
2234  HistoryLog().comment(
2235  str::form("%s installed ok", step->ident().c_str()),
2236  true /*timestamp*/);
2237 
2238  writeRpmMsgToHistory();
2239  }
2240  }
2241  if ( uninstallreport ) {
2242  waitForScriptEnd();
2243  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2244 
2245  HistoryLog().comment(
2246  str::form("%s uninstall failed", step->ident().c_str()),
2247  true /*timestamp*/);
2248 
2249  writeRpmMsgToHistory();
2250 
2251  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2252  } else {
2253  ( *uninstallreport)->progress( 100, resObj );
2254  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2255 
2256  PoolItem citem( *step );
2257  HistoryLog().remove(citem);
2258 
2259  HistoryLog().comment(
2260  str::form("%s removed ok", step->ident().c_str()),
2261  true /*timestamp*/);
2262 
2263  writeRpmMsgToHistory();
2264  }
2265  }
2266  if ( scriptreport ) {
2267  waitForScriptEnd();
2268  ( *scriptreport)->progress( 100, resObj );
2269  ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2270  }
2271  if ( transactionreport ) {
2272  waitForScriptEnd();
2273  ( *transactionreport)->progress( 100 );
2274  ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2275  }
2276  if ( cleanupreport ) {
2277  waitForScriptEnd();
2278  ( *cleanupreport)->progress( 100 );
2279  ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2280  }
2281  currentStepId = -1;
2282  lineno = 0;
2283  rpmmsg.clear();
2284  currentScriptType.clear();
2285  currentScriptPackage.clear();
2286  installreport.reset();
2287  uninstallreport.reset();
2288  scriptreport.reset();
2289  transactionreport.reset();
2290  cleanupreport.reset();
2291  };
2292 
2293  // This sets up the process and pushes the required transactions steps to it
2294  // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2295  //
2296  // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2297  // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2298  // 3) 2 writeable FDs that are set up by the parent Process when forking. The first FD is to be used for message sending, the second one for script output
2299 
2300  constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2301 
2302  const char *argv[] = {
2303  //"gdbserver",
2304  //"localhost:10001",
2305  zyppRpmBinary.data(),
2306  nullptr
2307  };
2308  auto prog = zyppng::Process::create();
2309 
2310  // we set up a pipe to communicate with the process, it is too dangerous to use stdout since librpm
2311  // might print to it.
2312  auto messagePipe = zyppng::Pipe::create();
2313  if ( !messagePipe )
2314  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2315 
2316  // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2317  // way than a FD to redirect that output
2318  auto scriptPipe = zyppng::Pipe::create();
2319  if ( !scriptPipe )
2320  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2321 
2322  prog->addFd( messagePipe->writeFd );
2323  prog->addFd( scriptPipe->writeFd );
2324 
2325  // set up the AsyncDataSource to read script output
2326  if ( !scriptSource->open( scriptPipe->readFd ) )
2327  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2328 
2329  prog->sigStarted().connect( [&](){
2330 
2331  // close the ends of the pipes we do not care about
2332  messagePipe->unrefWrite();
2333  scriptPipe->unrefWrite();
2334 
2335  // read the stdout and forward it to our log
2336  prog->stdoutDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2337  while( prog->stdoutDevice()->canReadLine() ) {
2338  L_DBG("zypp-rpm") << "<stdout> " << prog->stdoutDevice()->readLine().asStringView(); // no endl! - readLine does not trim
2339  }
2340  });
2341 
2342  // read the stderr and forward it to our log
2343  prog->stderrDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2344  while( prog->stderrDevice()->canReadLine() ) {
2345  L_ERR("zypp-rpm") << "<stderr> " << prog->stderrDevice()->readLine().asStringView(); // no endl! - readLine does not trim
2346  }
2347  });
2348 
2349  {
2350  // write the commit message in blocking mode
2351  const auto outFd = prog->stdinFd();
2352  OnScopeExit unblock([&](){
2353  io::setFDBlocking( outFd, false );
2354  });
2355  io::setFDBlocking( outFd );
2356 
2357  // first we push the commit information to the process, starting with the byte size
2358  zyppng::rpc::HeaderSizeType msgSize = commit.ByteSizeLong();
2359  const auto written = zyppng::eintrSafeCall( ::write, outFd, &msgSize, sizeof(zyppng::rpc::HeaderSizeType) );
2360  if ( written != sizeof(zyppng::rpc::HeaderSizeType) ) {
2361  prog->stop( SIGKILL );
2362  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit size to subprocess" ) );
2363  }
2364 
2365  zyppng::FileOutputStream fo ( outFd );
2366  if ( !commit.SerializeToZeroCopyStream( &fo ) ) {
2367  prog->stop( SIGKILL );
2368  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2369  }
2370  fo.Flush();
2371  }
2372 
2373  });
2374 
2375  // this is the source for control messages from zypp-rpm , we will get structured data information
2376  // in form of protobuf messages
2377  if ( !msgSource->open( messagePipe->readFd ) )
2378  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2379 
2380  size_t pendingMessageSize = 0;
2381  const auto &processMessages = [&] ( ) {
2382 
2383  // lambda function that parses the passed message type and checks if the stepId is a valid offset
2384  // in the steps list.
2385  const auto &parseMsgWithStepId = [&steps]( const auto &m, auto &p ){
2386  if ( !p.ParseFromString( m.value() ) ) {
2387  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2388  return false;
2389  }
2390 
2391  auto id = p.stepid();
2392  if ( id < 0 || id >= steps.size() ) {
2393  ERR << "Received invalid stepId: " << id << " in " << m.messagetypename() << " message from zypp-rpm, ignoring." << std::endl;
2394  return false;
2395  }
2396  return true;
2397  };
2398 
2399  while ( msgSource->bytesAvailable() ) {
2400 
2401  if ( pendingMessageSize == 0 ) {
2402  if ( msgSource->bytesAvailable() >= sizeof( zyppng::rpc::HeaderSizeType ) ) {
2403  msgSource->read( reinterpret_cast<char *>( &pendingMessageSize ), sizeof( zyppng::rpc::HeaderSizeType ) );
2404  }
2405  }
2406 
2407  if ( msgSource->bytesAvailable() < pendingMessageSize ) {
2408  return;
2409  }
2410 
2411  auto bytes = msgSource->read( pendingMessageSize );
2412  pendingMessageSize = 0;
2413 
2414  zypp::proto::Envelope m;
2415  if (! m.ParseFromArray( bytes.data(), bytes.size() ) ) {
2416  // if we get a misformed message we can not simply kill zypp-rpm since it might be executing the transaction, all we can do is
2417  // continue ( this should normally not happen , but code needs to handle it ).
2418  ERR << "Received misformed message from zypp-rpm, ignoring" << std::endl;
2419  return;
2420  }
2421 
2422  // due to librpm behaviour we need to make sense of the order of messages we receive
2423  // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2424  // Script related messages. What we do is remember the current step we are in and only close
2425  // the step when we get the start of the next one
2426  const auto &mName = m.messagetypename();
2427  if ( mName == "zypp.proto.target.RpmLog" ) {
2428 
2429  zpt::RpmLog p;
2430  if ( !p.ParseFromString( m.value() ) ) {
2431  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2432  continue;
2433  }
2434  ( p.level() >= RPMLOG_ERR ? L_ERR("zypp-rpm")
2435  : p.level() >= RPMLOG_WARNING ? L_WAR("zypp-rpm")
2436  : L_DBG("zypp-rpm") ) << "[rpm " << p.level() << "> " << p.line(); // no endl! - readLine does not trim
2437  report.sendLoglineRpm( p.line(), p.level() );
2438 
2439  } else if ( mName == "zypp.proto.target.PackageBegin" ) {
2440  finalizeCurrentReport();
2441 
2442  zpt::PackageBegin p;
2443  if ( !parseMsgWithStepId( m, p ) )
2444  continue;
2445 
2446  aboutToStartNewReport();
2447 
2448  auto & step = steps.at( p.stepid() );
2449  currentStepId = p.stepid();
2450  if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2451  uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2452  ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2453  } else {
2454  installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2455  ( *installreport )->start( makeResObject( step.satSolvable() ) );
2456  }
2457 
2458  } else if ( mName == "zypp.proto.target.PackageFinished" ) {
2459  zpt::PackageFinished p;
2460  if ( !parseMsgWithStepId( m, p ) )
2461  continue;
2462 
2463  if ( p.stepid() < 0 || p.stepid() > steps.size() )
2464  continue;
2465 
2466  // here we only set the step stage to done, we however need to wait for the next start in order to send
2467  // the finished report since there might be a error pending to be reported
2468  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_DONE );
2469 
2470  } else if ( mName == "zypp.proto.target.PackageProgress" ) {
2471  zpt::PackageProgress p;
2472  if ( !parseMsgWithStepId( m, p ) )
2473  continue;
2474 
2475  if ( uninstallreport )
2476  (*uninstallreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2477  else if ( installreport )
2478  (*installreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2479  else
2480  ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2481 
2482  } else if ( mName == "zypp.proto.target.PackageError" ) {
2483  zpt::PackageError p;
2484  if ( !parseMsgWithStepId( m, p ) )
2485  continue;
2486 
2487  if ( p.stepid() >= 0 && p.stepid() < steps.size() )
2488  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_ERROR );
2489 
2490  finalizeCurrentReport();
2491 
2492  } else if ( mName == "zypp.proto.target.ScriptBegin" ) {
2493  finalizeCurrentReport();
2494 
2495  zpt::ScriptBegin p;
2496  if ( !p.ParseFromString( m.value() ) ) {
2497  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2498  continue;
2499  }
2500 
2501  aboutToStartNewReport();
2502 
2503  Resolvable::constPtr resPtr;
2504  const auto stepId = p.stepid();
2505  if ( stepId >= 0 && stepId < steps.size() ) {
2506  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2507  }
2508 
2509  currentStepId = p.stepid();
2510  scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2511  currentScriptType = p.scripttype();
2512  currentScriptPackage = p.scriptpackage();
2513  (*scriptreport)->start( p.scripttype(), p.scriptpackage(), resPtr );
2514 
2515  } else if ( mName == "zypp.proto.target.ScriptFinished" ) {
2516 
2517  // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2518 
2519  } else if ( mName == "zypp.proto.target.ScriptError" ) {
2520 
2521  zpt::ScriptError p;
2522  if ( !p.ParseFromString( m.value() ) ) {
2523  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2524  continue;
2525  }
2526 
2527  Resolvable::constPtr resPtr;
2528  const auto stepId = p.stepid();
2529  if ( stepId >= 0 && stepId < steps.size() ) {
2530  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2531 
2532  if ( p.fatal() ) {
2533  steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2534  }
2535 
2536  }
2537 
2538  HistoryLog().comment(
2539  str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2540  true /*timestamp*/);
2541 
2542  writeRpmMsgToHistory();
2543 
2544  if ( !scriptreport ) {
2545  ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2546  continue;
2547  }
2548 
2549  // before killing the report we need to wait for the script end tag
2550  waitForScriptEnd();
2551  (*scriptreport)->finish( resPtr, p.fatal() ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2552 
2553  // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2554  scriptreport.reset();
2555  currentStepId = -1;
2556 
2557  } else if ( mName == "zypp.proto.target.CleanupBegin" ) {
2558  finalizeCurrentReport();
2559 
2560  zpt::CleanupBegin beg;
2561  if ( !beg.ParseFromString( m.value() ) ) {
2562  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2563  continue;
2564  }
2565 
2566  aboutToStartNewReport();
2567  cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2568  (*cleanupreport)->start( beg.nvra() );
2569  } else if ( mName == "zypp.proto.target.CleanupFinished" ) {
2570 
2571  finalizeCurrentReport();
2572 
2573  } else if ( mName == "zypp.proto.target.CleanupProgress" ) {
2574  zpt::CleanupProgress prog;
2575  if ( !prog.ParseFromString( m.value() ) ) {
2576  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2577  continue;
2578  }
2579 
2580  if ( !cleanupreport ) {
2581  ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2582  continue;
2583  }
2584 
2585  (*cleanupreport)->progress( prog.amount() );
2586 
2587  } else if ( mName == "zypp.proto.target.TransBegin" ) {
2588  finalizeCurrentReport();
2589 
2590  zpt::TransBegin beg;
2591  if ( !beg.ParseFromString( m.value() ) ) {
2592  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2593  continue;
2594  }
2595 
2596  aboutToStartNewReport();
2597  transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2598  (*transactionreport)->start( beg.name() );
2599  } else if ( mName == "zypp.proto.target.TransFinished" ) {
2600 
2601  finalizeCurrentReport();
2602 
2603  } else if ( mName == "zypp.proto.target.TransProgress" ) {
2604  zpt::TransProgress prog;
2605  if ( !prog.ParseFromString( m.value() ) ) {
2606  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2607  continue;
2608  }
2609 
2610  if ( !transactionreport ) {
2611  ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2612  continue;
2613  }
2614 
2615  (*transactionreport)->progress( prog.amount() );
2616  } else if ( mName == "zypp.proto.target.TransactionError" ) {
2617 
2618  zpt::TransactionError error;
2619  if ( !error.ParseFromString( m.value() ) ) {
2620  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2621  continue;
2622  }
2623 
2624  // this value is checked later
2625  transactionError = std::move(error);
2626 
2627  } else {
2628  ERR << "Received unexpected message from zypp-rpm: "<< m.messagetypename() << ", ignoring" << std::endl;
2629  return;
2630  }
2631 
2632  }
2633  };
2634  msgSource->connectFunc( &zyppng::AsyncDataSource::sigReadyRead, processMessages );
2635 
2636  // track the childs lifetime
2637  int zyppRpmExitCode = -1;
2638  prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2639  zyppRpmExitCode = code;
2640  loop->quit();
2641  });
2642 
2643  if ( !prog->start( argv ) ) {
2644  HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2645  ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2646  }
2647 
2648  loop->run();
2649 
2650  // make sure to read ALL available messages
2651  processMessages();
2652 
2653  // we will not receive a new start message , so we need to manually finalize the last report
2654  finalizeCurrentReport();
2655 
2656  // make sure to read all data from the log source
2657  bool readMsgs = false;
2658  while( prog->stderrDevice()->canReadLine() ) {
2659  readMsgs = true;
2660  MIL << "zypp-rpm: " << prog->stderrDevice()->readLine().asStringView();
2661  }
2662  while( prog->stdoutDevice()->canReadLine() ) {
2663  readMsgs = true;
2664  MIL << "zypp-rpm: " << prog->stdoutDevice()->readLine().asStringView();
2665  }
2666 
2667  while ( scriptSource->canReadLine() ) {
2668  readMsgs = true;
2669  MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2670  }
2671  if ( scriptSource->bytesAvailable() > 0 ) {
2672  readMsgs = true;
2673  MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2674  }
2675  if ( readMsgs )
2676  MIL << std::endl;
2677 
2678  switch ( zyppRpmExitCode ) {
2679  // we need to look at the summary, handle finishedwitherrors like no error here
2680  case zypprpm::NoError:
2681  case zypprpm::RpmFinishedWithError:
2682  break;
2683  case zypprpm::RpmFinishedWithTransactionError: {
2684  // here zypp-rpm sent us a error description
2685  if ( transactionError ) {
2686 
2687  std::ostringstream sstr;
2688  sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2689  for ( const auto & err : transactionError->problems() ) {
2690  sstr << " " << err.message() << "\n";
2691  }
2692  sstr << std::endl;
2694 
2695  } else {
2696  ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more information.") );
2697  }
2698  break;
2699  }
2700  case zypprpm::FailedToOpenDb:
2701  ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2702  break;
2703  case zypprpm::WrongHeaderSize:
2704  case zypprpm::WrongMessageFormat:
2705  ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2706  break;
2707  case zypprpm::RpmInitFailed:
2708  ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2709  break;
2710  case zypprpm::FailedToReadPackage:
2711  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more information.") );
2712  break;
2713  case zypprpm::FailedToAddStepToTransaction:
2714  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more information.") );
2715  break;
2716  case zypprpm::RpmOrderFailed:
2717  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more information.") );
2718  break;
2719  }
2720 
2721  for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort; ++stepId ) {
2722  auto &step = steps[stepId];
2723  PoolItem citem( step );
2724 
2725  if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2726  // other resolvables (non-Package) that are not handled by zypp-rpm
2727  if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2728  // Status is changed as the buddy package buddy
2729  // gets installed/deleted. Handle non-buddies only.
2730  if ( ! citem.buddy() && citem->isKind<Product>() ) {
2731  Product::constPtr p = citem->asKind<Product>();
2732 
2733  if ( citem.status().isToBeInstalled() ) {
2734  ERR << "Can't install orphan product without release-package! " << citem << endl;
2735  } else {
2736  // Deleting the corresponding product entry is all we con do.
2737  // So the product will no longer be visible as installed.
2738  std::string referenceFilename( p->referenceFilename() );
2739 
2740  if ( referenceFilename.empty() ) {
2741  ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2742  } else {
2743  Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2744 
2745  if ( ! rpm().hasFile( referencePath.asString() ) ) {
2746  // If it's not owned by a package, we can delete it.
2747  referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2748  if ( filesystem::unlink( referencePath ) != 0 )
2749  ERR << "Delete orphan product failed: " << referencePath << endl;
2750  } else {
2751  WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2752  }
2753  }
2754  }
2756  step.stepStage( sat::Transaction::STEP_DONE );
2757  }
2758  }
2759  }
2760  }
2761  }
2762 
2763  // Check presence of update scripts/messages. If aborting,
2764  // at least log omitted scripts.
2765  if ( ! successfullyInstalledPackages.empty() )
2766  {
2767  if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2768  successfullyInstalledPackages, abort ) )
2769  {
2770  WAR << "Commit aborted by the user" << endl;
2771  abort = true;
2772  }
2773  // send messages after scripts in case some script generates output,
2774  // that should be kept in t %ghost message file.
2775  RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2776  successfullyInstalledPackages,
2777  result_r );
2778  }
2779 
2780  // jsc#SLE-5116: Log patch status changes to history
2781  // NOTE: Should be the last action as it may need to reload
2782  // the Target in case of an incomplete transaction.
2783  logPatchStatusChanges( result_r.transaction(), *this );
2784 
2785  if ( abort ) {
2786  HistoryLog().comment( "Commit was aborted." );
2788  }
2789  }
2790 
2792 
2794  {
2795  return _rpm;
2796  }
2797 
2798  bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2799  {
2800  return _rpm.hasFile(path_str, name_str);
2801  }
2802 
2804  namespace
2805  {
2806  parser::ProductFileData baseproductdata( const Pathname & root_r )
2807  {
2809  PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2810 
2811  if ( baseproduct.isFile() )
2812  {
2813  try
2814  {
2815  ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2816  }
2817  catch ( const Exception & excpt )
2818  {
2819  ZYPP_CAUGHT( excpt );
2820  }
2821  }
2822  else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2823  {
2824  ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2825  }
2826  return ret;
2827  }
2828 
2829  inline Pathname staticGuessRoot( const Pathname & root_r )
2830  {
2831  if ( root_r.empty() )
2832  {
2833  // empty root: use existing Target or assume "/"
2834  Pathname ret ( ZConfig::instance().systemRoot() );
2835  if ( ret.empty() )
2836  return Pathname("/");
2837  return ret;
2838  }
2839  return root_r;
2840  }
2841 
2842  inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2843  {
2844  std::ifstream idfile( file_r.c_str() );
2845  for( iostr::EachLine in( idfile ); in; in.next() )
2846  {
2847  std::string line( str::trim( *in ) );
2848  if ( ! line.empty() )
2849  return line;
2850  }
2851  return std::string();
2852  }
2853  } // namespace
2855 
2857  {
2858  ResPool pool(ResPool::instance());
2859  for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2860  {
2861  Product::constPtr p = (*it)->asKind<Product>();
2862  if ( p->isTargetDistribution() )
2863  return p;
2864  }
2865  return nullptr;
2866  }
2867 
2869  {
2870  const Pathname needroot( staticGuessRoot(root_r) );
2871  const Target_constPtr target( getZYpp()->getTarget() );
2872  if ( target && target->root() == needroot )
2873  return target->requestedLocales();
2874  return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2875  }
2876 
2878  {
2879  MIL << "updateAutoInstalled if changed..." << endl;
2880  SolvIdentFile::Data newdata;
2881  for ( auto id : sat::Pool::instance().autoInstalled() )
2882  newdata.insert( IdString(id) ); // explicit ctor!
2883  _autoInstalledFile.setData( std::move(newdata) );
2884  }
2885 
2887  { return baseproductdata( _root ).registerTarget(); }
2888  // static version:
2889  std::string TargetImpl::targetDistribution( const Pathname & root_r )
2890  { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2891 
2893  { return baseproductdata( _root ).registerRelease(); }
2894  // static version:
2895  std::string TargetImpl::targetDistributionRelease( const Pathname & root_r )
2896  { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2897 
2899  { return baseproductdata( _root ).registerFlavor(); }
2900  // static version:
2901  std::string TargetImpl::targetDistributionFlavor( const Pathname & root_r )
2902  { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2903 
2905  {
2907  parser::ProductFileData pdata( baseproductdata( _root ) );
2908  ret.shortName = pdata.shortName();
2909  ret.summary = pdata.summary();
2910  return ret;
2911  }
2912  // static version:
2914  {
2916  parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2917  ret.shortName = pdata.shortName();
2918  ret.summary = pdata.summary();
2919  return ret;
2920  }
2921 
2923  {
2924  if ( _distributionVersion.empty() )
2925  {
2927  if ( !_distributionVersion.empty() )
2928  MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2929  }
2930  return _distributionVersion;
2931  }
2932  // static version
2933  std::string TargetImpl::distributionVersion( const Pathname & root_r )
2934  {
2935  std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2936  if ( distributionVersion.empty() )
2937  {
2938  // ...But the baseproduct method is not expected to work on RedHat derivatives.
2939  // On RHEL, Fedora and others the "product version" is determined by the first package
2940  // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2941  // with the $distroverpkg variable.
2942  scoped_ptr<rpm::RpmDb> tmprpmdb;
2943  if ( ZConfig::instance().systemRoot() == Pathname() )
2944  {
2945  try
2946  {
2947  tmprpmdb.reset( new rpm::RpmDb );
2948  tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2949  }
2950  catch( ... )
2951  {
2952  return "";
2953  }
2954  }
2957  distributionVersion = it->tag_version();
2958  }
2959  return distributionVersion;
2960  }
2961 
2962 
2964  {
2965  return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2966  }
2967  // static version:
2968  std::string TargetImpl::distributionFlavor( const Pathname & root_r )
2969  {
2970  return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
2971  }
2972 
2974  namespace
2975  {
2976  std::string guessAnonymousUniqueId( const Pathname & root_r )
2977  {
2978  // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
2979  std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
2980  if ( ret.empty() && root_r != "/" )
2981  {
2982  // if it has nonoe, use the outer systems one
2983  ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
2984  }
2985  return ret;
2986  }
2987  }
2988 
2989  std::string TargetImpl::anonymousUniqueId() const
2990  {
2991  return guessAnonymousUniqueId( root() );
2992  }
2993  // static version:
2994  std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
2995  {
2996  return guessAnonymousUniqueId( staticGuessRoot(root_r) );
2997  }
2998 
3000 
3001  void TargetImpl::vendorAttr( VendorAttr vendorAttr_r )
3002  {
3003  MIL << "New VendorAttr: " << vendorAttr_r << endl;
3004  _vendorAttr = std::move(vendorAttr_r);
3005  }
3007 
3008  void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3009  {
3010  // provide on local disk
3011  ManagedFile localfile = provideSrcPackage(srcPackage_r);
3012  // create a installation progress report proxy
3013  RpmInstallPackageReceiver progress( srcPackage_r );
3014  progress.connect(); // disconnected on destruction.
3015  // install it
3016  rpm().installPackage ( localfile );
3017  }
3018 
3019  ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3020  {
3021  // provide on local disk
3022  repo::RepoMediaAccess access_r;
3023  repo::SrcPackageProvider prov( access_r );
3024  return prov.provideSrcPackage( srcPackage_r );
3025  }
3027  } // namespace target
3030 } // namespace zypp
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:122
static bool fileMissing(const Pathname &pathname)
helper functor
Definition: TargetImpl.cc:882
static const UserData::ContentType contentRpmout
"zypp-rpm/scriptsa": Additional rpm output (sent immediately).
std::string asJSON() const
JSON representation.
Definition: Json.h:344
ZYppCommitResult commit(ResPool pool_r, const ZYppCommitPolicy &policy_r)
Commit changes in the pool.
Definition: TargetImpl.cc:1263
VendorAttr _vendorAttr
vendor equivalence settings.
Definition: TargetImpl.h:236
EstablishedStates::ChangedPseudoInstalled ChangedPseudoInstalled
Map holding pseudo installed items where current and established status differ.
Definition: ResPool.h:341
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:319
Interface to the rpm program.
Definition: RpmDb.h:47
Product interface.
Definition: Product.h:32
Convenience SendReport<rpm::SingleTransReport> wrapper.
Definition: TargetImpl.cc:1862
#define MIL
Definition: Logger.h:96
sat::Transaction getTransaction()
Return the Transaction computed by the last solver run.
Definition: Resolver.cc:74
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition: Resolver.cc:136
A Solvable object within the sat Pool.
Definition: Solvable.h:53
std::vector< sat::Transaction::Step > TransactionStepList
Save and restore locale set from file.
Alternating download and install.
Definition: DownloadMode.h:32
#define L_WAR(GROUP)
Definition: Logger.h:106
zypp::ContentType ContentType
Definition: UserData.h:50
#define _(MSG)
Definition: Gettext.h:37
ZYppCommitPolicy & rpmNoSignature(bool yesNo_r)
Use rpm option –nosignature (default: false)
const LocaleSet & getRequestedLocales() const
Return the requested locales.
Definition: ResPool.cc:130
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r) const
Provide SrcPackage in a local file.
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition: PathInfo.cc:1183
[M] Install(multiversion) item (
Definition: Transaction.h:67
unsigned splitEscaped(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \, bool withEmpty=false)
Split line_r into words with respect to escape delimeters.
Definition: String.h:595
bool solvfilesPathIsTemp() const
Whether we&#39;re using a temp.
Definition: TargetImpl.h:96
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:418
Solvable satSolvable() const
Return the corresponding Solvable.
Definition: Transaction.h:243
Result returned from ZYpp::commit.
void updateFileContent(const Pathname &filename, boost::function< bool()> condition, boost::function< std::string()> value)
updates the content of filename if condition is true, setting the content the the value returned by v...
Definition: TargetImpl.cc:847
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:126
First download all packages to the local cache.
Definition: DownloadMode.h:27
bool isToBeInstalled() const
Definition: ResStatus.h:253
void addSolv(const Pathname &file_r)
Load Solvables from a solv-file.
Definition: Repository.cc:320
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition: PathInfo.cc:1024
Command frame for communication with PluginScript.
Definition: PluginFrame.h:40
bool findByProvides(const std::string &tag_r)
Reset to iterate all packages that provide a certain tag.
Definition: librpmDb.cc:743
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like &#39;readlink&#39;.
Definition: PathInfo.cc:924
void setData(const Data &data_r)
Store new Data.
Definition: SolvIdentFile.h:69
IMPL_PTR_TYPE(TargetImpl)
SolvIdentFile _autoInstalledFile
user/auto installed database
Definition: TargetImpl.h:230
detail::IdType value_type
Definition: Queue.h:38
Architecture.
Definition: Arch.h:36
static ProductFileData scanFile(const Pathname &file_r)
Parse one file (or symlink) and return the ProductFileData parsed.
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition: StrMatcher.h:297
TargetImpl(const Pathname &root_r="/", bool doRebuild_r=false)
Ctor.
Definition: TargetImpl.cc:817
void stampCommand()
Log info about the current process.
Definition: HistoryLog.cc:220
#define L_ERR(GROUP)
Definition: Logger.h:107
Target::commit helper optimizing package provision.
bool isNeedreboot() const
Definition: SolvableType.h:83
ZYppCommitPolicy & rpmInstFlags(target::rpm::RpmInstFlags newFlags_r)
The default target::rpm::RpmInstFlags.
TransactionStepList & rTransactionStepList()
Manipulate transactionStepList.
Regular Expression.
Definition: StrMatcher.h:48
const sat::Transaction & transaction() const
The full transaction list.
void discardScripts()
Discard all remembered scrips.
StepStage stepStage() const
Step action result.
Definition: Transaction.cc:391
const Pathname & file() const
Return the file path.
Definition: SolvIdentFile.h:46
#define INT
Definition: Logger.h:100
int chmod(const Pathname &path, mode_t mode)
Like &#39;chmod&#39;.
Definition: PathInfo.cc:1092
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
ResStatus & status() const
Returns the current status.
Definition: PoolItem.cc:204
static const UserData::ContentType contentLogline
"zypp-rpm/logline" report a line suitable to be written to the screen.
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1613
ZYppCommitPolicy & dryRun(bool yesNo_r)
Set dry run (default: false).
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
byKind_iterator byKindBegin(const ResKind &kind_r) const
Definition: ResPool.h:261
void updateAutoInstalled()
Update the database of autoinstalled packages.
Definition: TargetImpl.cc:2877
ZYppCommitPolicy & rpmExcludeDocs(bool yesNo_r)
Use rpm option –excludedocs (default: false)
const char * c_str() const
String representation.
Definition: Pathname.h:110
std::string _distributionVersion
Cache distributionVersion.
Definition: TargetImpl.h:234
void commitFindFileConflicts(const ZYppCommitPolicy &policy_r, ZYppCommitResult &result_r)
Commit helper checking for file conflicts after download.
Parallel execution of stateful PluginScripts.
void setData(const Data &data_r)
Store new Data.
Definition: HardLocksFile.h:73
void setAutoInstalled(const Queue &autoInstalled_r)
Set ident list of all autoinstalled solvables.
Definition: Pool.cc:265
sat::Solvable buddy() const
Return the buddy we share our status object with.
Definition: PoolItem.cc:206
Access to the sat-pools string space.
Definition: IdString.h:42
Libsolv transaction wrapper.
Definition: Transaction.h:51
Pathname path() const
Definition: TmpPath.cc:146
Edition represents [epoch:]version[-release]
Definition: Edition.h:60
Attempts to create a lock to prevent the system from going into hibernate/shutdown.
std::string receiveLine()
Read one line from the input stream.
bool resetTransact(TransactByValue causer_r)
Not the same as setTransact( false ).
Definition: ResStatus.h:485
static const UserData::ContentType contentRpmout
"zypp-rpm/transactionsa": Additional rpm output (sent immediately).
Similar to DownloadInAdvance, but try to split the transaction into heaps, where at the end of each h...
Definition: DownloadMode.h:29
bool providesFile(const std::string &path_str, const std::string &name_str) const
If the package is installed and provides the file Needed to evaluate split provides during Resolver::...
Definition: TargetImpl.cc:2798
TraitsType::constPtrType constPtr
Definition: Product.h:38
const_iterator end() const
Iterator behind the last TransactionStep.
Definition: Transaction.cc:343
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:127
unsigned epoch_t
Type of an epoch.
Definition: Edition.h:64
void writeUpgradeTestcase()
Definition: TargetImpl.cc:356
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
Class representing a patch.
Definition: Patch.h:37
void installSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Install a source package on the Target.
Definition: TargetImpl.cc:3008
std::string targetDistributionFlavor() const
This is register.flavor attribute of the installed base product.
Definition: TargetImpl.cc:2898
void install(const PoolItem &pi)
Log installation (or update) of a package.
Definition: HistoryLog.cc:232
ResObject::constPtr resolvable() const
Returns the ResObject::constPtr.
Definition: PoolItem.cc:218
#define ERR
Definition: Logger.h:98
void addIdent(IdString ident_r)
Add all sat::Solvable with this ident_r.
JSON object.
Definition: Json.h:321
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from their initial one.
Definition: ResPool.h:349
std::vector< std::string > Arguments
std::string targetDistributionRelease() const
This is register.release attribute of the installed base product.
Definition: TargetImpl.cc:2892
Define a set of Solvables by ident and provides.
Definition: SolvableSpec.h:44
Extract and remember posttrans scripts for later execution.
Subclass to retrieve database content.
Definition: librpmDb.h:335
void remember(const Exception &old_r)
Store an other Exception as history.
Definition: Exception.cc:105
EstablishedStates establishedStates() const
Factory for EstablishedStates.
Definition: ResPool.cc:76
bool write(const Pathname &path_r, const std::string &key_r, const std::string &val_r, const std::string &newcomment_r)
Add or change a value in sysconfig file path_r.
Definition: sysconfig.cc:80
rpm::RpmDb _rpm
RPM database.
Definition: TargetImpl.h:226
Repository systemRepo()
Return the system repository, create it if missing.
Definition: Pool.cc:178
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: TargetImpl.cc:2922
const LocaleSet & locales() const
Return the loacale set.
void createLastDistributionFlavorCache() const
generates a cache of the last product flavor
Definition: TargetImpl.cc:909
void initRequestedLocales(const LocaleSet &locales_r)
Start tracking changes based on this locales_r.
Definition: Pool.cc:251
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
Definition: RepoStatus.cc:212
StringQueue autoInstalled() const
Return the ident strings of all packages that would be auto-installed after the transaction is run...
Definition: Transaction.cc:358
LocaleSet requestedLocales() const
Languages to be supported by the system.
Definition: TargetImpl.h:157
[ ] Nothing (includes implicit deletes due to obsoletes and non-package actions)
Definition: Transaction.h:64
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition: PathInfo.cc:1101
void push(value_type val_r)
Push a value to the end off the Queue.
Definition: Queue.cc:103
std::string getline(std::istream &str)
Read one line from stream.
Definition: IOStream.cc:33
std::string toJSON(void)
Definition: Json.h:136
Pathname _mountpoint
Definition: TargetImpl.cc:277
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:26
Store and operate on date (time_t).
Definition: Date.h:32
SolvableIterator solvablesEnd() const
Iterator behind the last Solvable.
Definition: Repository.cc:241
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
const Data & data() const
Return the data.
Definition: SolvIdentFile.h:53
std::string version() const
Version.
Definition: Edition.cc:94
Pathname _root
Path to the target.
Definition: TargetImpl.h:224
std::string rpmDbStateHash(const Pathname &root_r)
Definition: TargetImpl.cc:95
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:700
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition: UserData.h:118
static const std::string & systemRepoAlias()
Reserved system repository alias .
Definition: Pool.cc:46
bool collectScriptFromPackage(ManagedFile rpmPackage_r)
Extract and remember a packages posttrans script for later execution.
static const Pathname & fname()
Get the current log file path.
Definition: HistoryLog.cc:179
bool executeScripts()
Execute the remembered scripts.
const std::string & asString() const
String representation.
Definition: Pathname.h:91
void send(const PluginFrame &frame_r)
Send PluginFrame to all open plugins.
int rename(const Pathname &oldpath, const Pathname &newpath)
Like &#39;rename&#39;.
Definition: PathInfo.cc:742
Just download all packages to the local cache.
Definition: DownloadMode.h:25
Options and policies for ZYpp::commit.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
libzypp will decide what to do.
Definition: DownloadMode.h:24
A single step within a Transaction.
Definition: Transaction.h:218
Package interface.
Definition: Package.h:32
ZYppCommitPolicy & downloadMode(DownloadMode val_r)
Commit download policy to use.
void parseFrom(const InputStream &istr_r)
Parse file istr_r and add its specs (one per line, #-comments).
RequestedLocalesFile _requestedLocalesFile
Requested Locales database.
Definition: TargetImpl.h:228
void setLocales(const LocaleSet &locales_r)
Store a new locale set.
Pathname rootDir() const
Get rootdir (for file conflicts check)
Definition: Pool.cc:64
void getHardLockQueries(HardLockQueries &activeLocks_r)
Suggest a new set of queries based on the current selection.
Definition: ResPool.cc:106
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition: Pathname.cc:235
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:412
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from the established one...
Definition: PoolImpl.cc:26
std::string release() const
Release.
Definition: Edition.cc:110
Interim helper class to collect global options and settings.
Definition: ZConfig.h:63
#define WAR
Definition: Logger.h:97
Definition of vendor equivalence.
Definition: VendorAttr.h:60
SolvableIterator solvablesBegin() const
Iterator to the first Solvable.
Definition: Repository.cc:231
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
void sendLoglineRpm(const std::string &line_r, unsigned rpmlevel_r)
Convenience to send a contentLogline translating a rpm loglevel.
Definition: TargetImpl.cc:1873
bool order()
Order transaction steps for commit.
Definition: Transaction.cc:328
Pathname solvfilesPath() const
The solv file location actually in use (default or temp).
Definition: TargetImpl.h:92
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: TargetImpl.cc:2886
Resolver & resolver() const
The Resolver.
Definition: ResPool.cc:61
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
TraitsType::constPtrType constPtr
Definition: Patch.h:43
JSON array.
Definition: Json.h:256
const VendorAttr & vendorAttr() const
The targets current vendor equivalence settings.
Definition: TargetImpl.h:201
void initDatabase(Pathname root_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database below root_r.
Definition: RpmDb.cc:266
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like &#39;symlink&#39;.
Definition: PathInfo.cc:855
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition: RpmDb.cc:356
ZYppCommitPolicy & restrictToMedia(unsigned mediaNr_r)
Restrict commit to media 1.
std::list< PoolItem > PoolItemList
list of pool items
Definition: TargetImpl.h:59
std::string anonymousUniqueId() const
anonymous unique id
Definition: TargetImpl.cc:2989
const Pathname & _root
Definition: RepoManager.cc:144
static const UserData::ContentType contentRpmout
"zypp-rpm/installpkgsa": Additional rpm output (sent immediately).
static PoolImpl & myPool()
Definition: PoolImpl.cc:180
RepoStatus rpmDbRepoStatus(const Pathname &root_r)
Definition: TargetImpl.cc:113
const char * c_str() const
Conversion to const char *
Definition: IdString.cc:50
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition: String.h:1092
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition: String.cc:177
Pathname home() const
The directory to store things.
Definition: TargetImpl.h:120
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1234
void addProvides(Capability provides_r)
A all sat::Solvable matching this provides_r.
static std::string generateRandomId()
generates a random id using uuidgen
Definition: TargetImpl.cc:836
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:176
Provides files from different repos.
ManagedFile get(const PoolItem &citem_r)
Provide a package.
HardLocksFile _hardLocksFile
Hard-Locks database.
Definition: TargetImpl.h:232
SolvableIdType size_type
Definition: PoolMember.h:126
static void setRoot(const Pathname &root)
Set new root directory to the default history log file path.
Definition: HistoryLog.cc:163
int close()
Wait for the progamm to complete.
byKind_iterator byKindEnd(const ResKind &kind_r) const
Definition: ResPool.h:268
void setHardLockQueries(const HardLockQueries &newLocks_r)
Set a new set of queries.
Definition: ResPool.cc:103
void setSingleTransactionMode(bool yesno_r)
#define SUBST_IF(PAT, VAL)
std::list< UpdateNotificationFile > UpdateNotifications
Libsolv Id queue wrapper.
Definition: Queue.h:34
#define L_DBG(GROUP)
Definition: Logger.h:104
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:426
TraitsType::constPtrType constPtr
Definition: Resolvable.h:59
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:605
SrcPackage interface.
Definition: SrcPackage.h:29
bool upgradeMode() const
Definition: Resolver.cc:97
Global ResObject pool.
Definition: ResPool.h:60
Product::constPtr baseProduct() const
returns the target base installed product, also known as the distribution or platform.
Definition: TargetImpl.cc:2856
void createAnonymousId() const
generates the unique anonymous id which is called when creating the target
Definition: TargetImpl.cc:887
ZYppCommitPolicy & allMedia()
Process all media (default)
const_iterator begin() const
Iterator to the first TransactionStep.
Definition: Transaction.cc:337
pool::PoolTraits::HardLockQueries Data
Definition: HardLocksFile.h:41
#define NON_MOVABLE(CLASS)
Delete move ctor and move assign.
Definition: Easy.h:69
void add(const Value &val_r)
Push JSON Value to Array.
Definition: Json.h:271
StepType stepType() const
Type of action to perform in this step.
Definition: Transaction.cc:388
const Data & data() const
Return the data.
Definition: HardLocksFile.h:57
Base class for Exception.
Definition: Exception.h:145
bool preloaded() const
Whether preloaded hint is set.
const std::string & command() const
The command we&#39;re executing.
void load(const Pathname &path_r)
Find and launch plugins sending PLUGINBEGIN.
Data returned by ProductFileReader.
static Date now()
Return the current time.
Definition: Date.h:78
A sat capability.
Definition: Capability.h:59
std::string asJSON() const
JSON representation.
Definition: Json.h:279
void remove(const PoolItem &pi)
Log removal of a package.
Definition: HistoryLog.cc:260
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition: RpmDb.cc:1810
Typesafe passing of user data via callbacks.
Definition: UserData.h:38
epoch_t epoch() const
Epoch.
Definition: Edition.cc:82
std::string distroverpkg() const
Package telling the "product version" on systems not using /etc/product.d/baseproduct.
Definition: ZConfig.cc:1176
Pathname root() const
The root set for this target.
Definition: TargetImpl.h:116
void setNeedrebootSpec(sat::SolvableSpec needrebootSpec_r)
Solvables which should trigger the reboot-needed hint if installed/updated.
Definition: Pool.cc:267
virtual ~TargetImpl()
Dtor.
Definition: TargetImpl.cc:945
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:93
void eraseFromPool()
Remove this Repository from its Pool.
Definition: Repository.cc:297
Global sat-pool.
Definition: Pool.h:46
bool hasFile(const std::string &file_r, const std::string &name_r="") const
Return true if at least one package owns a certain file (name_r empty) Return true if package name_r ...
Definition: RpmDb.cc:967
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Definition: HistoryLog.cc:188
#define NON_COPYABLE(CLASS)
Delete copy ctor and copy assign.
Definition: Easy.h:59
TraitsType::constPtrType constPtr
Definition: SrcPackage.h:36
static const UserData::ContentType contentRpmout
"zypp-rpm/cleanupkgsa": Additional rpm output (sent immediately).
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
bool solvablesEmpty() const
Whether Repository contains solvables.
Definition: Repository.cc:219
ResObject::Ptr makeResObject(const sat::Solvable &solvable_r)
Create ResObject from sat::Solvable.
Definition: ResObject.cc:43
sat::Transaction & rTransaction()
Manipulate transaction.
Combining sat::Solvable and ResStatus.
Definition: PoolItem.h:50
bool singleTransModeEnabled() const
Pathname systemRoot() const
The target root directory.
Definition: ZConfig.cc:815
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Provides a source package on the Target.
Definition: TargetImpl.cc:3019
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:218
Target::DistributionLabel distributionLabel() const
This is shortName and summary attribute of the installed base product.
Definition: TargetImpl.cc:2904
Track changing files or directories.
Definition: RepoStatus.h:40
std::string asString() const
Conversion to std::string
Definition: IdString.h:98
bool isKind(const ResKind &kind_r) const
Definition: SolvableType.h:64
const std::string & asString() const
Definition: Arch.cc:489
void XRunUpdateMessages(const Pathname &root_r, const Pathname &messagesPath_r, const std::vector< sat::Solvable > &checkPackages_r, ZYppCommitResult &result_r)
Definition: TargetImpl.cc:802
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
Definition: TargetImpl.cc:2963
static const UserData::ContentType contentRpmout
"zypp-rpm/removepkgsa": Additional rpm output (sent immediately).
size_type solvablesSize() const
Number of solvables in Repository.
Definition: Repository.cc:225
void commitInSingleTransaction(const ZYppCommitPolicy &policy_r, CommitPackageCache &packageCache_r, ZYppCommitResult &result_r)
Commit ordered changes (internal helper)
Definition: TargetImpl.cc:1909
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
std::unordered_set< IdString > Data
Definition: SolvIdentFile.h:37
Pathname defaultSolvfilesPath() const
The systems default solv file location.
Definition: TargetImpl.cc:958
#define idstr(V)
Solvable satSolvable() const
Return the corresponding sat::Solvable.
Definition: SolvableType.h:57
void add(const String &key_r, const Value &val_r)
Add key/value pair.
Definition: Json.h:336
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1027
void setCommitList(std::vector< sat::Solvable > commitList_r)
Download(commit) sequence of solvables to compute read ahead.
bool empty() const
Whether this is an empty object without valid data.
std::unordered_set< Locale > LocaleSet
Definition: Locale.h:27
TrueBool _guard
Definition: TargetImpl.cc:1560
void report(const callback::UserData &userData_r)
Definition: TargetImpl.cc:1897
rpm::RpmDb & rpm()
The RPM database.
Definition: TargetImpl.cc:2793
TraitsType::constPtrType constPtr
Definition: Package.h:38
BlockingMode setFDBlocking(int fd, bool mode)
Definition: IOTools.cc:31
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:63
#define DBG
Definition: Logger.h:95
ZYppCommitResult & _result
Definition: TargetImpl.cc:1561
Mime type like &#39;type/subtype&#39; classification of content.
Definition: ContentType.h:29
static ResPool instance()
Singleton ctor.
Definition: ResPool.cc:37
void sendLogline(const std::string &line_r, ReportType::loglevel level_r=ReportType::loglevel::msg)
Convenience to send a contentLogline.
Definition: TargetImpl.cc:1865
void load(bool force=true)
Definition: TargetImpl.cc:1126