libzypp  17.28.5
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  std::vector<ManagedFile> locFiles;
1899 
1900  // fill the transaction
1901  for( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; stepId++ ) {
1902  auto &step = steps[stepId];
1903  PoolItem citem( step );
1904  if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
1905  if ( citem->isKind<Package>() )
1906  {
1907  // for packages this means being obsoleted (by rpm)
1908  // thius no additional action is needed.
1909  step.stepStage( sat::Transaction::STEP_DONE );
1910  continue;
1911  }
1912  }
1913 
1914  if ( citem->isKind<Package>() ) {
1915  Package::constPtr p = citem->asKind<Package>();
1916  if ( citem.status().isToBeInstalled() )
1917  {
1918  try {
1919  locFiles.push_back( packageCache_r.get( citem ) );
1920 
1921  zpt::TransactionStep tStep;
1922  tStep.set_stepid( stepId );
1923  tStep.mutable_install()->set_pathname( locFiles.back()->asString() );
1924  tStep.mutable_install()->set_multiversion( p->multiversionInstall() );
1925 
1926  *commit.mutable_steps()->Add( ) = std::move(tStep);
1927  }
1928  catch ( const AbortRequestException &e )
1929  {
1930  WAR << "commit aborted by the user" << endl;
1931  abort = true;
1932  step.stepStage( sat::Transaction::STEP_ERROR );
1933  break;
1934  }
1935  catch ( const SkipRequestException &e )
1936  {
1937  ZYPP_CAUGHT( e );
1938  WAR << "Skipping package " << p << " in commit" << endl;
1939  step.stepStage( sat::Transaction::STEP_ERROR );
1940  continue;
1941  }
1942  catch ( const Exception &e )
1943  {
1944  // bnc #395704: missing catch causes abort.
1945  // TODO see if packageCache fails to handle errors correctly.
1946  ZYPP_CAUGHT( e );
1947  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1948  step.stepStage( sat::Transaction::STEP_ERROR );
1949  continue;
1950  }
1951  } else {
1952 
1953  zpt::TransactionStep tStep;
1954  tStep.set_stepid( stepId );
1955  tStep.mutable_remove()->set_name( p->name() );
1956  tStep.mutable_remove()->set_version( p->edition().version() );
1957  tStep.mutable_remove()->set_release( p->edition().release() );
1958  tStep.mutable_remove()->set_arch( p->arch().asString() );
1959 
1960  *commit.mutable_steps()->Add() = std::move(tStep);
1961 
1962  }
1963  } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
1964  // SrcPackage is install-only
1965  SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1966 
1967  try {
1968  // provide on local disk
1969  locFiles.push_back( provideSrcPackage( p ) );
1970 
1971  zpt::TransactionStep tStep;
1972  tStep.set_stepid( stepId );
1973  tStep.mutable_install()->set_pathname( locFiles.back()->asString() );
1974  tStep.mutable_install()->set_multiversion( false );
1975  *commit.mutable_steps()->Add() = std::move(tStep);
1976 
1977  } catch ( const Exception &e ) {
1978  ZYPP_CAUGHT( e );
1979  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1980  step.stepStage( sat::Transaction::STEP_ERROR );
1981  continue;
1982  }
1983  }
1984  }
1985 
1986  std::vector<sat::Solvable> successfullyInstalledPackages;
1987 
1988  if ( commit.steps_size() ) {
1989 
1990  // create the event loop early
1991  auto loop = zyppng::EventLoop::create();
1992 
1993  attemptToModify();
1994 
1995 
1996  // transaction related variables:
1997  //
1998  // the index of the step in the transaction list that we currenty execute.
1999  // this can be -1
2000  int currentStepId = -1;
2001 
2002  // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2003  // the script fd, once we receive it we set this flag to true and ignore all output
2004  // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2005  // and start a new one
2006  bool gotEndOfScript = false;
2007 
2008  // the possible reports we emit during the transaction
2009  std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2010  std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2011  std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2012  std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2013  std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2014 
2015  // this will be set if we receive a transaction error description
2016  std::optional<zpt::TransactionError> transactionError;
2017 
2018  // infos about the currently executed script, empty if no script is currently executed
2019  std::string currentScriptType;
2020  std::string currentScriptPackage;
2021 
2022  // buffer to collect rpm output per report, this will be written to the log once the
2023  // report ends
2024  std::string rpmmsg;
2025 
2026  // maximum number of lines that we are buffering in rpmmsg
2027  constexpr auto MAXRPMMESSAGELINES = 10000;
2028 
2029  // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2030  unsigned lineno = 0;
2031 
2032  // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2033  auto msgSource = zyppng::AsyncDataSource::create();
2034  auto scriptSource = zyppng::AsyncDataSource::create();
2035 
2036 
2037  // helper function that sends RPM output to the currently active report, writing a warning to the log
2038  // if there is none
2039  const auto &sendRpmLineToReport = [&]( const std::string &line ){
2040 
2041  const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2042  callback::UserData cmdout(cType);
2043  if ( currentStepId >= 0 )
2044  cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2045  cmdout.set( "line", line );
2046  report->report(cmdout);
2047  };
2048 
2049  if ( installreport ) {
2050  sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2051  } else if ( uninstallreport ) {
2052  sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2053  } else if ( scriptreport ) {
2054  sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2055  } else if ( transactionreport ) {
2056  sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2057  } else if ( cleanupreport ) {
2058  sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2059  } else {
2060  WAR << "Got rpm output without active report " << line << std::endl;
2061  }
2062 
2063  // remember rpm output
2064  if ( lineno >= MAXRPMMESSAGELINES ) {
2065  if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2066  return;
2067  }
2068  rpmmsg += line;
2069  if ( line.back() != '\n' )
2070  rpmmsg += '\n';
2071  };
2072 
2073 
2074  // callback and helper function to process data that is received on the script FD
2075  const auto &processDataFromScriptFd = [&](){
2076 
2077  while ( scriptSource->canReadLine() ) {
2078 
2079  if ( gotEndOfScript )
2080  return;
2081 
2082  std::string l = scriptSource->readLine().asString();
2083  if( str::endsWith( l, endOfScriptTag ) ) {
2084  DBG << "Received end of script tag" << std::endl;
2085  gotEndOfScript = true;
2086  l = l.substr( 0, l.size() - endOfScriptTag.size() );
2087  if ( l.size() == 0 )
2088  return;
2089  }
2090 
2091  sendRpmLineToReport( l );
2092  }
2093  };
2094  scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2095 
2096  // helper function that just waits until the end of script tag was received on the scriptSource
2097  const auto &waitForScriptEnd = [&]() {
2098 
2099  // nothing to wait for
2100  if ( gotEndOfScript )
2101  return;
2102 
2103  // we process all available data
2104  processDataFromScriptFd();
2105 
2106  // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2107  while ( scriptSource->canRead() && !gotEndOfScript ) {
2108  // readyRead will trigger processDataFromScriptFd so no need to call it again
2109  // we still got nothing, lets wait for more
2110  scriptSource->waitForReadyRead( 100 );
2111  }
2112  };
2113 
2114  const auto &aboutToStartNewReport = [&](){
2115 
2116  if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2117  ERR << "There is still a running report, this is a bug" << std::endl;
2118  assert(false);
2119  }
2120 
2121  DBG << "Starting new report, setting gotEndOfScript to false" << std::endl;
2122  gotEndOfScript = false;
2123  };
2124 
2125  const auto &writeRpmMsgToHistory = [&](){
2126  if ( rpmmsg.size() == 0 )
2127  return;
2128 
2129  if ( lineno >= MAXRPMMESSAGELINES )
2130  rpmmsg += "[truncated]\n";
2131 
2132  std::ostringstream sstr;
2133  sstr << "rpm output:" << endl << rpmmsg << endl;
2134  HistoryLog().comment(sstr.str());
2135  };
2136 
2137  // helper function that closes the current report and cleans up the ressources
2138  const auto &finalizeCurrentReport = [&]() {
2139  sat::Transaction::Step *step = nullptr;
2140  Resolvable::constPtr resObj;
2141  if ( currentStepId >= 0 ) {
2142  step = &steps.at(currentStepId);
2143  resObj = makeResObject( step->satSolvable() );
2144  }
2145 
2146  if ( installreport ) {
2147  waitForScriptEnd();
2148  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2149 
2150  HistoryLog().comment(
2151  str::form("%s install failed", step->ident().c_str()),
2152  true /*timestamp*/);
2153 
2154  writeRpmMsgToHistory();
2155 
2156  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2157  } else {
2158  ( *installreport)->progress( 100, resObj );
2159  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2160 
2161  successfullyInstalledPackages.push_back( step->satSolvable() );
2162 
2163  PoolItem citem( *step );
2164  if ( !( flags & rpm::RPMINST_TEST ) ) {
2165  // @TODO are we really doing this just for install?
2166  if ( citem.isNeedreboot() ) {
2167  auto rebootNeededFile = root() / "/run/reboot-needed";
2168  if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2169  filesystem::touch( rebootNeededFile );
2170  }
2172  HistoryLog().install(citem);
2173  }
2174 
2175  HistoryLog().comment(
2176  str::form("%s installed ok", step->ident().c_str()),
2177  true /*timestamp*/);
2178 
2179  writeRpmMsgToHistory();
2180  }
2181  }
2182  if ( uninstallreport ) {
2183  waitForScriptEnd();
2184  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2185 
2186  HistoryLog().comment(
2187  str::form("%s uninstall failed", step->ident().c_str()),
2188  true /*timestamp*/);
2189 
2190  writeRpmMsgToHistory();
2191 
2192  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2193  } else {
2194  ( *uninstallreport)->progress( 100, resObj );
2195  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2196 
2197  PoolItem citem( *step );
2198  HistoryLog().remove(citem);
2199 
2200  HistoryLog().comment(
2201  str::form("%s removed ok", step->ident().c_str()),
2202  true /*timestamp*/);
2203 
2204  writeRpmMsgToHistory();
2205  }
2206  }
2207  if ( scriptreport ) {
2208  waitForScriptEnd();
2209  ( *scriptreport)->progress( 100, resObj );
2210  ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2211  }
2212  if ( transactionreport ) {
2213  waitForScriptEnd();
2214  ( *transactionreport)->progress( 100 );
2215  ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2216  }
2217  if ( cleanupreport ) {
2218  waitForScriptEnd();
2219  ( *cleanupreport)->progress( 100 );
2220  ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2221  }
2222  DBG << "Report finalized" << std::endl;
2223  currentStepId = -1;
2224  lineno = 0;
2225  rpmmsg.clear();
2226  currentScriptType.clear();
2227  currentScriptPackage.clear();
2228  installreport.reset();
2229  uninstallreport.reset();
2230  scriptreport.reset();
2231  transactionreport.reset();
2232  cleanupreport.reset();
2233  };
2234 
2235  // This sets up the process and pushes the required transactions steps to it
2236  // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2237  //
2238  // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2239  // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2240  // 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
2241 
2242  constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2243 
2244  const char *argv[] = {
2245  //"gdbserver",
2246  //"localhost:10001",
2247  zyppRpmBinary.data(),
2248  nullptr
2249  };
2250  auto prog = zyppng::Process::create();
2251 
2252  // we set up a pipe to communicate with the process, its too dangerous to use stdout since librpm
2253  // might print to it.
2254  auto messagePipe = zyppng::Pipe::create();
2255  if ( !messagePipe )
2256  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2257 
2258  // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2259  // way than a FD to redirect that output
2260  auto scriptPipe = zyppng::Pipe::create();
2261  if ( !scriptPipe )
2262  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2263 
2264  prog->addFd( messagePipe->writeFd );
2265  prog->addFd( scriptPipe->writeFd );
2266 
2267  // set up the AsyncDataSource to read script output
2268  if ( !scriptSource->open( scriptPipe->readFd ) )
2269  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2270 
2271  prog->sigStarted().connect( [&](){
2272 
2273  // close the ends of the pipes we do not care about
2274  messagePipe->unrefWrite();
2275  scriptPipe->unrefWrite();
2276 
2277  // read the stdout and forward it to our log
2278  prog->stdoutDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2279  while( prog->stdoutDevice()->canReadLine() ) {
2280  MIL << "zypp-rpm stdout: " << prog->stdoutDevice()->readLine().asStringView() << std::endl;
2281  }
2282  });
2283 
2284  // read the stderr and forward it to our log
2285  prog->stderrDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2286  while( prog->stderrDevice()->canReadLine() ) {
2287  MIL << "zypp-rpm stderr: " << prog->stderrDevice()->readLine().asStringView() << std::endl;
2288  }
2289  });
2290 
2291  {
2292  // write the commit message in blocking mode
2293  const auto outFd = prog->stdinFd();
2294  OnScopeExit unblock([&](){
2295  io::setFDBlocking( outFd, false );
2296  });
2297  io::setFDBlocking( outFd );
2298 
2299  // first we push the commit information to the process, starting with the byte size
2300  zyppng::rpc::HeaderSizeType msgSize = commit.ByteSizeLong();
2301  const auto written = zyppng::eintrSafeCall( ::write, outFd, &msgSize, sizeof(zyppng::rpc::HeaderSizeType) );
2302  if ( written != sizeof(zyppng::rpc::HeaderSizeType) ) {
2303  prog->stop( SIGKILL );
2304  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit size to subprocess" ) );
2305  }
2306 
2307  zyppng::FileOutputStream fo ( outFd );
2308  if ( !commit.SerializeToZeroCopyStream( &fo ) ) {
2309  prog->stop( SIGKILL );
2310  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2311  }
2312  fo.Flush();
2313  }
2314 
2315  });
2316 
2317  // this is the source for control messages from zypp-rpm , we will get structured data information
2318  // in form of protobuf messages
2319  if ( !msgSource->open( messagePipe->readFd ) )
2320  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2321 
2322  size_t pendingMessageSize = 0;
2323  const auto &processMessages = [&] ( ) {
2324 
2325  // lambda function that parses the passed message type and checks if the stepId is a valid offset
2326  // in the steps list.
2327  const auto &parseMsgWithStepId = [&steps]( const auto &m, auto &p ){
2328  if ( !p.ParseFromString( m.value() ) ) {
2329  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2330  return false;
2331  }
2332 
2333  auto id = p.stepid();
2334  if ( id < 0 || id >= steps.size() ) {
2335  ERR << "Received invalid stepId: " << id << " in " << m.messagetypename() << " message from zypp-rpm, ignoring." << std::endl;
2336  return false;
2337  }
2338  return true;
2339  };
2340 
2341  while ( msgSource->bytesAvailable() ) {
2342 
2343  if ( pendingMessageSize == 0 ) {
2344  if ( msgSource->bytesAvailable() >= sizeof( zyppng::rpc::HeaderSizeType ) ) {
2345  msgSource->read( reinterpret_cast<char *>( &pendingMessageSize ), sizeof( zyppng::rpc::HeaderSizeType ) );
2346  }
2347  }
2348 
2349  if ( msgSource->bytesAvailable() < pendingMessageSize ) {
2350  return;
2351  }
2352 
2353  auto bytes = msgSource->read( pendingMessageSize );
2354  pendingMessageSize = 0;
2355 
2356  zypp::proto::Envelope m;
2357  if (! m.ParseFromArray( bytes.data(), bytes.size() ) ) {
2358  // 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
2359  // continue ( this should normally not happen , but code needs to handle it ).
2360  ERR << "Received misformed message from zypp-rpm, ignoring" << std::endl;
2361  return;
2362  }
2363 
2364  // due to librpm behaviour we need to make sense of the order of messages we receive
2365  // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2366  // Script related messages. What we do is remember the current step we are in and only close
2367  // the step when we get the start of the next one
2368  const auto &mName = m.messagetypename();
2369  if ( mName == "zypp.proto.target.RpmLog" ) {
2370 
2371  zpt::RpmLog p;
2372  if ( !p.ParseFromString( m.value() ) ) {
2373  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2374  continue;
2375  }
2376 
2377  sendRpmLineToReport( p.line() );
2378 
2379 
2380  } else if ( mName == "zypp.proto.target.PackageBegin" ) {
2381  finalizeCurrentReport();
2382 
2383  zpt::PackageBegin p;
2384  if ( !parseMsgWithStepId( m, p ) )
2385  continue;
2386 
2387  aboutToStartNewReport();
2388 
2389  auto & step = steps.at( p.stepid() );
2390  currentStepId = p.stepid();
2391  if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2392  uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2393  ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2394  } else {
2395  installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2396  ( *installreport )->start( makeResObject( step.satSolvable() ) );
2397  }
2398 
2399  } else if ( mName == "zypp.proto.target.PackageFinished" ) {
2400  zpt::PackageFinished p;
2401  if ( !parseMsgWithStepId( m, p ) )
2402  continue;
2403 
2404  if ( p.stepid() < 0 || p.stepid() > steps.size() )
2405  continue;
2406 
2407  // here we only set the step stage to done, we however need to wait for the next start in order to send
2408  // the finished report since there might be a error pending to be reported
2409  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_DONE );
2410 
2411  } else if ( mName == "zypp.proto.target.PackageProgress" ) {
2412  zpt::PackageProgress p;
2413  if ( !parseMsgWithStepId( m, p ) )
2414  continue;
2415 
2416  if ( uninstallreport )
2417  (*uninstallreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2418  else if ( installreport )
2419  (*installreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2420  else
2421  ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2422 
2423  } else if ( mName == "zypp.proto.target.PackageError" ) {
2424  zpt::PackageError p;
2425  if ( !parseMsgWithStepId( m, p ) )
2426  continue;
2427 
2428  if ( p.stepid() >= 0 && p.stepid() < steps.size() )
2429  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_ERROR );
2430 
2431  finalizeCurrentReport();
2432 
2433  } else if ( mName == "zypp.proto.target.ScriptBegin" ) {
2434  finalizeCurrentReport();
2435 
2436  zpt::ScriptBegin p;
2437  if ( !p.ParseFromString( m.value() ) ) {
2438  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2439  continue;
2440  }
2441 
2442  aboutToStartNewReport();
2443 
2444  Resolvable::constPtr resPtr;
2445  const auto stepId = p.stepid();
2446  if ( stepId >= 0 && stepId < steps.size() ) {
2447  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2448  }
2449 
2450  currentStepId = p.stepid();
2451  scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2452  currentScriptType = p.scripttype();
2453  currentScriptPackage = p.scriptpackage();
2454  (*scriptreport)->start( p.scripttype(), p.scriptpackage(), resPtr );
2455 
2456  } else if ( mName == "zypp.proto.target.ScriptFinished" ) {
2457 
2458  // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2459  MIL << "Received" << mName << " from zypp-rpm" << std::endl;
2460 
2461  } else if ( mName == "zypp.proto.target.ScriptError" ) {
2462 
2463  zpt::ScriptError p;
2464  if ( !p.ParseFromString( m.value() ) ) {
2465  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2466  continue;
2467  }
2468 
2469  Resolvable::constPtr resPtr;
2470  const auto stepId = p.stepid();
2471  if ( stepId >= 0 && stepId < steps.size() ) {
2472  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2473 
2474  if ( p.fatal() ) {
2475  steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2476  }
2477 
2478  }
2479 
2480  HistoryLog().comment(
2481  str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2482  true /*timestamp*/);
2483 
2484  writeRpmMsgToHistory();
2485 
2486  if ( !scriptreport ) {
2487  ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2488  continue;
2489  }
2490 
2491  // before killing the report we need to wait for the script end tag
2492  waitForScriptEnd();
2493  (*scriptreport)->finish( resPtr, p.fatal() ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2494 
2495  // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2496  scriptreport.reset();
2497  currentStepId = -1;
2498 
2499  } else if ( mName == "zypp.proto.target.CleanupBegin" ) {
2500  finalizeCurrentReport();
2501 
2502  zpt::CleanupBegin beg;
2503  if ( !beg.ParseFromString( m.value() ) ) {
2504  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2505  continue;
2506  }
2507 
2508  aboutToStartNewReport();
2509  cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2510  (*cleanupreport)->start( beg.nvra() );
2511  } else if ( mName == "zypp.proto.target.CleanupFinished" ) {
2512 
2513  finalizeCurrentReport();
2514 
2515  } else if ( mName == "zypp.proto.target.CleanupProgress" ) {
2516  zpt::CleanupProgress prog;
2517  if ( !prog.ParseFromString( m.value() ) ) {
2518  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2519  continue;
2520  }
2521 
2522  if ( !cleanupreport ) {
2523  ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2524  continue;
2525  }
2526 
2527  (*cleanupreport)->progress( prog.amount() );
2528 
2529  } else if ( mName == "zypp.proto.target.TransBegin" ) {
2530  finalizeCurrentReport();
2531 
2532  zpt::TransBegin beg;
2533  if ( !beg.ParseFromString( m.value() ) ) {
2534  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2535  continue;
2536  }
2537 
2538  aboutToStartNewReport();
2539  transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2540  (*transactionreport)->start( beg.name() );
2541  } else if ( mName == "zypp.proto.target.TransFinished" ) {
2542 
2543  finalizeCurrentReport();
2544 
2545  } else if ( mName == "zypp.proto.target.TransProgress" ) {
2546  zpt::TransProgress prog;
2547  if ( !prog.ParseFromString( m.value() ) ) {
2548  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2549  continue;
2550  }
2551 
2552  if ( !transactionreport ) {
2553  ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2554  continue;
2555  }
2556 
2557  (*transactionreport)->progress( prog.amount() );
2558  } else if ( mName == "zypp.proto.target.TransactionError" ) {
2559 
2560  zpt::TransactionError error;
2561  if ( !error.ParseFromString( m.value() ) ) {
2562  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2563  continue;
2564  }
2565 
2566  // this value is checked later
2567  transactionError = std::move(error);
2568 
2569  } else {
2570  ERR << "Received unexpected message from zypp-rpm: "<< m.messagetypename() << ", ignoring" << std::endl;
2571  return;
2572  }
2573 
2574  }
2575  };
2576  msgSource->connectFunc( &zyppng::AsyncDataSource::sigReadyRead, processMessages );
2577 
2578  // track the childs lifetime
2579  int zyppRpmExitCode = -1;
2580  prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2581  zyppRpmExitCode = code;
2582  loop->quit();
2583  });
2584 
2585  if ( !prog->start( argv ) ) {
2586  HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2587  ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2588  }
2589 
2590  loop->run();
2591 
2592  // make sure to read ALL available messages
2593  processMessages();
2594 
2595  // we will not receive a new start message , so we need to manually finalize the last report
2596  finalizeCurrentReport();
2597 
2598  // make sure to read all data from the log source
2599  bool readMsgs = false;
2600  while( prog->stderrDevice()->canReadLine() ) {
2601  readMsgs = true;
2602  MIL << "zypp-rpm: " << prog->stderrDevice()->readLine().asStringView();
2603  }
2604  while( prog->stdoutDevice()->canReadLine() ) {
2605  readMsgs = true;
2606  MIL << "zypp-rpm: " << prog->stdoutDevice()->readLine().asStringView();
2607  }
2608 
2609  while ( scriptSource->canReadLine() ) {
2610  readMsgs = true;
2611  MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2612  }
2613  if ( scriptSource->bytesAvailable() > 0 ) {
2614  readMsgs = true;
2615  MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2616  }
2617  if ( readMsgs )
2618  MIL << std::endl;
2619 
2620  switch ( zyppRpmExitCode ) {
2621  // we need to look at the summary, handle finishedwitherrors like no error here
2622  case zypprpm::NoError:
2623  case zypprpm::RpmFinishedWithError:
2624  break;
2625  case zypprpm::RpmFinishedWithTransactionError: {
2626  // here zypp-rpm sent us a error description
2627  if ( transactionError ) {
2628 
2629  std::ostringstream sstr;
2630  sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2631  for ( const auto & err : transactionError->problems() ) {
2632  sstr << " " << err.message() << "\n";
2633  }
2634  sstr << std::endl;
2636 
2637  } else {
2638  ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more informations.") );
2639  }
2640  break;
2641  }
2642  case zypprpm::FailedToOpenDb:
2643  ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2644  break;
2645  case zypprpm::WrongHeaderSize:
2646  case zypprpm::WrongMessageFormat:
2647  ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2648  break;
2649  case zypprpm::RpmInitFailed:
2650  ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2651  break;
2652  case zypprpm::FailedToReadPackage:
2653  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more informations.") );
2654  break;
2655  case zypprpm::FailedToAddStepToTransaction:
2656  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more informations.") );
2657  break;
2658  case zypprpm::RpmOrderFailed:
2659  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more informations.") );
2660  break;
2661  }
2662 
2663  for( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; stepId++ ) {
2664  auto &step = steps[stepId];
2665  PoolItem citem( step );
2666 
2667  if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2668  // other resolvables (non-Package) that are not handled by zypp-rpm
2669  if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2670  // Status is changed as the buddy package buddy
2671  // gets installed/deleted. Handle non-buddies only.
2672  if ( ! citem.buddy() && citem->isKind<Product>() ) {
2673  Product::constPtr p = citem->asKind<Product>();
2674 
2675  if ( citem.status().isToBeInstalled() ) {
2676  ERR << "Can't install orphan product without release-package! " << citem << endl;
2677  } else {
2678  // Deleting the corresponding product entry is all we con do.
2679  // So the product will no longer be visible as installed.
2680  std::string referenceFilename( p->referenceFilename() );
2681 
2682  if ( referenceFilename.empty() ) {
2683  ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2684  } else {
2685  Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2686 
2687  if ( ! rpm().hasFile( referencePath.asString() ) ) {
2688  // If it's not owned by a package, we can delete it.
2689  referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2690  if ( filesystem::unlink( referencePath ) != 0 )
2691  ERR << "Delete orphan product failed: " << referencePath << endl;
2692  } else {
2693  WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2694  }
2695  }
2696  }
2698  step.stepStage( sat::Transaction::STEP_DONE );
2699  }
2700  }
2701  }
2702  }
2703  }
2704 
2705  // Check presence of update scripts/messages. If aborting,
2706  // at least log omitted scripts.
2707  if ( ! successfullyInstalledPackages.empty() )
2708  {
2709  if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2710  successfullyInstalledPackages, abort ) )
2711  {
2712  WAR << "Commit aborted by the user" << endl;
2713  abort = true;
2714  }
2715  // send messages after scripts in case some script generates output,
2716  // that should be kept in t %ghost message file.
2717  RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2718  successfullyInstalledPackages,
2719  result_r );
2720  }
2721 
2722  // jsc#SLE-5116: Log patch status changes to history
2723  // NOTE: Should be the last action as it may need to reload
2724  // the Target in case of an incomplete transaction.
2725  logPatchStatusChanges( result_r.transaction(), *this );
2726 
2727  if ( abort ) {
2728  HistoryLog().comment( "Commit was aborted." );
2730  }
2731  }
2732 
2734 
2736  {
2737  return _rpm;
2738  }
2739 
2740  bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2741  {
2742  return _rpm.hasFile(path_str, name_str);
2743  }
2744 
2746  namespace
2747  {
2748  parser::ProductFileData baseproductdata( const Pathname & root_r )
2749  {
2751  PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2752 
2753  if ( baseproduct.isFile() )
2754  {
2755  try
2756  {
2757  ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2758  }
2759  catch ( const Exception & excpt )
2760  {
2761  ZYPP_CAUGHT( excpt );
2762  }
2763  }
2764  else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2765  {
2766  ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2767  }
2768  return ret;
2769  }
2770 
2771  inline Pathname staticGuessRoot( const Pathname & root_r )
2772  {
2773  if ( root_r.empty() )
2774  {
2775  // empty root: use existing Target or assume "/"
2776  Pathname ret ( ZConfig::instance().systemRoot() );
2777  if ( ret.empty() )
2778  return Pathname("/");
2779  return ret;
2780  }
2781  return root_r;
2782  }
2783 
2784  inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2785  {
2786  std::ifstream idfile( file_r.c_str() );
2787  for( iostr::EachLine in( idfile ); in; in.next() )
2788  {
2789  std::string line( str::trim( *in ) );
2790  if ( ! line.empty() )
2791  return line;
2792  }
2793  return std::string();
2794  }
2795  } // namespace
2797 
2799  {
2800  ResPool pool(ResPool::instance());
2801  for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2802  {
2803  Product::constPtr p = (*it)->asKind<Product>();
2804  if ( p->isTargetDistribution() )
2805  return p;
2806  }
2807  return nullptr;
2808  }
2809 
2811  {
2812  const Pathname needroot( staticGuessRoot(root_r) );
2813  const Target_constPtr target( getZYpp()->getTarget() );
2814  if ( target && target->root() == needroot )
2815  return target->requestedLocales();
2816  return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2817  }
2818 
2820  {
2821  MIL << "updateAutoInstalled if changed..." << endl;
2822  SolvIdentFile::Data newdata;
2823  for ( auto id : sat::Pool::instance().autoInstalled() )
2824  newdata.insert( IdString(id) ); // explicit ctor!
2825  _autoInstalledFile.setData( std::move(newdata) );
2826  }
2827 
2829  { return baseproductdata( _root ).registerTarget(); }
2830  // static version:
2831  std::string TargetImpl::targetDistribution( const Pathname & root_r )
2832  { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2833 
2835  { return baseproductdata( _root ).registerRelease(); }
2836  // static version:
2837  std::string TargetImpl::targetDistributionRelease( const Pathname & root_r )
2838  { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2839 
2841  { return baseproductdata( _root ).registerFlavor(); }
2842  // static version:
2843  std::string TargetImpl::targetDistributionFlavor( const Pathname & root_r )
2844  { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2845 
2847  {
2849  parser::ProductFileData pdata( baseproductdata( _root ) );
2850  ret.shortName = pdata.shortName();
2851  ret.summary = pdata.summary();
2852  return ret;
2853  }
2854  // static version:
2856  {
2858  parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2859  ret.shortName = pdata.shortName();
2860  ret.summary = pdata.summary();
2861  return ret;
2862  }
2863 
2865  {
2866  if ( _distributionVersion.empty() )
2867  {
2869  if ( !_distributionVersion.empty() )
2870  MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2871  }
2872  return _distributionVersion;
2873  }
2874  // static version
2875  std::string TargetImpl::distributionVersion( const Pathname & root_r )
2876  {
2877  std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2878  if ( distributionVersion.empty() )
2879  {
2880  // ...But the baseproduct method is not expected to work on RedHat derivatives.
2881  // On RHEL, Fedora and others the "product version" is determined by the first package
2882  // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2883  // with the $distroverpkg variable.
2884  scoped_ptr<rpm::RpmDb> tmprpmdb;
2885  if ( ZConfig::instance().systemRoot() == Pathname() )
2886  {
2887  try
2888  {
2889  tmprpmdb.reset( new rpm::RpmDb );
2890  tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2891  }
2892  catch( ... )
2893  {
2894  return "";
2895  }
2896  }
2899  distributionVersion = it->tag_version();
2900  }
2901  return distributionVersion;
2902  }
2903 
2904 
2906  {
2907  return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2908  }
2909  // static version:
2910  std::string TargetImpl::distributionFlavor( const Pathname & root_r )
2911  {
2912  return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
2913  }
2914 
2916  namespace
2917  {
2918  std::string guessAnonymousUniqueId( const Pathname & root_r )
2919  {
2920  // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
2921  std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
2922  if ( ret.empty() && root_r != "/" )
2923  {
2924  // if it has nonoe, use the outer systems one
2925  ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
2926  }
2927  return ret;
2928  }
2929  }
2930 
2931  std::string TargetImpl::anonymousUniqueId() const
2932  {
2933  return guessAnonymousUniqueId( root() );
2934  }
2935  // static version:
2936  std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
2937  {
2938  return guessAnonymousUniqueId( staticGuessRoot(root_r) );
2939  }
2940 
2942 
2943  void TargetImpl::vendorAttr( VendorAttr vendorAttr_r )
2944  {
2945  MIL << "New VendorAttr: " << vendorAttr_r << endl;
2946  _vendorAttr = std::move(vendorAttr_r);
2947  }
2949 
2950  void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
2951  {
2952  // provide on local disk
2953  ManagedFile localfile = provideSrcPackage(srcPackage_r);
2954  // create a installation progress report proxy
2955  RpmInstallPackageReceiver progress( srcPackage_r );
2956  progress.connect(); // disconnected on destruction.
2957  // install it
2958  rpm().installPackage ( localfile );
2959  }
2960 
2961  ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
2962  {
2963  // provide on local disk
2964  repo::RepoMediaAccess access_r;
2965  repo::SrcPackageProvider prov( access_r );
2966  return prov.provideSrcPackage( srcPackage_r );
2967  }
2969  } // namespace target
2972 } // 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:1123
[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:964
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:864
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:1032
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:2819
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:2740
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:2950
std::string targetDistributionFlavor() const
This is register.flavor attribute of the installed base product.
Definition: TargetImpl.cc:2840
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:2834
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:2864
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:1041
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:640
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:682
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:279
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:2828
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:795
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:2931
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:1174
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:162
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:576
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:2798
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:92
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:218
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:2961
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:2846
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:2905
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:2735
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