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