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