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