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