libzypp  17.27.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 
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() );
1878 
1879  zpt::Commit commit;
1880  commit.set_flags( flags );
1881  commit.set_ignorearch( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
1882  commit.set_arch( ZConfig::instance().systemArchitecture().asString() );
1883  commit.set_dbpath( rpm().dbPath().asString() );
1884  commit.set_root( rpm().root().asString() );
1885 
1886  bool abort = false;
1887  std::vector<ManagedFile> locFiles;
1888 
1889  // fill the transaction
1890  for( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; stepId++ ) {
1891  auto &step = steps[stepId];
1892  PoolItem citem( step );
1893  if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
1894  if ( citem->isKind<Package>() )
1895  {
1896  // for packages this means being obsoleted (by rpm)
1897  // thius no additional action is needed.
1898  step.stepStage( sat::Transaction::STEP_DONE );
1899  continue;
1900  }
1901  }
1902 
1903  if ( citem->isKind<Package>() ) {
1904  Package::constPtr p = citem->asKind<Package>();
1905  if ( citem.status().isToBeInstalled() )
1906  {
1907  try {
1908  locFiles.push_back( packageCache_r.get( citem ) );
1909 
1910  zpt::TransactionStep tStep;
1911  tStep.set_stepid( stepId );
1912  tStep.mutable_install()->set_pathname( locFiles.back()->asString() );
1913  tStep.mutable_install()->set_multiversion( p->multiversionInstall() );
1914 
1915  *commit.mutable_steps()->Add( ) = std::move(tStep);
1916  }
1917  catch ( const AbortRequestException &e )
1918  {
1919  WAR << "commit aborted by the user" << endl;
1920  abort = true;
1921  step.stepStage( sat::Transaction::STEP_ERROR );
1922  break;
1923  }
1924  catch ( const SkipRequestException &e )
1925  {
1926  ZYPP_CAUGHT( e );
1927  WAR << "Skipping package " << p << " in commit" << endl;
1928  step.stepStage( sat::Transaction::STEP_ERROR );
1929  continue;
1930  }
1931  catch ( const Exception &e )
1932  {
1933  // bnc #395704: missing catch causes abort.
1934  // TODO see if packageCache fails to handle errors correctly.
1935  ZYPP_CAUGHT( e );
1936  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1937  step.stepStage( sat::Transaction::STEP_ERROR );
1938  continue;
1939  }
1940  } else {
1941 
1942  zpt::TransactionStep tStep;
1943  tStep.set_stepid( stepId );
1944  tStep.mutable_remove()->set_name( p->name() );
1945  tStep.mutable_remove()->set_version( p->edition().version() );
1946  tStep.mutable_remove()->set_release( p->edition().release() );
1947  tStep.mutable_remove()->set_arch( p->arch().asString() );
1948 
1949  *commit.mutable_steps()->Add() = std::move(tStep);
1950 
1951  }
1952  } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
1953  // SrcPackage is install-only
1954  SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1955 
1956  try {
1957  // provide on local disk
1958  locFiles.push_back( provideSrcPackage( p ) );
1959 
1960  zpt::TransactionStep tStep;
1961  tStep.set_stepid( stepId );
1962  tStep.mutable_install()->set_pathname( locFiles.back()->asString() );
1963  tStep.mutable_install()->set_multiversion( false );
1964  *commit.mutable_steps()->Add() = std::move(tStep);
1965 
1966  } catch ( const Exception &e ) {
1967  ZYPP_CAUGHT( e );
1968  INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1969  step.stepStage( sat::Transaction::STEP_ERROR );
1970  continue;
1971  }
1972  }
1973  }
1974 
1975  std::vector<sat::Solvable> successfullyInstalledPackages;
1976 
1977  if ( commit.steps_size() ) {
1978 
1979  // create the event loop early
1980  auto loop = zyppng::EventLoop::create();
1981 
1982  attemptToModify();
1983 
1984 
1985  // transaction related variables:
1986  //
1987  // the index of the step in the transaction list that we currenty execute.
1988  // this can be -1
1989  int currentStepId = -1;
1990 
1991  // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
1992  // the script fd, once we receive it we set this flag to true and ignore all output
1993  // that is written to the pipe ( aside from buffering it ) until we finalize the current report
1994  // and start a new one
1995  bool gotEndOfScript = false;
1996 
1997  // the possible reports we emit during the transaction
1998  std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
1999  std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2000  std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2001  std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2002  std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2003 
2004  // this will be set if we receive a transaction error description
2005  std::optional<zpt::TransactionError> transactionError;
2006 
2007  // infos about the currently executed script, empty if no script is currently executed
2008  std::string currentScriptType;
2009  std::string currentScriptPackage;
2010 
2011  // buffer to collect rpm output per report, this will be written to the log once the
2012  // report ends
2013  std::string rpmmsg;
2014 
2015  // maximum number of lines that we are buffering in rpmmsg
2016  constexpr auto MAXRPMMESSAGELINES = 10000;
2017 
2018  // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2019  unsigned lineno = 0;
2020 
2021  // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2022  auto msgSource = zyppng::AsyncDataSource::create();
2023  auto scriptSource = zyppng::AsyncDataSource::create();
2024 
2025 
2026  // helper function that sends RPM output to the currently active report, writing a warning to the log
2027  // if there is none
2028  const auto &sendRpmLineToReport = [&]( std::string line ){
2029 
2030  const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2031  callback::UserData cmdout(cType);
2032  if ( currentStepId >= 0 )
2033  cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2034  cmdout.set( "line", line );
2035  report->report(cmdout);
2036  };
2037 
2038  if ( installreport ) {
2039  sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2040  } else if ( uninstallreport ) {
2041  sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2042  } else if ( scriptreport ) {
2043  sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2044  } else if ( transactionreport ) {
2045  sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2046  } else if ( cleanupreport ) {
2047  sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2048  } else {
2049  WAR << "Got rpm output without active report " << line << std::endl;
2050  }
2051 
2052  // remember rpm output
2053  if ( lineno >= MAXRPMMESSAGELINES ) {
2054  if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2055  return;
2056  }
2057  rpmmsg += line;
2058  if ( line.back() != '\n' )
2059  line += '\n';
2060  };
2061 
2062 
2063  // callback and helper function to process data that is received on the script FD
2064  const auto &processDataFromScriptFd = [&](){
2065 
2066  while ( scriptSource->canReadLine() ) {
2067 
2068  if ( gotEndOfScript )
2069  return;
2070 
2071  std::string l = scriptSource->readLine().asString();
2072  if( str::endsWith( l, endOfScriptTag ) ) {
2073  DBG << "Received end of script tag" << std::endl;
2074  gotEndOfScript = true;
2075  l = l.substr( 0, l.size() - endOfScriptTag.size() );
2076  if ( l.size() == 0 )
2077  return;
2078  }
2079 
2080  sendRpmLineToReport( l );
2081  }
2082  };
2083  scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2084 
2085  // helper function that just waits until the end of script tag was received on the scriptSource
2086  const auto &waitForScriptEnd = [&]() {
2087 
2088  // nothing to wait for
2089  if ( gotEndOfScript )
2090  return;
2091 
2092  // we process all available data
2093  processDataFromScriptFd();
2094 
2095  // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2096  while ( scriptSource->canRead() && !gotEndOfScript ) {
2097  // readyRead will trigger processDataFromScriptFd so no need to call it again
2098  // we still got nothing, lets wait for more
2099  scriptSource->waitForReadyRead( 100 );
2100  }
2101  };
2102 
2103  const auto &aboutToStartNewReport = [&](){
2104 
2105  if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2106  ERR << "There is still a running report, this is a bug" << std::endl;
2107  assert(false);
2108  }
2109 
2110  DBG << "Starting new report, setting gotEndOfScript to false" << std::endl;
2111  gotEndOfScript = false;
2112  };
2113 
2114  const auto &writeRpmMsgToHistory = [&](){
2115  if ( rpmmsg.size() == 0 )
2116  return;
2117 
2118  if ( lineno >= MAXRPMMESSAGELINES )
2119  rpmmsg += "[truncated]\n";
2120 
2121  std::ostringstream sstr;
2122  sstr << "rpm output:" << endl << rpmmsg << endl;
2123  HistoryLog().comment(sstr.str());
2124  };
2125 
2126  // helper function that closes the current report and cleans up the ressources
2127  const auto &finalizeCurrentReport = [&]() {
2128  sat::Transaction::Step *step = nullptr;
2129  Resolvable::constPtr resObj;
2130  if ( currentStepId >= 0 ) {
2131  step = &steps.at(currentStepId);
2132  resObj = makeResObject( step->satSolvable() );
2133  }
2134 
2135  if ( installreport ) {
2136  waitForScriptEnd();
2137  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2138 
2139  HistoryLog().comment(
2140  str::form("%s install failed", step->ident().c_str()),
2141  true /*timestamp*/);
2142 
2143  writeRpmMsgToHistory();
2144 
2145  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2146  } else {
2147  ( *installreport)->progress( 100, resObj );
2148  ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2149 
2150  successfullyInstalledPackages.push_back( step->satSolvable() );
2151 
2152  PoolItem citem( *step );
2153  if ( !( flags & rpm::RPMINST_TEST ) ) {
2154  // @TODO are we really doing this just for install?
2155  if ( citem.isNeedreboot() ) {
2156  auto rebootNeededFile = root() / "/run/reboot-needed";
2157  if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2158  filesystem::touch( rebootNeededFile );
2159  }
2161  HistoryLog().install(citem);
2162  }
2163 
2164  HistoryLog().comment(
2165  str::form("%s installed ok", step->ident().c_str()),
2166  true /*timestamp*/);
2167 
2168  writeRpmMsgToHistory();
2169  }
2170  }
2171  if ( uninstallreport ) {
2172  waitForScriptEnd();
2173  if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2174 
2175  HistoryLog().comment(
2176  str::form("%s uninstall failed", step->ident().c_str()),
2177  true /*timestamp*/);
2178 
2179  writeRpmMsgToHistory();
2180 
2181  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2182  } else {
2183  ( *uninstallreport)->progress( 100, resObj );
2184  ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2185 
2186  PoolItem citem( *step );
2187  HistoryLog().remove(citem);
2188 
2189  HistoryLog().comment(
2190  str::form("%s removed ok", step->ident().c_str()),
2191  true /*timestamp*/);
2192 
2193  writeRpmMsgToHistory();
2194  }
2195  }
2196  if ( scriptreport ) {
2197  waitForScriptEnd();
2198  ( *scriptreport)->progress( 100, resObj );
2199  ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2200  }
2201  if ( transactionreport ) {
2202  waitForScriptEnd();
2203  ( *transactionreport)->progress( 100 );
2204  ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2205  }
2206  if ( cleanupreport ) {
2207  waitForScriptEnd();
2208  ( *cleanupreport)->progress( 100 );
2209  ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2210  }
2211  DBG << "Report finalized" << std::endl;
2212  currentStepId = -1;
2213  lineno = 0;
2214  rpmmsg.clear();
2215  currentScriptType.clear();
2216  currentScriptPackage.clear();
2217  installreport.reset();
2218  uninstallreport.reset();
2219  scriptreport.reset();
2220  transactionreport.reset();
2221  cleanupreport.reset();
2222  };
2223 
2224  // This sets up the process and pushes the required transactions steps to it
2225  // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2226  //
2227  // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2228  // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2229  // 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
2230 
2231  constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2232 
2233  const char *argv[] = {
2234  //"gdbserver",
2235  //"localhost:10001",
2236  zyppRpmBinary.data(),
2237  nullptr
2238  };
2239  auto prog = zyppng::Process::create();
2240 
2241  // we set up a pipe to communicate with the process, its too dangerous to use stdout since librpm
2242  // might print to it.
2243  auto messagePipe = zyppng::Pipe::create();
2244  if ( !messagePipe )
2245  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2246 
2247  // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2248  // way than a FD to redirect that output
2249  auto scriptPipe = zyppng::Pipe::create();
2250  if ( !scriptPipe )
2251  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2252 
2253  prog->addFd( messagePipe->writeFd );
2254  prog->addFd( scriptPipe->writeFd );
2255 
2256  // set up the AsyncDataSource to read script output
2257  if ( !scriptSource->open( scriptPipe->readFd ) )
2258  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2259 
2260  prog->sigStarted().connect( [&](){
2261 
2262  // close the ends of the pipes we do not care about
2263  messagePipe->unrefWrite();
2264  scriptPipe->unrefWrite();
2265 
2266  // read the stdout and forward it to our log
2267  prog->stdoutDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2268  while( prog->stdoutDevice()->canReadLine() ) {
2269  MIL << "zypp-rpm stdout: " << prog->stdoutDevice()->readLine().asStringView() << std::endl;
2270  }
2271  });
2272 
2273  // read the stderr and forward it to our log
2274  prog->stderrDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2275  while( prog->stderrDevice()->canReadLine() ) {
2276  MIL << "zypp-rpm stderr: " << prog->stderrDevice()->readLine().asStringView() << std::endl;
2277  }
2278  });
2279 
2280  {
2281  // write the commit message in blocking mode
2282  const auto outFd = prog->stdinFd();
2283  OnScopeExit unblock([&](){
2284  io::setFDBlocking( outFd, false );
2285  });
2286  io::setFDBlocking( outFd );
2287 
2288  // first we push the commit information to the process, starting with the byte size
2289  zyppng::rpc::HeaderSizeType msgSize = commit.ByteSizeLong();
2290  const auto written = zyppng::eintrSafeCall( ::write, outFd, &msgSize, sizeof(zyppng::rpc::HeaderSizeType) );
2291  if ( written != sizeof(zyppng::rpc::HeaderSizeType) ) {
2292  prog->stop( SIGKILL );
2293  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit size to subprocess" ) );
2294  }
2295 
2296  zyppng::FileOutputStream fo ( outFd );
2297  if ( !commit.SerializeToZeroCopyStream( &fo ) ) {
2298  prog->stop( SIGKILL );
2299  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2300  }
2301  fo.Flush();
2302  }
2303 
2304  });
2305 
2306  // this is the source for control messages from zypp-rpm , we will get structured data information
2307  // in form of protobuf messages
2308  if ( !msgSource->open( messagePipe->readFd ) )
2309  ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2310 
2311  size_t pendingMessageSize = 0;
2312  const auto &processMessages = [&] ( ) {
2313 
2314  // lambda function that parses the passed message type and checks if the stepId is a valid offset
2315  // in the steps list.
2316  const auto &parseMsgWithStepId = [&steps]( const auto &m, auto &p ){
2317  if ( !p.ParseFromString( m.value() ) ) {
2318  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2319  return false;
2320  }
2321 
2322  auto id = p.stepid();
2323  if ( id < 0 || id >= steps.size() ) {
2324  ERR << "Received invalid stepId: " << id << " in " << m.messagetypename() << " message from zypp-rpm, ignoring." << std::endl;
2325  return false;
2326  }
2327  return true;
2328  };
2329 
2330  while ( msgSource->bytesAvailable() ) {
2331 
2332  if ( pendingMessageSize == 0 ) {
2333  if ( msgSource->bytesAvailable() >= sizeof( zyppng::rpc::HeaderSizeType ) ) {
2334  msgSource->read( reinterpret_cast<char *>( &pendingMessageSize ), sizeof( zyppng::rpc::HeaderSizeType ) );
2335  }
2336  }
2337 
2338  if ( msgSource->bytesAvailable() < pendingMessageSize ) {
2339  return;
2340  }
2341 
2342  auto bytes = msgSource->read( pendingMessageSize );
2343  pendingMessageSize = 0;
2344 
2345  zypp::proto::Envelope m;
2346  if (! m.ParseFromArray( bytes.data(), bytes.size() ) ) {
2347  // 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
2348  // continue ( this should normally not happen , but code needs to handle it ).
2349  ERR << "Received misformed message from zypp-rpm, ignoring" << std::endl;
2350  return;
2351  }
2352 
2353  // due to librpm behaviour we need to make sense of the order of messages we receive
2354  // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2355  // Script related messages. What we do is remember the current step we are in and only close
2356  // the step when we get the start of the next one
2357  const auto &mName = m.messagetypename();
2358  if ( mName == "zypp.proto.target.RpmLog" ) {
2359 
2360  zpt::RpmLog p;
2361  if ( !p.ParseFromString( m.value() ) ) {
2362  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2363  continue;
2364  }
2365 
2366  sendRpmLineToReport( p.line() );
2367 
2368 
2369  } else if ( mName == "zypp.proto.target.PackageBegin" ) {
2370  finalizeCurrentReport();
2371 
2372  zpt::PackageBegin p;
2373  if ( !parseMsgWithStepId( m, p ) )
2374  continue;
2375 
2376  aboutToStartNewReport();
2377 
2378  auto & step = steps.at( p.stepid() );
2379  currentStepId = p.stepid();
2380  if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2381  uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2382  ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2383  } else {
2384  installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2385  ( *installreport )->start( makeResObject( step.satSolvable() ) );
2386  }
2387 
2388  } else if ( mName == "zypp.proto.target.PackageFinished" ) {
2389  zpt::PackageFinished p;
2390  if ( !parseMsgWithStepId( m, p ) )
2391  continue;
2392 
2393  if ( p.stepid() < 0 || p.stepid() > steps.size() )
2394  continue;
2395 
2396  // here we only set the step stage to done, we however need to wait for the next start in order to send
2397  // the finished report since there might be a error pending to be reported
2398  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_DONE );
2399 
2400  } else if ( mName == "zypp.proto.target.PackageProgress" ) {
2401  zpt::PackageProgress p;
2402  if ( !parseMsgWithStepId( m, p ) )
2403  continue;
2404 
2405  if ( uninstallreport )
2406  (*uninstallreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2407  else if ( installreport )
2408  (*installreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2409  else
2410  ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2411 
2412  } else if ( mName == "zypp.proto.target.PackageError" ) {
2413  zpt::PackageError p;
2414  if ( !parseMsgWithStepId( m, p ) )
2415  continue;
2416 
2417  if ( p.stepid() >= 0 && p.stepid() < steps.size() )
2418  steps[ p.stepid() ].stepStage( sat::Transaction::STEP_ERROR );
2419 
2420  finalizeCurrentReport();
2421 
2422  } else if ( mName == "zypp.proto.target.ScriptBegin" ) {
2423  finalizeCurrentReport();
2424 
2425  zpt::ScriptBegin p;
2426  if ( !p.ParseFromString( m.value() ) ) {
2427  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2428  continue;
2429  }
2430 
2431  aboutToStartNewReport();
2432 
2433  Resolvable::constPtr resPtr;
2434  const auto stepId = p.stepid();
2435  if ( stepId >= 0 && stepId < steps.size() ) {
2436  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2437  }
2438 
2439  currentStepId = p.stepid();
2440  scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2441  currentScriptType = p.scripttype();
2442  currentScriptPackage = p.scriptpackage();
2443  (*scriptreport)->start( p.scripttype(), p.scriptpackage(), resPtr );
2444 
2445  } else if ( mName == "zypp.proto.target.ScriptFinished" ) {
2446 
2447  // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2448  MIL << "Received" << mName << " from zypp-rpm" << std::endl;
2449 
2450  } else if ( mName == "zypp.proto.target.ScriptError" ) {
2451 
2452  zpt::ScriptError p;
2453  if ( !p.ParseFromString( m.value() ) ) {
2454  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2455  continue;
2456  }
2457 
2458  Resolvable::constPtr resPtr;
2459  const auto stepId = p.stepid();
2460  if ( stepId >= 0 && stepId < steps.size() ) {
2461  resPtr = makeResObject( steps.at(stepId).satSolvable() );
2462 
2463  if ( p.fatal() ) {
2464  steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2465  }
2466 
2467  }
2468 
2469  HistoryLog().comment(
2470  str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2471  true /*timestamp*/);
2472 
2473  writeRpmMsgToHistory();
2474 
2475  if ( !scriptreport ) {
2476  ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2477  continue;
2478  }
2479 
2480  // before killing the report we need to wait for the script end tag
2481  waitForScriptEnd();
2482  (*scriptreport)->finish( makeResObject( steps[stepId].satSolvable() ), p.fatal() ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2483 
2484  // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2485  scriptreport.reset();
2486  currentStepId = -1;
2487 
2488  } else if ( mName == "zypp.proto.target.CleanupBegin" ) {
2489  finalizeCurrentReport();
2490 
2491  zpt::CleanupBegin beg;
2492  if ( !beg.ParseFromString( m.value() ) ) {
2493  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2494  continue;
2495  }
2496 
2497  aboutToStartNewReport();
2498  cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2499  (*cleanupreport)->start( beg.nvra() );
2500  } else if ( mName == "zypp.proto.target.CleanupFinished" ) {
2501 
2502  finalizeCurrentReport();
2503 
2504  } else if ( mName == "zypp.proto.target.CleanupProgress" ) {
2505  zpt::CleanupProgress prog;
2506  if ( !prog.ParseFromString( m.value() ) ) {
2507  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2508  continue;
2509  }
2510 
2511  if ( !cleanupreport ) {
2512  ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2513  continue;
2514  }
2515 
2516  (*cleanupreport)->progress( prog.amount() );
2517 
2518  } else if ( mName == "zypp.proto.target.TransBegin" ) {
2519  finalizeCurrentReport();
2520 
2521  zpt::TransBegin beg;
2522  if ( !beg.ParseFromString( m.value() ) ) {
2523  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2524  continue;
2525  }
2526 
2527  aboutToStartNewReport();
2528  transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2529  (*transactionreport)->start( beg.name() );
2530  } else if ( mName == "zypp.proto.target.TransFinished" ) {
2531 
2532  finalizeCurrentReport();
2533 
2534  } else if ( mName == "zypp.proto.target.TransProgress" ) {
2535  zpt::TransProgress prog;
2536  if ( !prog.ParseFromString( m.value() ) ) {
2537  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2538  continue;
2539  }
2540 
2541  if ( !transactionreport ) {
2542  ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2543  continue;
2544  }
2545 
2546  (*transactionreport)->progress( prog.amount() );
2547  } else if ( mName == "zypp.proto.target.TransactionError" ) {
2548 
2549  zpt::TransactionError error;
2550  if ( !error.ParseFromString( m.value() ) ) {
2551  ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2552  continue;
2553  }
2554 
2555  // this value is checked later
2556  transactionError = std::move(error);
2557 
2558  } else {
2559  ERR << "Received unexpected message from zypp-rpm: "<< m.messagetypename() << ", ignoring" << std::endl;
2560  return;
2561  }
2562 
2563  }
2564  };
2565  msgSource->connectFunc( &zyppng::AsyncDataSource::sigReadyRead, processMessages );
2566 
2567  // track the childs lifetime
2568  int zyppRpmExitCode = -1;
2569  prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2570  zyppRpmExitCode = code;
2571  loop->quit();
2572  });
2573 
2574  if ( !prog->start( argv ) ) {
2575  HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2576  ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2577  }
2578 
2579  loop->run();
2580 
2581  // make sure to read ALL available messages
2582  processMessages();
2583 
2584  // we will not receive a new start message , so we need to manually finalize the last report
2585  finalizeCurrentReport();
2586 
2587  // make sure to read all data from the log source
2588  bool readMsgs = false;
2589  while( prog->stderrDevice()->canReadLine() ) {
2590  readMsgs = true;
2591  MIL << "zypp-rpm: " << prog->stderrDevice()->readLine().asStringView();
2592  }
2593  while( prog->stdoutDevice()->canReadLine() ) {
2594  readMsgs = true;
2595  MIL << "zypp-rpm: " << prog->stdoutDevice()->readLine().asStringView();
2596  }
2597 
2598  while ( scriptSource->canReadLine() ) {
2599  readMsgs = true;
2600  MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2601  }
2602  if ( scriptSource->bytesAvailable() > 0 ) {
2603  readMsgs = true;
2604  MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2605  }
2606  if ( readMsgs )
2607  MIL << std::endl;
2608 
2609  switch ( zyppRpmExitCode ) {
2610  // we need to look at the summary, handle finishedwitherrors like no error here
2611  case zypprpm::NoError:
2612  case zypprpm::RpmFinishedWithError:
2613  break;
2614  case zypprpm::RpmFinishedWithTransactionError: {
2615  // here zypp-rpm sent us a error description
2616  if ( transactionError ) {
2617 
2618  std::ostringstream sstr;
2619  sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2620  for ( const auto & err : transactionError->problems() ) {
2621  sstr << " " << err.message() << "\n";
2622  }
2623  sstr << std::endl;
2625 
2626  } else {
2627  ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more informations.") );
2628  }
2629  break;
2630  }
2631  case zypprpm::FailedToOpenDb:
2632  ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2633  break;
2634  case zypprpm::WrongHeaderSize:
2635  case zypprpm::WrongMessageFormat:
2636  ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2637  break;
2638  case zypprpm::RpmInitFailed:
2639  ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2640  break;
2641  case zypprpm::FailedToReadPackage:
2642  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more informations.") );
2643  break;
2644  case zypprpm::FailedToAddStepToTransaction:
2645  ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more informations.") );
2646  break;
2647  }
2648 
2649  for( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; stepId++ ) {
2650  auto &step = steps[stepId];
2651  PoolItem citem( step );
2652 
2653  if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2654  // other resolvables (non-Package) that are not handled by zypp-rpm
2655  if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2656  // Status is changed as the buddy package buddy
2657  // gets installed/deleted. Handle non-buddies only.
2658  if ( ! citem.buddy() && citem->isKind<Product>() ) {
2659  Product::constPtr p = citem->asKind<Product>();
2660 
2661  if ( citem.status().isToBeInstalled() ) {
2662  ERR << "Can't install orphan product without release-package! " << citem << endl;
2663  } else {
2664  // Deleting the corresponding product entry is all we con do.
2665  // So the product will no longer be visible as installed.
2666  std::string referenceFilename( p->referenceFilename() );
2667 
2668  if ( referenceFilename.empty() ) {
2669  ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2670  } else {
2671  Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2672 
2673  if ( ! rpm().hasFile( referencePath.asString() ) ) {
2674  // If it's not owned by a package, we can delete it.
2675  referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2676  if ( filesystem::unlink( referencePath ) != 0 )
2677  ERR << "Delete orphan product failed: " << referencePath << endl;
2678  } else {
2679  WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2680  }
2681  }
2682  }
2684  step.stepStage( sat::Transaction::STEP_DONE );
2685  }
2686  }
2687  }
2688  }
2689  }
2690 
2691  // Check presence of update scripts/messages. If aborting,
2692  // at least log omitted scripts.
2693  if ( ! successfullyInstalledPackages.empty() )
2694  {
2695  if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2696  successfullyInstalledPackages, abort ) )
2697  {
2698  WAR << "Commit aborted by the user" << endl;
2699  abort = true;
2700  }
2701  // send messages after scripts in case some script generates output,
2702  // that should be kept in t %ghost message file.
2703  RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2704  successfullyInstalledPackages,
2705  result_r );
2706  }
2707 
2708  // jsc#SLE-5116: Log patch status changes to history
2709  // NOTE: Should be the last action as it may need to reload
2710  // the Target in case of an incomplete transaction.
2711  logPatchStatusChanges( result_r.transaction(), *this );
2712 
2713  if ( abort ) {
2714  HistoryLog().comment( "Commit was aborted." );
2716  }
2717  }
2718 
2720 
2722  {
2723  return _rpm;
2724  }
2725 
2726  bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2727  {
2728  return _rpm.hasFile(path_str, name_str);
2729  }
2730 
2732  namespace
2733  {
2734  parser::ProductFileData baseproductdata( const Pathname & root_r )
2735  {
2737  PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2738 
2739  if ( baseproduct.isFile() )
2740  {
2741  try
2742  {
2743  ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2744  }
2745  catch ( const Exception & excpt )
2746  {
2747  ZYPP_CAUGHT( excpt );
2748  }
2749  }
2750  else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2751  {
2752  ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2753  }
2754  return ret;
2755  }
2756 
2757  inline Pathname staticGuessRoot( const Pathname & root_r )
2758  {
2759  if ( root_r.empty() )
2760  {
2761  // empty root: use existing Target or assume "/"
2762  Pathname ret ( ZConfig::instance().systemRoot() );
2763  if ( ret.empty() )
2764  return Pathname("/");
2765  return ret;
2766  }
2767  return root_r;
2768  }
2769 
2770  inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2771  {
2772  std::ifstream idfile( file_r.c_str() );
2773  for( iostr::EachLine in( idfile ); in; in.next() )
2774  {
2775  std::string line( str::trim( *in ) );
2776  if ( ! line.empty() )
2777  return line;
2778  }
2779  return std::string();
2780  }
2781  } // namespace
2783 
2785  {
2786  ResPool pool(ResPool::instance());
2787  for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2788  {
2789  Product::constPtr p = (*it)->asKind<Product>();
2790  if ( p->isTargetDistribution() )
2791  return p;
2792  }
2793  return nullptr;
2794  }
2795 
2797  {
2798  const Pathname needroot( staticGuessRoot(root_r) );
2799  const Target_constPtr target( getZYpp()->getTarget() );
2800  if ( target && target->root() == needroot )
2801  return target->requestedLocales();
2802  return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2803  }
2804 
2806  {
2807  MIL << "updateAutoInstalled if changed..." << endl;
2808  SolvIdentFile::Data newdata;
2809  for ( auto id : sat::Pool::instance().autoInstalled() )
2810  newdata.insert( IdString(id) ); // explicit ctor!
2811  _autoInstalledFile.setData( std::move(newdata) );
2812  }
2813 
2815  { return baseproductdata( _root ).registerTarget(); }
2816  // static version:
2817  std::string TargetImpl::targetDistribution( const Pathname & root_r )
2818  { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2819 
2821  { return baseproductdata( _root ).registerRelease(); }
2822  // static version:
2823  std::string TargetImpl::targetDistributionRelease( const Pathname & root_r )
2824  { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2825 
2827  { return baseproductdata( _root ).registerFlavor(); }
2828  // static version:
2829  std::string TargetImpl::targetDistributionFlavor( const Pathname & root_r )
2830  { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2831 
2833  {
2835  parser::ProductFileData pdata( baseproductdata( _root ) );
2836  ret.shortName = pdata.shortName();
2837  ret.summary = pdata.summary();
2838  return ret;
2839  }
2840  // static version:
2842  {
2844  parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2845  ret.shortName = pdata.shortName();
2846  ret.summary = pdata.summary();
2847  return ret;
2848  }
2849 
2851  {
2852  if ( _distributionVersion.empty() )
2853  {
2855  if ( !_distributionVersion.empty() )
2856  MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2857  }
2858  return _distributionVersion;
2859  }
2860  // static version
2861  std::string TargetImpl::distributionVersion( const Pathname & root_r )
2862  {
2863  std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2864  if ( distributionVersion.empty() )
2865  {
2866  // ...But the baseproduct method is not expected to work on RedHat derivatives.
2867  // On RHEL, Fedora and others the "product version" is determined by the first package
2868  // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2869  // with the $distroverpkg variable.
2870  scoped_ptr<rpm::RpmDb> tmprpmdb;
2871  if ( ZConfig::instance().systemRoot() == Pathname() )
2872  {
2873  try
2874  {
2875  tmprpmdb.reset( new rpm::RpmDb );
2876  tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2877  }
2878  catch( ... )
2879  {
2880  return "";
2881  }
2882  }
2885  distributionVersion = it->tag_version();
2886  }
2887  return distributionVersion;
2888  }
2889 
2890 
2892  {
2893  return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2894  }
2895  // static version:
2896  std::string TargetImpl::distributionFlavor( const Pathname & root_r )
2897  {
2898  return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
2899  }
2900 
2902  namespace
2903  {
2904  std::string guessAnonymousUniqueId( const Pathname & root_r )
2905  {
2906  // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
2907  std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
2908  if ( ret.empty() && root_r != "/" )
2909  {
2910  // if it has nonoe, use the outer systems one
2911  ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
2912  }
2913  return ret;
2914  }
2915  }
2916 
2917  std::string TargetImpl::anonymousUniqueId() const
2918  {
2919  return guessAnonymousUniqueId( root() );
2920  }
2921  // static version:
2922  std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
2923  {
2924  return guessAnonymousUniqueId( staticGuessRoot(root_r) );
2925  }
2926 
2928 
2929  void TargetImpl::vendorAttr( VendorAttr vendorAttr_r )
2930  {
2931  MIL << "New VendorAttr: " << vendorAttr_r << endl;
2932  _vendorAttr = std::move(vendorAttr_r);
2933  }
2935 
2936  void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
2937  {
2938  // provide on local disk
2939  ManagedFile localfile = provideSrcPackage(srcPackage_r);
2940  // create a installation progress report proxy
2941  RpmInstallPackageReceiver progress( srcPackage_r );
2942  progress.connect(); // disconnected on destruction.
2943  // install it
2944  rpm().installPackage ( localfile );
2945  }
2946 
2947  ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
2948  {
2949  // provide on local disk
2950  repo::RepoMediaAccess access_r;
2951  repo::SrcPackageProvider prov( access_r );
2952  return prov.provideSrcPackage( srcPackage_r );
2953  }
2955  } // namespace target
2958 } // namespace zypp
#define idstr(V)
const Pathname & _root
Definition: RepoManager.cc:144
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:62
Pathname _mountpoint
Definition: TargetImpl.cc:275
#define SUBST_IF(PAT, VAL)
ZYppCommitResult & _result
Definition: TargetImpl.cc:1559
TrueBool _guard
Definition: TargetImpl.cc:1558
Architecture.
Definition: Arch.h:37
const std::string & asString() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: Arch.cc:485
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:162
A sat capability.
Definition: Capability.h:60
Mime type like 'type/subtype' classification of content.
Definition: ContentType.h:30
Store and operate on date (time_t).
Definition: Date.h:33
static Date now()
Return the current time.
Definition: Date.h:78
Edition represents [epoch:]version[-release]
Definition: Edition.h:61
unsigned epoch_t
Type of an epoch.
Definition: Edition.h:64
std::string version() const
Version.
Definition: Edition.cc:94
std::string release() const
Release.
Definition: Edition.cc:110
epoch_t epoch() const
Epoch.
Definition: Edition.cc:82
Base class for Exception.
Definition: Exception.h:146
void remember(const Exception &old_r)
Store an other Exception as history.
Definition: Exception.cc:105
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
const std::string & command() const
The command we're executing.
std::vector< std::string > Arguments
int close()
Wait for the progamm to complete.
Writing the zypp history file.
Definition: HistoryLog.h:57
void stampCommand()
Log info about the current process.
Definition: HistoryLog.cc:220
static void setRoot(const Pathname &root)
Set new root directory to the default history log file path.
Definition: HistoryLog.cc:163
void remove(const PoolItem &pi)
Log removal of a package.
Definition: HistoryLog.cc:260
static const Pathname & fname()
Get the current log file path.
Definition: HistoryLog.cc:179
void install(const PoolItem &pi)
Log installation (or update) of a package.
Definition: HistoryLog.cc:232
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Definition: HistoryLog.cc:188
Access to the sat-pools string space.
Definition: IdString.h:43
const char * c_str() const
Conversion to const char *
Definition: IdString.cc:50
std::string asString() const
Conversion to std::string
Definition: IdString.h:98
@ REGEX
Regular Expression.
Definition: StrMatcher.h:48
Package interface.
Definition: Package.h:33
TraitsType::constPtrType constPtr
Definition: Package.h:38
Class representing a patch.
Definition: Patch.h:38
TraitsType::constPtrType constPtr
Definition: Patch.h:43
Parallel execution of stateful PluginScripts.
void load(const Pathname &path_r)
Find and launch plugins sending PLUGINBEGIN.
void send(const PluginFrame &frame_r)
Send PluginFrame to all open plugins.
Command frame for communication with PluginScript.
Definition: PluginFrame.h:41
Combining sat::Solvable and ResStatus.
Definition: PoolItem.h:51
ResObject::constPtr resolvable() const
Returns the ResObject::constPtr.
Definition: PoolItem.cc:218
ResStatus & status() const
Returns the current status.
Definition: PoolItem.cc:204
sat::Solvable buddy() const
Return the buddy we share our status object with.
Definition: PoolItem.cc:206
Product interface.
Definition: Product.h:33
TraitsType::constPtrType constPtr
Definition: Product.h:38
Track changing files or directories.
Definition: RepoStatus.h:41
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
Definition: RepoStatus.cc:212
bool solvablesEmpty() const
Whether Repository contains solvables.
Definition: Repository.cc:219
SolvableIterator solvablesEnd() const
Iterator behind the last Solvable.
Definition: Repository.cc:241
SolvableIterator solvablesBegin() const
Iterator to the first Solvable.
Definition: Repository.cc:231
size_type solvablesSize() const
Number of solvables in Repository.
Definition: Repository.cc:225
void addSolv(const Pathname &file_r)
Load Solvables from a solv-file.
Definition: Repository.cc:320
void eraseFromPool()
Remove this Repository from it's Pool.
Definition: Repository.cc:297
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from the established one.
Definition: PoolImpl.cc:26
Global ResObject pool.
Definition: ResPool.h:61
static ResPool instance()
Singleton ctor.
Definition: ResPool.cc:37
EstablishedStates::ChangedPseudoInstalled ChangedPseudoInstalled
Map holding pseudo installed items where current and established status differ.
Definition: ResPool.h:341
void setHardLockQueries(const HardLockQueries &newLocks_r)
Set a new set of queries.
Definition: ResPool.cc:103
Resolver & resolver() const
The Resolver.
Definition: ResPool.cc:61
const LocaleSet & getRequestedLocales() const
Return the requested locales.
Definition: ResPool.cc:130
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from their initial one.
Definition: ResPool.h:349
byKind_iterator byKindEnd(const ResKind &kind_r) const
Definition: ResPool.h:268
EstablishedStates establishedStates() const
Factory for EstablishedStates.
Definition: ResPool.cc:76
byKind_iterator byKindBegin(const ResKind &kind_r) const
Definition: ResPool.h:261
void getHardLockQueries(HardLockQueries &activeLocks_r)
Suggest a new set of queries based on the current selection.
Definition: ResPool.cc:106
bool isToBeInstalled() const
Definition: ResStatus.h:253
bool resetTransact(TransactByValue causer_r)
Not the same as setTransact( false ).
Definition: ResStatus.h:485
TraitsType::constPtrType constPtr
Definition: Resolvable.h:59
sat::Transaction getTransaction()
Return the Transaction computed by the last solver run.
Definition: Resolver.cc:74
bool upgradeMode() const
Definition: Resolver.cc:97
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition: Resolver.cc:136
Attempts to create a lock to prevent the system from going into hibernate/shutdown.
SrcPackage interface.
Definition: SrcPackage.h:30
TraitsType::constPtrType constPtr
Definition: SrcPackage.h:36
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition: StrMatcher.h:298
Definition of vendor equivalence.
Definition: VendorAttr.h:61
Interim helper class to collect global options and settings.
Definition: ZConfig.h:62
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:126
std::string distroverpkg() const
Package telling the "product version" on systems not using /etc/product.d/baseproduct.
Definition: ZConfig.cc:1194
Options and policies for ZYpp::commit.
ZYppCommitPolicy & rpmInstFlags(target::rpm::RpmInstFlags newFlags_r)
The default target::rpm::RpmInstFlags.
bool singleTransModeEnabled() const
ZYppCommitPolicy & rpmExcludeDocs(bool yesNo_r)
Use rpm option –excludedocs (default: false)
ZYppCommitPolicy & dryRun(bool yesNo_r)
Set dry run (default: false).
ZYppCommitPolicy & restrictToMedia(unsigned mediaNr_r)
Restrict commit to media 1.
ZYppCommitPolicy & allMedia()
Process all media (default)
ZYppCommitPolicy & downloadMode(DownloadMode val_r)
Commit download policy to use.
ZYppCommitPolicy & rpmNoSignature(bool yesNo_r)
Use rpm option –nosignature (default: false)
Result returned from ZYpp::commit.
TransactionStepList & rTransactionStepList()
Manipulate transactionStepList.
void setSingleTransactionMode(bool yesno_r)
std::vector< sat::Transaction::Step > TransactionStepList
const sat::Transaction & transaction() const
The full transaction list.
sat::Transaction & rTransaction()
Manipulate transaction.
Typesafe passing of user data via callbacks.
Definition: UserData.h:39
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition: UserData.h:118
std::string receiveLine()
Read one line from the input stream.
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:219
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:279
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
const std::string & asString() const
String representation.
Definition: Pathname.h:91
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
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
const char * c_str() const
String representation.
Definition: Pathname.h:110
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:128
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:218
Pathname path() const
Definition: TmpPath.cc:146
Data returned by ProductFileReader.
bool empty() const
Whether this is an empty object without valid data.
static ProductFileData scanFile(const Pathname &file_r)
Parse one file (or symlink) and return the ProductFileData parsed.
Provides files from different repos.
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r) const
Provide SrcPackage in a local file.
Global sat-pool.
Definition: Pool.h:47
void setAutoInstalled(const Queue &autoInstalled_r)
Set ident list of all autoinstalled solvables.
Definition: Pool.cc:265
Pathname rootDir() const
Get rootdir (for file conflicts check)
Definition: Pool.cc:64
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
static const std::string & systemRepoAlias()
Reserved system repository alias @System .
Definition: Pool.cc:46
void setNeedrebootSpec(sat::SolvableSpec needrebootSpec_r)
Solvables which should trigger the reboot-needed hint if installed/updated.
Definition: Pool.cc:267
Repository systemRepo()
Return the system repository, create it if missing.
Definition: Pool.cc:178
void initRequestedLocales(const LocaleSet &locales_r)
Start tracking changes based on this locales_r.
Definition: Pool.cc:251
Libsolv Id queue wrapper.
Definition: Queue.h:35
detail::IdType value_type
Definition: Queue.h:38
void push(value_type val_r)
Push a value to the end off the Queue.
Definition: Queue.cc:103
Define a set of Solvables by ident and provides.
Definition: SolvableSpec.h:45
void addProvides(Capability provides_r)
A all sat::Solvable matching this provides_r.
void addIdent(IdString ident_r)
Add all sat::Solvable with this ident_r.
void parseFrom(const InputStream &istr_r)
Parse file istr_r and add it's specs (one per line, #-comments).
A Solvable object within the sat Pool.
Definition: Solvable.h:54
A single step within a Transaction.
Definition: Transaction.h:219
StepType stepType() const
Type of action to perform in this step.
Definition: Transaction.cc:388
StepStage stepStage() const
Step action result.
Definition: Transaction.cc:391
Solvable satSolvable() const
Return the corresponding Solvable.
Definition: Transaction.h:243
Libsolv transaction wrapper.
Definition: Transaction.h:52
const_iterator end() const
Iterator behind the last TransactionStep.
Definition: Transaction.cc:343
StringQueue autoInstalled() const
Return the ident strings of all packages that would be auto-installed after the transaction is run.
Definition: Transaction.cc:358
const_iterator begin() const
Iterator to the first TransactionStep.
Definition: Transaction.cc:337
bool order()
Order transaction steps for commit.
Definition: Transaction.cc:328
@ TRANSACTION_MULTIINSTALL
[M] Install(multiversion) item (
Definition: Transaction.h:67
@ TRANSACTION_INSTALL
[+] Install(update) item
Definition: Transaction.h:66
@ TRANSACTION_IGNORE
[ ] Nothing (includes implicit deletes due to obsoletes and non-package actions)
Definition: Transaction.h:64
@ TRANSACTION_ERASE
[-] Delete item
Definition: Transaction.h:65
@ STEP_DONE
[OK] success
Definition: Transaction.h:74
@ STEP_TODO
[__] unprocessed
Definition: Transaction.h:73
@ STEP_ERROR
[**] error
Definition: Transaction.h:75
Target::commit helper optimizing package provision.
void setCommitList(std::vector< sat::Solvable > commitList_r)
Download(commit) sequence of solvables to compute read ahead.
bool preloaded() const
Whether preloaded hint is set.
ManagedFile get(const PoolItem &citem_r)
Provide a package.
void setData(const Data &data_r)
Store new Data.
Definition: HardLocksFile.h:73
const Data & data() const
Return the data.
Definition: HardLocksFile.h:57
pool::PoolTraits::HardLockQueries Data
Definition: HardLocksFile.h:41
Save and restore locale set from file.
void setLocales(const LocaleSet &locales_r)
Store a new locale set.
const LocaleSet & locales() const
Return the loacale set.
void tryLevel(target::rpm::InstallResolvableReport::RpmLevel level_r)
Extract and remember posttrans scripts for later execution.
bool collectScriptFromPackage(ManagedFile rpmPackage_r)
Extract and remember a packages posttrans script for later execution.
bool executeScripts()
Execute the remembered scripts.
void discardScripts()
Discard all remembered scrips.
bool aborted() const
Returns true if removing is aborted during progress.
const Pathname & file() const
Return the file path.
Definition: SolvIdentFile.h:46
std::unordered_set< IdString > Data
Definition: SolvIdentFile.h:37
const Data & data() const
Return the data.
Definition: SolvIdentFile.h:53
void setData(const Data &data_r)
Store new Data.
Definition: SolvIdentFile.h:69
Base class for concrete Target implementations.
Definition: TargetImpl.h:54
std::string targetDistributionRelease() const
This is register.release attribute of the installed base product.
Definition: TargetImpl.cc:2820
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: TargetImpl.cc:2814
LocaleSet requestedLocales() const
Languages to be supported by the system.
Definition: TargetImpl.h:157
void updateAutoInstalled()
Update the database of autoinstalled packages.
Definition: TargetImpl.cc:2805
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Provides a source package on the Target.
Definition: TargetImpl.cc:2947
Pathname _root
Path to the target.
Definition: TargetImpl.h:224
RequestedLocalesFile _requestedLocalesFile
Requested Locales database.
Definition: TargetImpl.h:228
void createLastDistributionFlavorCache() const
generates a cache of the last product flavor
Definition: TargetImpl.cc:907
std::string _distributionVersion
Cache distributionVersion.
Definition: TargetImpl.h:234
std::list< PoolItem > PoolItemList
list of pool items
Definition: TargetImpl.h:59
rpm::RpmDb _rpm
RPM database.
Definition: TargetImpl.h:226
rpm::RpmDb & rpm()
The RPM database.
Definition: TargetImpl.cc:2721
Pathname solvfilesPath() const
The solv file location actually in use (default or temp).
Definition: TargetImpl.h:92
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: TargetImpl.cc:2850
const VendorAttr & vendorAttr() const
The targets current vendor equivalence settings.
Definition: TargetImpl.h:201
void createAnonymousId() const
generates the unique anonymous id which is called when creating the target
Definition: TargetImpl.cc:885
SolvIdentFile _autoInstalledFile
user/auto installed database
Definition: TargetImpl.h:230
Product::constPtr baseProduct() const
returns the target base installed product, also known as the distribution or platform.
Definition: TargetImpl.cc:2784
Target::DistributionLabel distributionLabel() const
This is shortName and summary attribute of the installed base product.
Definition: TargetImpl.cc:2832
virtual ~TargetImpl()
Dtor.
Definition: TargetImpl.cc:943
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:2726
HardLocksFile _hardLocksFile
Hard-Locks database.
Definition: TargetImpl.h:232
Pathname root() const
The root set for this target.
Definition: TargetImpl.h:116
void load(bool force=true)
Definition: TargetImpl.cc:1124
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:2891
void commitInSingleTransaction(const ZYppCommitPolicy &policy_r, CommitPackageCache &packageCache_r, ZYppCommitResult &result_r)
Commit ordered changes (internal helper)
Definition: TargetImpl.cc:1859
void installSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Install a source package on the Target.
Definition: TargetImpl.cc:2936
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
Pathname home() const
The directory to store things.
Definition: TargetImpl.h:120
void commitFindFileConflicts(const ZYppCommitPolicy &policy_r, ZYppCommitResult &result_r)
Commit helper checking for file conflicts after download.
Pathname defaultSolvfilesPath() const
The systems default solv file location.
Definition: TargetImpl.cc:956
std::string anonymousUniqueId() const
anonymous unique id
Definition: TargetImpl.cc:2917
TargetImpl(const Pathname &root_r="/", bool doRebuild_r=false)
Ctor.
Definition: TargetImpl.cc:815
bool solvfilesPathIsTemp() const
Whether we're using a temp.
Definition: TargetImpl.h:96
std::string targetDistributionFlavor() const
This is register.flavor attribute of the installed base product.
Definition: TargetImpl.cc:2826
Interface to the rpm program.
Definition: RpmDb.h:48
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1559
void initDatabase(Pathname root_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database below root_r.
Definition: RpmDb.cc:265
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition: RpmDb.cc:1753
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition: RpmDb.cc:355
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:966
Subclass to retrieve database content.
Definition: librpmDb.h:336
bool findByProvides(const std::string &tag_r)
Reset to iterate all packages that provide a certain tag.
Definition: librpmDb.cc:743
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
int chmod(const Pathname &path, mode_t mode)
Like 'chmod'.
Definition: PathInfo.cc:1032
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:26
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
int unlink(const Pathname &path)
Like 'unlink'.
Definition: PathInfo.cc:640
int rename(const Pathname &oldpath, const Pathname &newpath)
Like 'rename'.
Definition: PathInfo.cc:682
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition: PathInfo.cc:1123
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition: PathInfo.cc:412
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition: PathInfo.cc:1041
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like 'readlink'.
Definition: PathInfo.cc:864
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition: PathInfo.cc:319
int touch(const Pathname &path)
Change file's modification and access times.
Definition: PathInfo.cc:1174
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition: PathInfo.cc:964
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like 'symlink'.
Definition: PathInfo.cc:795
BlockingMode setFDBlocking(int fd, bool mode)
Definition: IOTools.cc:31
std::string getline(std::istream &str)
Read one line from stream.
Definition: IOStream.cc:33
std::string toJSON(void)
Definition: Json.h:136
SolvableIdType size_type
Definition: PoolMember.h:126
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1027
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition: String.h:1092
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition: String.cc:177
unsigned splitEscaped(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", bool withEmpty=false)
Split line_r into words with respect to escape delimeters.
Definition: String.h:595
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
IMPL_PTR_TYPE(TargetImpl)
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 rpmDbStateHash(const Pathname &root_r)
Definition: TargetImpl.cc:93
void writeUpgradeTestcase()
Definition: TargetImpl.cc:354
static bool fileMissing(const Pathname &pathname)
helper functor
Definition: TargetImpl.cc:880
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
RepoStatus rpmDbRepoStatus(const Pathname &root_r)
Definition: TargetImpl.cc:111
static std::string generateRandomId()
generates a random id using uuidgen
Definition: TargetImpl.cc:834
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
std::string asString(const DefaultIntegral< Tp, TInitial > &obj)
std::unordered_set< Locale > LocaleSet
Definition: Locale.h:27
ResObject::Ptr makeResObject(const sat::Solvable &solvable_r)
Create ResObject from sat::Solvable.
Definition: ResObject.cc:43
std::list< UpdateNotificationFile > UpdateNotifications
@ DownloadInHeaps
Similar to DownloadInAdvance, but try to split the transaction into heaps, where at the end of each h...
Definition: DownloadMode.h:29
@ DownloadOnly
Just download all packages to the local cache.
Definition: DownloadMode.h:25
@ DownloadAsNeeded
Alternating download and install.
Definition: DownloadMode.h:32
@ DownloadInAdvance
First download all packages to the local cache.
Definition: DownloadMode.h:27
@ DownloadDefault
libzypp will decide what to do.
Definition: DownloadMode.h:24
JSON array.
Definition: Json.h:257
std::string asJSON() const
JSON representation.
Definition: Json.h:279
void add(const Value &val_r)
Push JSON Value to Array.
Definition: Json.h:271
JSON object.
Definition: Json.h:322
void add(const String &key_r, const Value &val_r)
Add key/value pair.
Definition: Json.h:336
std::string asJSON() const
JSON representation.
Definition: Json.h:344
bool isKind(const ResKind &kind_r) const
Definition: SolvableType.h:64
Solvable satSolvable() const
Return the corresponding sat::Solvable.
Definition: SolvableType.h:57
bool isNeedreboot() const
Definition: SolvableType.h:83
static PoolImpl & myPool()
Definition: PoolImpl.cc:178
static const UserData::ContentType contentRpmout
"zypp-rpm/cleanupkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/scriptsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/installpkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/removepkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/transactionsa": Additional rpm output (sent immediately).
#define NON_COPYABLE(CLASS)
Delete copy ctor and copy assign.
Definition: Easy.h:59
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
#define NON_MOVABLE(CLASS)
Delete move ctor and move assign.
Definition: Easy.h:69
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:396
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
#define _(MSG)
Definition: Gettext.h:37
#define DBG
Definition: Logger.h:95
#define MIL
Definition: Logger.h:96
#define ERR
Definition: Logger.h:98
#define WAR
Definition: Logger.h:97
#define INT
Definition: Logger.h:100