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