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