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