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