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