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