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