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