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