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