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