libzypp  17.38.7
RpmDb.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include "librpm.h"
13 extern "C"
14 {
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
17 }
18 #include <cstdlib>
19 #include <cstdio>
20 #include <ctime>
21 
22 #include <iostream>
23 #include <fstream>
24 #include <sstream>
25 #include <list>
26 #include <map>
27 #include <set>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 #include <algorithm>
32 
33 #include <zypp-core/base/StringV.h>
34 #include <zypp-core/base/Logger.h>
35 #include <zypp-core/base/String.h>
36 #include <zypp-core/base/Gettext.h>
37 #include <zypp/base/LocaleGuard.h>
38 #include <zypp-core/base/DtorReset>
39 
40 #include <zypp-core/Date.h>
41 #include <zypp-core/Pathname.h>
42 #include <zypp/PathInfo.h>
43 #include <zypp-common/PublicKey.h>
44 #include <zypp-core/ui/ProgressData>
45 
46 #include <zypp/target/rpm/RpmDb.h>
49 
50 #include <zypp/HistoryLog.h>
53 #include <zypp/TmpPath.h>
54 #include <zypp/KeyRing.h>
55 #include <zypp-common/KeyManager.h>
56 #include <zypp/ZYppFactory.h>
57 #include <zypp/ZConfig.h>
58 #include <zypp-core/base/IOTools.h>
59 
60 using std::endl;
61 using namespace zypp::filesystem;
62 
63 #define WARNINGMAILPATH "/var/log/YaST2/"
64 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
65 #define MAXRPMMESSAGELINES 10000
66 
67 #define WORKAROUNDRPMPWDBUG
68 
69 #undef ZYPP_BASE_LOGGER_LOGGROUP
70 #define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
71 
72 namespace zypp
73 {
74  namespace zypp_readonly_hack
75  {
76  bool IGotIt(); // in readonly-mode
77  }
78  namespace env
79  {
80  inline bool ZYPP_RPM_DEBUG()
81  {
82  static bool val = [](){
83  const char * env = getenv("ZYPP_RPM_DEBUG");
84  return( env && str::strToBool( env, true ) );
85  }();
86  return val;
87  }
88  } // namespace env
89 namespace target
90 {
91 namespace rpm
92 {
93 namespace
94 {
95 const char* quoteInFilename_m = "\'\"";
96 inline std::string rpmQuoteFilename( const Pathname & path_r )
97 {
98  std::string path( path_r.asString() );
99  for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
100  pos != std::string::npos;
101  pos = path.find_first_of( quoteInFilename_m, pos ) )
102  {
103  path.insert( pos, "\\" );
104  pos += 2; // skip '\\' and the quoted char.
105  }
106  return path;
107 }
108 
109 
114  inline Pathname workaroundRpmPwdBug( Pathname path_r )
115  {
116 #if defined(WORKAROUNDRPMPWDBUG)
117  if ( path_r.relative() )
118  {
119  // try to prepend cwd
120  AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
121  if ( cwd )
122  return Pathname( cwd ) / path_r;
123  WAR << "Can't get cwd!" << endl;
124  }
125 #endif
126  return path_r; // no problem with absolute pathnames
127  }
128 
133  inline bool workaroundDUMPPOSTTRANS_BUG_1216091( bool checkit_r=false )
134  {
135  auto checkit = []()->bool {
136  bool broken = false;
137  librpmDb::db_const_iterator it( "/" );
138  if ( it.findPackage( "rpm" )
139  && Edition::match( it->tag_edition(), "4.18.0" ) == 0
140  && not it->tag_provides().count( Capability("rpm_fixed_runposttrans") ) ) {
141  WAR << "Workaround broken rpm --runposttrans" << endl;
142  broken = true;
143  }
144  return broken;
145  };
146 
147  static bool broken = false;
148  if ( checkit_r )
149  broken = checkit();
150  return broken;
151  }
152 }
153 
155 {
156  KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb) { connect(); }
159  KeyRingSignalReceiver &operator=(const KeyRingSignalReceiver &) = delete;
160  KeyRingSignalReceiver &operator=(KeyRingSignalReceiver &&) = delete;
161 
163  {
164  disconnect();
165  }
166 
167  void trustedKeyAdded( const PublicKey &key ) override
168  {
169  MIL << "trusted key added to zypp Keyring. Importing..." << endl;
170  _rpmdb.importPubkey( key );
171  }
172 
173  void trustedKeyRemoved( const PublicKey &key ) override
174  {
175  MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
176  _rpmdb.removePubkey( key );
177  }
178 
180 };
181 
182 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
183 
184 unsigned diffFiles(const std::string& file1, const std::string& file2, std::string& out, int maxlines)
185 {
186  const char* argv[] =
187  {
188  "diff",
189  "-u",
190  file1.c_str(),
191  file2.c_str(),
192  NULL
193  };
194  ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
195 
196  //if(!prog)
197  //return 2;
198 
199  std::string line;
200  int count = 0;
201  for (line = prog.receiveLine(), count=0;
202  !line.empty();
203  line = prog.receiveLine(), count++ )
204  {
205  if (maxlines<0?true:count<maxlines)
206  out+=line;
207  }
208 
209  return prog.close();
210 }
211 
213 //
214 // CLASS NAME : RpmDb
215 //
217 
218 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
219 
221 
223 //
224 //
225 // METHOD NAME : RpmDb::RpmDb
226 // METHOD TYPE : Constructor
227 //
228 RpmDb::RpmDb()
229  : _backuppath ("/var/adm/backup")
230  , _packagebackups(false)
231 {
232  process = 0;
233  exit_code = -1;
235  // Some rpm versions are patched not to abort installation if
236  // symlink creation failed.
237  setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
238  sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
239 }
240 
242 //
243 //
244 // METHOD NAME : RpmDb::~RpmDb
245 // METHOD TYPE : Destructor
246 //
248 {
249  MIL << "~RpmDb()" << endl;
250  closeDatabase();
251  delete process;
252  MIL << "~RpmDb() end" << endl;
253  sKeyRingReceiver.reset();
254 }
255 
257 //
258 //
259 // METHOD NAME : RpmDb::dumpOn
260 // METHOD TYPE : std::ostream &
261 //
262 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
263 {
264  return str << "RpmDb[" << dumpPath( _root, _dbPath ) << "]";
265 }
266 
269 {
270  if ( initialized() )
271  return db_const_iterator( root(), dbPath() );
272  return db_const_iterator();
273 }
274 
276 //
277 //
278 // METHOD NAME : RpmDb::initDatabase
279 // METHOD TYPE : PMError
280 //
281 void RpmDb::initDatabase( Pathname root_r, bool doRebuild_r )
282 {
283  workaroundDUMPPOSTTRANS_BUG_1216091( /*checkit_r*/true );
285  // Check arguments
287  if ( root_r.empty() )
288  root_r = "/";
289 
290  const Pathname & dbPath_r { librpmDb::suggestedDbPath( root_r ) }; // also asserts root_r is absolute
291 
292  // The rpmdb compat symlink.
293  // Required at least until rpmdb2solv takes a dppath argument.
294  // Otherwise it creates a db at "/var/lib/rpm".
295  if ( dbPath_r != "/var/lib/rpm" && ! PathInfo( root_r/"/var/lib/rpm" ).isExist() )
296  {
297  WAR << "Inject missing /var/lib/rpm compat symlink to " << dbPath_r << endl;
298  filesystem::assert_dir( root_r/"/var/lib" );
299  filesystem::symlink( "../../"/dbPath_r, root_r/"/var/lib/rpm" );
300  }
301 
303  // Check whether already initialized
305  if ( initialized() )
306  {
307  // Just check for a changing root because the librpmDb::suggestedDbPath
308  // may indeed change: rpm %post moving the db from /var/lib/rpm
309  // to /usr/lib/sysimage/rpm. We continue to use the old dbpath
310  // (via the compat symlink) until a re-init.
311  if ( root_r == _root ) {
312  MIL << "Calling initDatabase: already initialized at " << dumpPath( _root, _dbPath ) << endl;
313  return;
314  }
315  else
316  ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
317  }
318 
319  MIL << "Calling initDatabase: " << dumpPath( root_r, dbPath_r )
320  << ( doRebuild_r ? " (rebuilddb)" : "" ) << endl;
321 
323  // init database
325  // creates dbdir and empty rpm database if not present
326  // or throws RpmException
327  librpmDb::dbOpenCreate( root_r, dbPath_r );
328  _root = root_r;
329  _dbPath = dbPath_r;
330 
331  if ( doRebuild_r )
332  rebuildDatabase();
333 
334  MIL << "Synchronizing keys with zypp keyring" << endl;
335  syncTrustedKeys();
336 
337 #if 0 // if this is needed we need to forcefully close the db of running db_const_iterators
338  // Close the database in case any write acces (create/convert)
339  // happened during init. This should drop any lock acquired
340  // by librpm. On demand it will be reopened readonly and should
341  // not hold any lock.
342  librpmDb::dbRelease( true );
343 #endif
344  MIL << "InitDatabase: " << *this << endl;
345 }
346 
348 //
349 //
350 // METHOD NAME : RpmDb::closeDatabase
351 // METHOD TYPE : PMError
352 //
354 {
355  if ( ! initialized() )
356  {
357  return;
358  }
359 
360  // NOTE: There are no persistent librpmDb handles to invalidate.
361  // Running db_const_iterator may keep the DB physically open until they
362  // go out of scope too.
363  MIL << "closeDatabase: " << *this << endl;
364  _root = _dbPath = Pathname();
365 }
366 
368 //
369 //
370 // METHOD NAME : RpmDb::rebuildDatabase
371 // METHOD TYPE : PMError
372 //
374 {
376 
377  report->start( root() + dbPath() );
378 
379  try
380  {
381  doRebuildDatabase(report);
382  }
383  catch (RpmException & excpt_r)
384  {
385  report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
386  ZYPP_RETHROW(excpt_r);
387  }
388  report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
389 }
390 
392 {
394  MIL << "RpmDb::rebuildDatabase" << *this << endl;
395 
396  const Pathname mydbpath { root()/dbPath() }; // the configured path used in reports
397  {
398  // For --rebuilddb take care we're using the real db directory
399  // and not a symlink. Otherwise rpm will rename the symlink and
400  // replace it with a real directory containing the converted db.
401  DtorReset guardRoot { _root };
402  DtorReset guardDbPath{ _dbPath };
403  _root = "/";
404  _dbPath = filesystem::expandlink( mydbpath );
405 
406  // run rpm
407  RpmArgVec opts;
408  opts.push_back("--rebuilddb");
409  opts.push_back("-vv");
411  }
412 
413  // generate and report progress
414  ProgressData tics;
415  {
416  ProgressData::value_type hdrTotal = 0;
417  for ( auto it = dbConstIterator(); *it; ++it, ++hdrTotal )
418  {;}
419  tics.range( hdrTotal );
420  }
421  tics.sendTo( [&report,&mydbpath]( const ProgressData & tics_r ) -> bool {
422  return report->progress( tics_r.reportValue(), mydbpath );
423  } );
424  tics.toMin();
425 
426  std::string line;
427  std::string errmsg;
428  while ( systemReadLine( line ) )
429  {
430  static const std::string debugPrefix { "D:" };
431  static const std::string progressPrefix { "D: read h#" };
432  static const std::string ignoreSuffix { "digest: OK" };
433 
434  if ( ! str::startsWith( line, debugPrefix ) )
435  {
436  if ( ! str::endsWith( line, ignoreSuffix ) )
437  {
438  errmsg += line;
439  errmsg += '\n';
440  WAR << line << endl;
441  }
442  }
443  else if ( str::startsWith( line, progressPrefix ) )
444  {
445  if ( ! tics.incr() )
446  {
447  WAR << "User requested abort." << endl;
448  systemKill();
449  }
450  }
451  }
452 
453  if ( systemStatus() != 0 )
454  {
455  //TranslatorExplanation after semicolon is error message
456  ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
457  }
458  else
459  {
460  tics.toMax();
461  }
462 }
463 
465 namespace
466 {
471  void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
472  {
474  // Remember latest release and where it occurred
475  struct Key
476  {
477  Key()
478  : _inRpmKeys( nullptr )
479  , _inZyppKeys( nullptr )
480  {}
481 
482  void updateIf( const Edition & rpmKey_r )
483  {
484  std::string keyRelease( rpmKey_r.release() );
485  int comp = _release.compare( keyRelease );
486  if ( comp < 0 )
487  {
488  // update to newer release
489  _release.swap( keyRelease );
490  _inRpmKeys = &rpmKey_r;
491  _inZyppKeys = nullptr;
492  if ( !keyRelease.empty() )
493  DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
494  }
495  else if ( comp == 0 )
496  {
497  // stay with this release
498  if ( ! _inRpmKeys )
499  _inRpmKeys = &rpmKey_r;
500  }
501  // else: this is an old release
502  else
503  DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
504  }
505 
506  void updateIf( const PublicKeyData & zyppKey_r )
507  {
508  std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
509  int comp = _release.compare( keyRelease );
510  if ( comp < 0 )
511  {
512  // update to newer release
513  _release.swap( keyRelease );
514  _inRpmKeys = nullptr;
515  _inZyppKeys = &zyppKey_r;
516  if ( !keyRelease.empty() )
517  DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
518  }
519  else if ( comp == 0 )
520  {
521  // stay with this release
522  if ( ! _inZyppKeys )
523  _inZyppKeys = &zyppKey_r;
524  }
525  // else: this is an old release
526  else
527  DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
528  }
529 
530  std::string _release;
531  const Edition * _inRpmKeys;
532  const PublicKeyData * _inZyppKeys;
533  };
535 
536  // collect keys by ID(version) and latest creation(release)
537  std::map<std::string,Key> _keymap;
538 
539  for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
540  {
541  _keymap[(*it).version()].updateIf( *it );
542  }
543 
544  for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
545  {
546  _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
547  }
548 
549  // compute missing keys
550  std::set<Edition> rpmKeys;
551  std::list<PublicKeyData> zyppKeys;
552  for_( it, _keymap.begin(), _keymap.end() )
553  {
554  DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
555  << ( (*it).second._inRpmKeys ? "R" : "_" )
556  << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
557  if ( ! (*it).second._inRpmKeys )
558  {
559  zyppKeys.push_back( *(*it).second._inZyppKeys );
560  }
561  if ( ! (*it).second._inZyppKeys )
562  {
563  rpmKeys.insert( *(*it).second._inRpmKeys );
564  }
565  }
566  rpmKeys_r.swap( rpmKeys );
567  zyppKeys_r.swap( zyppKeys );
568  }
569 } // namespace
571 
573 {
574  MIL << "Going to sync trusted keys..." << endl;
575  std::set<Edition> rpmKeys( pubkeyEditions() );
576  std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
577 
578  if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
579  {
580  // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
581  // when re-acquiring the zyppp lock. For now we remove all excess keys.
582  // TODO: Once we can safely assume that all PK versions are updated we
583  // can think about re-importing newer key versions found in the zypp keyring and
584  // removing only excess ones (but case is not very likely). Unfixed PK versions
585  // however will remove the newer version found in the zypp keyring and by doing
586  // this, the key here will be removed via callback as well (keys are deleted
587  // via gpg id, regardless of the edition).
588  MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
589  // Temporarily disconnect to prevent the attempt to pass back the delete request.
591  bool dirty = false;
592  for ( const PublicKeyData & keyData : zyppKeys )
593  {
594  if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
595  {
596  DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
597  getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
598  if ( !dirty ) dirty = true;
599  }
600  }
601  if ( dirty )
602  zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
603  }
604 
605  computeKeyRingSync( rpmKeys, zyppKeys );
606  MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
607  MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
608 
610  if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
611  {
612  // export to zypp keyring
613  MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
614  // Temporarily disconnect to prevent the attempt to re-import the exported keys.
616  auto keepDbOpen = dbConstIterator(); // just to keep a ref.
617 
618  TmpFile tmpfile( getZYpp()->tmpPath() );
619  {
620  std::ofstream tmpos( tmpfile.path().c_str() );
621  for_( it, rpmKeys.begin(), rpmKeys.end() )
622  {
623  // we export the rpm key into a file
624  RpmHeader::constPtr result;
625  getData( "gpg-pubkey", *it, result );
626  tmpos << result->tag_description() << endl;
627  }
628  }
629  try
630  {
631  getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
632  // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
633  // Modern rpm does not import those keys, but when migrating a pre SLE12 system
634  // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
635  std::set<Edition> missingKeys;
636  for ( const Edition & key : rpmKeys )
637  {
638  if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
639  continue;
640  ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
641  missingKeys.insert( key );
642  }
643  if ( ! missingKeys.empty() )
644  callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
645  }
646  catch ( const Exception & excpt )
647  {
648  ZYPP_CAUGHT( excpt );
649  ERR << "Could not import keys into zypp keyring: " << endl;
650  }
651  }
652 
654  if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
655  {
656  // import from zypp keyring
657  MIL << "Importing zypp trusted keyring" << std::endl;
658  for_( it, zyppKeys.begin(), zyppKeys.end() )
659  {
660  try
661  {
662  importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
663  }
664  catch ( const RpmException & exp )
665  {
666  ZYPP_CAUGHT( exp );
667  }
668  }
669  }
670  MIL << "Trusted keys synced." << endl;
671 }
672 
675 
678 
680 //
681 //
682 // METHOD NAME : RpmDb::importPubkey
683 // METHOD TYPE : PMError
684 //
685 void RpmDb::importPubkey( const PublicKey & pubkey_r )
686 {
688 
689  // bnc#828672: On the fly key import in READONLY
691  {
692  WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
693  return;
694  }
695 
696  // check if the key is already in the rpm database
697  Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
698  std::set<Edition> rpmKeys = pubkeyEditions();
699  bool hasOldkeys = false;
700 
701  for_( it, rpmKeys.begin(), rpmKeys.end() )
702  {
703  // bsc#1008325: Keys using subkeys for signing don't get a higher release
704  // if new subkeys are added, because the primary key remains unchanged.
705  // For now always re-import keys with subkeys. Here we don't want to export the
706  // keys in the rpm database to check whether the subkeys are the same. The calling
707  // code should take care, we don't re-import the same kesy over and over again.
708  if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
709  {
710  MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
711  return;
712  }
713 
714  if ( keyEd.version() != (*it).version() )
715  continue; // different key ID (version)
716 
717  if ( keyEd.release() < (*it).release() )
718  {
719  MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
720  return;
721  }
722  else
723  {
724  hasOldkeys = true;
725  }
726  }
727  MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
728 
729  if ( hasOldkeys )
730  {
731  // We must explicitly delete old key IDs first (all releases,
732  // that's why we don't call removePubkey here).
733  std::string keyName( "gpg-pubkey-" + keyEd.version() );
734  RpmArgVec opts;
735  opts.push_back ( "-e" );
736  opts.push_back ( "--allmatches" );
737  opts.push_back ( "--" );
738  opts.push_back ( keyName.c_str() );
740 
741  std::string line;
742  while ( systemReadLine( line ) )
743  {
744  ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
745  }
746 
747  if ( systemStatus() != 0 )
748  {
749  ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
750  }
751  else
752  {
753  MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
754  }
755  }
756 
757  // import the new key
758  RpmArgVec opts;
759  opts.push_back ( "--import" );
760  opts.push_back ( "--" );
761  std::string pubkeypath( pubkey_r.path().asString() );
762  opts.push_back ( pubkeypath.c_str() );
764 
765  std::string line;
766  std::vector<std::string> excplines;
767  while ( systemReadLine( line ) )
768  {
769  if ( str::startsWith( line, "error:" ) )
770  {
771  WAR << line << endl;
772  excplines.push_back( std::move(line) );
773  }
774  else
775  DBG << line << endl;
776  }
777 
778  if ( systemStatus() != 0 )
779  {
780  // Translator: %1% is a gpg public key
781  RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
782  excp.moveToHistory( excplines );
783  excp.addHistory( std::move(error_message) );
784  ZYPP_THROW( excp );
785  }
786  else
787  {
788  MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
789  }
790 }
791 
793 //
794 //
795 // METHOD NAME : RpmDb::removePubkey
796 // METHOD TYPE : PMError
797 //
798 void RpmDb::removePubkey( const PublicKey & pubkey_r )
799 {
801 
802  // check if the key is in the rpm database and just
803  // return if it does not.
804  std::set<Edition> rpm_keys = pubkeyEditions();
805  std::set<Edition>::const_iterator found_edition = rpm_keys.end();
806  std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
807 
808  for_( it, rpm_keys.begin(), rpm_keys.end() )
809  {
810  if ( (*it).version() == pubkeyVersion )
811  {
812  found_edition = it;
813  break;
814  }
815  }
816 
817  // the key does not exist, cannot be removed
818  if (found_edition == rpm_keys.end())
819  {
820  WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
821  return;
822  }
823 
824  std::string rpm_name("gpg-pubkey-" + found_edition->asString());
825 
826  RpmArgVec opts;
827  opts.push_back ( "-e" );
828  opts.push_back ( "--" );
829  opts.push_back ( rpm_name.c_str() );
831 
832  std::string line;
833  std::vector<std::string> excplines;
834  while ( systemReadLine( line ) )
835  {
836  if ( str::startsWith( line, "error:" ) )
837  {
838  WAR << line << endl;
839  excplines.push_back( std::move(line) );
840  }
841  else
842  DBG << line << endl;
843  }
844 
845  if ( systemStatus() != 0 )
846  {
847  // Translator: %1% is a gpg public key
848  RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
849  excp.moveToHistory( excplines );
850  excp.addHistory( std::move(error_message) );
851  ZYPP_THROW( excp );
852  }
853  else
854  {
855  MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
856  }
857 }
858 
860 //
861 //
862 // METHOD NAME : RpmDb::pubkeys
863 // METHOD TYPE : std::set<Edition>
864 //
865 std::list<PublicKey> RpmDb::pubkeys() const
866 {
867  std::list<PublicKey> ret;
868 
869  auto it = dbConstIterator();
870  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
871  {
872  Edition edition = it->tag_edition();
873  if (edition != Edition::noedition)
874  {
875  // we export the rpm key into a file
876  RpmHeader::constPtr result;
877  getData( "gpg-pubkey", edition, result );
878  TmpFile file(getZYpp()->tmpPath());
879  std::ofstream os;
880  try
881  {
882  os.open(file.path().asString().c_str());
883  // dump rpm key into the tmp file
884  os << result->tag_description();
885  //MIL << "-----------------------------------------------" << endl;
886  //MIL << result->tag_description() <<endl;
887  //MIL << "-----------------------------------------------" << endl;
888  os.close();
889  // read the public key from the dumped file
890  PublicKey key(file);
891  ret.push_back(key);
892  }
893  catch ( std::exception & e )
894  {
895  ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
896  // just ignore the key
897  }
898  }
899  }
900  return ret;
901 }
902 
903 std::set<Edition> RpmDb::pubkeyEditions() const
904  {
905  std::set<Edition> ret;
906 
907  auto it = dbConstIterator();
908  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
909  {
910  Edition edition = it->tag_edition();
911  if (edition != Edition::noedition)
912  ret.insert( edition );
913  }
914  return ret;
915  }
916 
917 
919 //
920 //
921 // METHOD NAME : RpmDb::fileList
922 // METHOD TYPE : bool
923 //
924 // DESCRIPTION :
925 //
926 std::list<FileInfo>
927 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
928 {
929  std::list<FileInfo> result;
930 
931  auto it = dbConstIterator();
932  bool found = false;
933  if (edition_r == Edition::noedition)
934  {
935  found = it.findPackage( name_r );
936  }
937  else
938  {
939  found = it.findPackage( name_r, edition_r );
940  }
941  if (!found)
942  return result;
943 
944  return result;
945 }
946 
947 
949 //
950 //
951 // METHOD NAME : RpmDb::hasFile
952 // METHOD TYPE : bool
953 //
954 // DESCRIPTION :
955 //
956 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
957 {
958  auto it = dbConstIterator();
959  bool res = false;
960  do
961  {
962  res = it.findByFile( file_r );
963  if (!res) break;
964  if (!name_r.empty())
965  {
966  res = (it->tag_name() == name_r);
967  }
968  ++it;
969  }
970  while (res && *it);
971  return res;
972 }
973 
975 //
976 //
977 // METHOD NAME : RpmDb::whoOwnsFile
978 // METHOD TYPE : std::string
979 //
980 // DESCRIPTION :
981 //
982 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
983 {
984  auto it = dbConstIterator();
985  if (it.findByFile( file_r ))
986  {
987  return it->tag_name();
988  }
989  return "";
990 }
991 
993 //
994 //
995 // METHOD NAME : RpmDb::hasProvides
996 // METHOD TYPE : bool
997 //
998 // DESCRIPTION :
999 //
1000 bool RpmDb::hasProvides( const std::string & tag_r ) const
1001 {
1002  auto it = dbConstIterator();
1003  return it.findByProvides( tag_r );
1004 }
1005 
1007 //
1008 //
1009 // METHOD NAME : RpmDb::hasRequiredBy
1010 // METHOD TYPE : bool
1011 //
1012 // DESCRIPTION :
1013 //
1014 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1015 {
1016  auto it = dbConstIterator();
1017  return it.findByRequiredBy( tag_r );
1018 }
1019 
1021 //
1022 //
1023 // METHOD NAME : RpmDb::hasConflicts
1024 // METHOD TYPE : bool
1025 //
1026 // DESCRIPTION :
1027 //
1028 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1029 {
1030  auto it = dbConstIterator();
1031  return it.findByConflicts( tag_r );
1032 }
1033 
1035 //
1036 //
1037 // METHOD NAME : RpmDb::hasPackage
1038 // METHOD TYPE : bool
1039 //
1040 // DESCRIPTION :
1041 //
1042 bool RpmDb::hasPackage( const std::string & name_r ) const
1043 {
1044  auto it = dbConstIterator();
1045  return it.findPackage( name_r );
1046 }
1047 
1049 //
1050 //
1051 // METHOD NAME : RpmDb::hasPackage
1052 // METHOD TYPE : bool
1053 //
1054 // DESCRIPTION :
1055 //
1056 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1057 {
1058  auto it = dbConstIterator();
1059  return it.findPackage( name_r, ed_r );
1060 }
1061 
1063 //
1064 //
1065 // METHOD NAME : RpmDb::getData
1066 // METHOD TYPE : PMError
1067 //
1068 // DESCRIPTION :
1069 //
1070 void RpmDb::getData( const std::string & name_r,
1071  RpmHeader::constPtr & result_r ) const
1072 {
1073  auto it = dbConstIterator();
1074  it.findPackage( name_r );
1075  result_r = *it;
1076 #if 0 // if this is needed we need to forcefully close the db of running db_const_iterators
1077  if (it.dbError())
1078  ZYPP_THROW(*(it.dbError()));
1079 #endif
1080 }
1081 
1083 //
1084 //
1085 // METHOD NAME : RpmDb::getData
1086 // METHOD TYPE : void
1087 //
1088 // DESCRIPTION :
1089 //
1090 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1091  RpmHeader::constPtr & result_r ) const
1092 {
1093  auto it = dbConstIterator();
1094  it.findPackage( name_r, ed_r );
1095  result_r = *it;
1096 #if 0 // if this is needed we need to forcefully close the db of running db_const_iterators
1097  if (it.dbError())
1098  ZYPP_THROW(*(it.dbError()));
1099 #endif
1100 }
1101 
1103 namespace
1104 {
1105  struct RpmlogCapture : public std::vector<std::string>
1106  {
1107  RpmlogCapture()
1108  {
1109  rpmlogSetCallback( rpmLogCB, this );
1110  _oldMask = rpmlogSetMask( RPMLOG_UPTO( RPMLOG_PRI(RPMLOG_INFO) ) );
1111  }
1112 
1113  RpmlogCapture(const RpmlogCapture &) = delete;
1114  RpmlogCapture(RpmlogCapture &&) = delete;
1115  RpmlogCapture &operator=(const RpmlogCapture &) = delete;
1116  RpmlogCapture &operator=(RpmlogCapture &&) = delete;
1117 
1118  ~RpmlogCapture() {
1119  rpmlogSetCallback( nullptr, nullptr );
1120  rpmlogSetMask( _oldMask );
1121  }
1122 
1123  static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1124  { return reinterpret_cast<RpmlogCapture*>(data_r)->rpmLog( rec_r ); }
1125 
1126  int rpmLog( rpmlogRec rec_r )
1127  {
1128  std::string l { ::rpmlogRecMessage( rec_r ) }; // NL terminated line!
1129  l.pop_back(); // strip trailing NL
1130  push_back( std::move(l) );
1131  return 0;
1132  }
1133 
1134  private:
1135  int _oldMask = 0;
1136  };
1137 
1138  std::ostream & operator<<( std::ostream & str, const RpmlogCapture & obj )
1139  {
1140  char sep = '\0';
1141  for ( const auto & l : obj ) {
1142  if ( sep ) str << sep; else sep = '\n';
1143  str << l;
1144  }
1145  return str;
1146  }
1147 
1148 
1149  RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1150  const Pathname & root_r, // target root
1151  bool requireGPGSig_r, // whether no gpg signature is to be reported
1152  RpmDb::CheckPackageDetail & detail_r ) // detailed result
1153  {
1154  PathInfo file( path_r );
1155  if ( ! file.isFile() )
1156  {
1157  ERR << "Not a file: " << file << endl;
1158  return RpmDb::CHK_ERROR;
1159  }
1160 
1161  FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1162  if ( fd == 0 || ::Ferror(fd) )
1163  {
1164  ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1165  if ( fd )
1166  ::Fclose( fd );
1167  return RpmDb::CHK_ERROR;
1168  }
1169  rpmts ts = ::rpmtsCreate();
1170  ::rpmtsSetRootDir( ts, root_r.c_str() );
1171  ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1172 #ifdef HAVE_RPM_VERIFY_TRANSACTION_STEP
1173  ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
1174 #endif
1175 
1176  RpmlogCapture vresult;
1177  LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1178  static rpmQVKArguments_s qva = ([](){ rpmQVKArguments_s qva; memset( &qva, 0, sizeof(rpmQVKArguments_s) ); return qva; })();
1179  int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1180  guard.restore();
1181 
1182  ts = rpmtsFree(ts);
1183  ::Fclose( fd );
1184 
1185  // Check the individual signature/disgest results:
1186 
1187  // To.map back known result strings to enum, everything else is CHK_ERROR.
1188  typedef std::map<std::string_view,RpmDb::CheckPackageResult> ResultMap;
1189  static const ResultMap resultMap {
1190  { "OK", RpmDb::CHK_OK },
1191  { "NOKEY", RpmDb::CHK_NOKEY },
1192  { "BAD", RpmDb::CHK_FAIL },
1193  { "UNKNOWN", RpmDb::CHK_NOTFOUND },
1194  { "NOTRUSTED", RpmDb::CHK_NOTTRUSTED },
1195  { "NOTFOUND", RpmDb::CHK_NOTFOUND },
1196  };
1197  auto getresult = []( const ResultMap & resultMap, ResultMap::key_type key )->ResultMap::mapped_type {
1198  auto it = resultMap.find( key );
1199  return it != resultMap.end() ? it->second : RpmDb::CHK_ERROR;
1200  };
1201 
1202  // To track the signature states we saw.
1203  unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1204 
1205  // To track the kind off sigs we saw.
1206  enum Saw {
1207  SawNone = 0,
1208  SawHeaderSig = (1 << 0), // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1209  SawHeaderDigest = (1 << 1), // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1210  SawPayloadDigest = (1 << 2), // Payload SHA256 digest: OK
1211  SawSig = (1 << 3), // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1212  SawDigest = (1 << 4), // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1213  };
1214  unsigned saw = SawNone;
1215 
1216  static const str::regex rx( "^ *(Header|Payload)? .*(Signature, key|digest).*: ([A-Z]+)" );
1217  str::smatch what;
1218  for ( const std::string & line : vresult )
1219  {
1220  if ( line[0] != ' ' ) // result lines are indented
1221  continue;
1222 
1224  if ( str::regex_match( line, what, rx ) ) {
1225 
1226  lineres = getresult( resultMap, what[3] );
1227  if ( lineres == RpmDb::CHK_NOTFOUND )
1228  continue; // just collect details for signatures found (#229)
1229 
1230  if ( what[1][0] == 'H' ) {
1231  saw |= ( what[2][0] == 'S' ? SawHeaderSig :SawHeaderDigest );
1232  }
1233  else if ( what[1][0] == 'P' ) {
1234  if ( what[2][0] == 'd' ) saw |= SawPayloadDigest;
1235  }
1236  else {
1237  saw |= ( what[2][0] == 'S' ? SawSig : SawDigest );
1238  }
1239  }
1240 
1241  ++count[lineres];
1242  detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, line ) );
1243  }
1244 
1245  // Now combine the overall result:
1247 
1248  if ( count[RpmDb::CHK_FAIL] )
1249  ret = RpmDb::CHK_FAIL;
1250 
1251  else if ( count[RpmDb::CHK_NOTFOUND] )
1252  ret = RpmDb::CHK_NOTFOUND;
1253 
1254  else if ( count[RpmDb::CHK_NOKEY] )
1255  ret = RpmDb::CHK_NOKEY;
1256 
1257  else if ( count[RpmDb::CHK_NOTTRUSTED] )
1258  ret = RpmDb::CHK_NOTTRUSTED;
1259 
1260  else if ( ret == RpmDb::CHK_OK ) {
1261  // Everything is OK, so check whether it's sufficient.
1262  // bsc#1184501: To count as signed the package needs a header signature
1263  // and either a payload digest (secured by the header sig) or a content signature.
1264  bool isSigned = (saw & SawHeaderSig) && ( (saw & SawPayloadDigest) || (saw & SawSig) );
1265  if ( not isSigned ) {
1266  std::string message { " " };
1267  if ( not (saw & SawHeaderSig) )
1268  message += _("Package header is not signed!");
1269  else
1270  message += _("Package payload is not signed!");
1271 
1272  detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::move(message) ) );
1273  if ( requireGPGSig_r )
1274  ret = RpmDb::CHK_NOSIG;
1275  }
1276  }
1277 
1278  if ( ret != RpmDb::CHK_OK )
1279  {
1280  // In case of an error line results may be reported to the user. In case rpm printed
1281  // only 8byte key IDs to stdout we try to get longer IDs from the header.
1282  bool didReadHeader = false;
1283  std::unordered_map< std::string, std::string> fprs;
1284 
1285  // we replace the data only if the key IDs are actually only 8 bytes
1286  str::regex rxexpr( "key ID ([a-fA-F0-9]{8}):" );
1287  for ( auto &detail : detail_r ) {
1288  auto &line = detail.second;
1289  str::smatch what;
1290  if ( str::regex_match( line, what, rxexpr ) ) {
1291 
1292  if ( !didReadHeader ) {
1293  didReadHeader = true;
1294 
1295  // Get signature info from the package header, RPM always prints only the 8 byte ID
1296  auto header = RpmHeader::readPackage( path_r, RpmHeader::NOVERIFY );
1297  if ( header ) {
1298  auto keyMgr = zypp::KeyManagerCtx::createForOpenPGP();
1299  const auto &addFprs = [&]( auto tag ){
1300  const auto &list1 = keyMgr.readSignatureFingerprints( header->blob_val( tag ) );
1301  for ( const auto &id : list1 ) {
1302  if ( id.size() <= 8 )
1303  continue;
1304 
1305  const auto &lowerId = str::toLower( id );
1306  fprs.insert( std::make_pair( lowerId.substr( lowerId.size() - 8 ), lowerId ) );
1307  }
1308  };
1309 
1310  addFprs( RPMTAG_SIGGPG );
1311  addFprs( RPMTAG_SIGPGP );
1312  addFprs( RPMTAG_RSAHEADER );
1313  addFprs( RPMTAG_DSAHEADER );
1314 
1315  } else {
1316  ERR << "Failed to read package signatures." << std::endl;
1317  }
1318  }
1319 
1320  // if we have no keys we can substitute we can leave the loop right away
1321  if ( !fprs.size() )
1322  break;
1323 
1324  {
1325  // replace the short key ID with the long ones parsed from the header
1326  const auto &keyId = str::toLower( what[1] );
1327  if ( const auto &i = fprs.find( keyId ); i != fprs.end() ) {
1328  str::replaceAll( line, keyId, i->second );
1329  }
1330  }
1331  }
1332  }
1333 
1334  WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1335  WAR << vresult << endl;
1336  }
1337  else
1338  DBG << path_r << " [0-Signature is OK]" << endl;
1339  return ret;
1340  }
1341 
1342 } // namespace
1344 //
1345 // METHOD NAME : RpmDb::checkPackage
1346 // METHOD TYPE : RpmDb::CheckPackageResult
1347 //
1349 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1350 
1352 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1353 
1355 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1356 
1357 
1358 // determine changed files of installed package
1359 bool
1360 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1361 {
1362  bool ok = true;
1363 
1364  fileList.clear();
1365 
1366  if ( ! initialized() ) return false;
1367 
1368  RpmArgVec opts;
1369 
1370  opts.push_back ("-V");
1371  opts.push_back ("--nodeps");
1372  opts.push_back ("--noscripts");
1373  opts.push_back ("--nomd5");
1374  opts.push_back ("--");
1375  opts.push_back (packageName.c_str());
1376 
1378 
1379  if ( process == NULL )
1380  return false;
1381 
1382  /* from rpm manpage
1383  5 MD5 sum
1384  S File size
1385  L Symlink
1386  T Mtime
1387  D Device
1388  U User
1389  G Group
1390  M Mode (includes permissions and file type)
1391  */
1392 
1393  std::string line;
1394  while (systemReadLine(line))
1395  {
1396  if (line.length() > 12 &&
1397  (line[0] == 'S' || line[0] == 's' ||
1398  (line[0] == '.' && line[7] == 'T')))
1399  {
1400  // file has been changed
1401  std::string filename;
1402 
1403  filename.assign(line, 11, line.length() - 11);
1404  fileList.insert(filename);
1405  }
1406  }
1407 
1408  systemStatus();
1409  // exit code ignored, rpm returns 1 no matter if package is installed or
1410  // not
1411 
1412  return ok;
1413 }
1414 
1415 
1416 /****************************************************************/
1417 /* private member-functions */
1418 /****************************************************************/
1419 
1420 /*--------------------------------------------------------------*/
1421 /* Run rpm with the specified arguments, handling stderr */
1422 /* as specified by disp */
1423 /*--------------------------------------------------------------*/
1424 void
1427 {
1428  if ( process )
1429  {
1430  delete process;
1431  process = NULL;
1432  }
1433  exit_code = -1;
1434 
1435  if ( ! initialized() )
1436  {
1438  }
1439 
1440  RpmArgVec args;
1441 
1442  // always set root and dbpath
1443 #if defined(WORKAROUNDRPMPWDBUG)
1444  args.push_back("#/"); // chdir to / to workaround bnc#819354
1445 #endif
1446  args.push_back("rpm");
1447  args.push_back("--root");
1448  args.push_back(_root.asString().c_str());
1449  args.push_back("--dbpath");
1450  args.push_back(_dbPath.asString().c_str());
1451  if ( env::ZYPP_RPM_DEBUG() )
1452  args.push_back("-vv");
1453  const char* argv[args.size() + opts.size() + 1];
1454 
1455  const char** p = argv;
1456  p = copy (args.begin (), args.end (), p);
1457  p = copy (opts.begin (), opts.end (), p);
1458  *p = 0;
1459 
1460 #if 0 // if this is needed we need to forcefully close the db of running db_const_iterators
1461  // Invalidate all outstanding database handles in case
1462  // the database gets modified.
1463  librpmDb::dbRelease( true );
1464 #endif
1465 
1466  // Launch the program with default locale
1467  process = new ExternalProgram(argv, disp, false, -1, true);
1468  return;
1469 }
1470 
1471 /*--------------------------------------------------------------*/
1472 /* Read a line from the rpm process */
1473 /*--------------------------------------------------------------*/
1474 bool RpmDb::systemReadLine( std::string & line )
1475 {
1476  line.erase();
1477 
1478  if ( process == NULL )
1479  return false;
1480 
1481  if ( process->inputFile() )
1482  {
1483  process->setBlocking( false );
1484  FILE * inputfile = process->inputFile();
1485  do {
1486  // Check every 5 seconds if the process is still running to prevent against
1487  // daemons launched in rpm %post that do not close their filedescriptors,
1488  // causing us to block for infinity. (bnc#174548)
1489  const auto &readResult = io::receiveUpto( inputfile, '\n', 5 * 1000, false );
1490  switch ( readResult.first ) {
1492  if ( !process->running() )
1493  return false;
1494 
1495  // we might have received a partial line, lets not forget about it
1496  line += readResult.second;
1497  break;
1498  }
1501  line += readResult.second;
1502  if ( line.size() && line.back() == '\n')
1503  line.pop_back();
1504  return line.size(); // in case of pending output
1505  }
1507  line += readResult.second;
1508 
1509  if ( line.size() && line.back() == '\n')
1510  line.pop_back();
1511 
1512  if ( env::ZYPP_RPM_DEBUG() )
1513  L_DBG("RPM_DEBUG") << line << endl;
1514  return true; // complete line
1515  }
1516  }
1517  } while( true );
1518  }
1519  return false;
1520 }
1521 
1522 /*--------------------------------------------------------------*/
1523 /* Return the exit status of the rpm process, closing the */
1524 /* connection if not already done */
1525 /*--------------------------------------------------------------*/
1526 int
1528 {
1529  if ( process == NULL )
1530  return -1;
1531 
1532  exit_code = process->close();
1533  if (exit_code == 0)
1534  error_message = "";
1535  else
1537  process->kill();
1538  delete process;
1539  process = 0;
1540 
1541  // DBG << "exit code " << exit_code << endl;
1542 
1543  return exit_code;
1544 }
1545 
1546 /*--------------------------------------------------------------*/
1547 /* Forcably kill the rpm process */
1548 /*--------------------------------------------------------------*/
1549 void
1551 {
1552  if (process) process->kill();
1553 }
1554 
1555 
1556 // generate diff mails for config files
1557 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1558 {
1559  std::string msg = line.substr(9);
1560  std::string::size_type pos1 = std::string::npos;
1561  std::string::size_type pos2 = std::string::npos;
1562  std::string file1s, file2s;
1563  Pathname file1;
1564  Pathname file2;
1565 
1566  pos1 = msg.find (typemsg);
1567  for (;;)
1568  {
1569  if ( pos1 == std::string::npos )
1570  break;
1571 
1572  pos2 = pos1 + strlen (typemsg);
1573 
1574  if (pos2 >= msg.length() )
1575  break;
1576 
1577  file1 = msg.substr (0, pos1);
1578  file2 = msg.substr (pos2);
1579 
1580  file1s = file1.asString();
1581  file2s = file2.asString();
1582 
1583  if (!_root.empty() && _root != "/")
1584  {
1585  file1 = _root + file1;
1586  file2 = _root + file2;
1587  }
1588 
1589  std::string out;
1590  int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1591  if (ret)
1592  {
1593  Pathname file = _root + WARNINGMAILPATH;
1594  if (filesystem::assert_dir(file) != 0)
1595  {
1596  ERR << "Could not create " << file.asString() << endl;
1597  break;
1598  }
1599  file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1600  std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1601  if (!notify)
1602  {
1603  ERR << "Could not open " << file << endl;
1604  break;
1605  }
1606 
1607  // Translator: %s = name of an rpm package. A list of diffs follows
1608  // this message.
1609  notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1610  if (ret>1)
1611  {
1612  ERR << "diff failed" << endl;
1613  notify << str::form(difffailmsg,
1614  file1s.c_str(), file2s.c_str()) << endl;
1615  }
1616  else
1617  {
1618  notify << str::form(diffgenmsg,
1619  file1s.c_str(), file2s.c_str()) << endl;
1620 
1621  // remove root for the viewer's pleasure (#38240)
1622  if (!_root.empty() && _root != "/")
1623  {
1624  if (out.substr(0,4) == "--- ")
1625  {
1626  out.replace(4, file1.asString().length(), file1s);
1627  }
1628  std::string::size_type pos = out.find("\n+++ ");
1629  if (pos != std::string::npos)
1630  {
1631  out.replace(pos+5, file2.asString().length(), file2s);
1632  }
1633  }
1634  notify << out << endl;
1635  }
1636  notify.close();
1637  notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1638  notify.close();
1639  }
1640  else
1641  {
1642  WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1643  }
1644  break;
1645  }
1646 }
1647 
1649 //
1650 // METHOD NAME : RpmDb::installPackage
1651 //
1652 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1653 { installPackage( filename, flags, nullptr ); }
1654 
1655 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags, RpmPostTransCollector* postTransCollector_r )
1656 {
1657  if ( postTransCollector_r && postTransCollector_r->hasPosttransScript( filename ) )
1658  flags |= rpm::RPMINST_NOPOSTTRANS; // Just set the flag here. In \ref doInstallPackage we collect what else is needed.
1659 
1661 
1662  report->start(filename);
1663 
1664  do
1665  try
1666  {
1667  doInstallPackage( filename, flags, postTransCollector_r, report );
1668  report->finish();
1669  break;
1670  }
1671  catch (RpmException & excpt_r)
1672  {
1673  RpmInstallReport::Action user = report->problem( excpt_r );
1674 
1675  if ( user == RpmInstallReport::ABORT )
1676  {
1677  report->finish( excpt_r );
1678  ZYPP_RETHROW(excpt_r);
1679  }
1680  else if ( user == RpmInstallReport::IGNORE )
1681  {
1682  break;
1683  }
1684  }
1685  while (true);
1686 }
1687 
1688 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, RpmPostTransCollector* postTransCollector_r, callback::SendReport<RpmInstallReport> & report )
1689 {
1691  HistoryLog historylog;
1692 
1693  MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1694 
1695  // backup
1696  if ( _packagebackups )
1697  {
1698  // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1699  if ( ! backupPackage( filename ) )
1700  {
1701  ERR << "backup of " << filename.asString() << " failed" << endl;
1702  }
1703  // FIXME status handling
1704  report->progress( 0 ); // allow 1% for backup creation.
1705  }
1706 
1707  // run rpm
1708  RpmArgVec opts;
1709  if ( postTransCollector_r && ( _root == "/" || not workaroundDUMPPOSTTRANS_BUG_1216091() ) ) {
1710  opts.push_back("--define"); // bsc#1041742: Attempt to delay %transfiletrigger(postun|in) execution iff rpm supports it.
1711  opts.push_back("_dump_posttrans 1"); // Old rpm ignores the --define, new rpm injects 'dump_posttrans:' lines to collect and execute later.
1712  }
1713  if (flags & RPMINST_NOUPGRADE)
1714  opts.push_back("-i");
1715  else
1716  opts.push_back("-U");
1717 
1718  opts.push_back("--percent");
1719  opts.push_back("--noglob");
1720 
1721  // ZConfig defines cross-arch installation
1722  if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1723  opts.push_back("--ignorearch");
1724 
1725  if (flags & RPMINST_NODIGEST)
1726  opts.push_back("--nodigest");
1727  if (flags & RPMINST_NOSIGNATURE)
1728  opts.push_back("--nosignature");
1729  if (flags & RPMINST_EXCLUDEDOCS)
1730  opts.push_back ("--excludedocs");
1731  if (flags & RPMINST_NOSCRIPTS)
1732  opts.push_back ("--noscripts");
1733  if (flags & RPMINST_FORCE)
1734  opts.push_back ("--force");
1735  if (flags & RPMINST_NODEPS)
1736  opts.push_back ("--nodeps");
1737  if (flags & RPMINST_IGNORESIZE)
1738  opts.push_back ("--ignoresize");
1739  if (flags & RPMINST_JUSTDB)
1740  opts.push_back ("--justdb");
1741  if (flags & RPMINST_TEST)
1742  opts.push_back ("--test");
1743  if (flags & RPMINST_NOPOSTTRANS)
1744  opts.push_back ("--noposttrans");
1745 
1746  opts.push_back("--");
1747 
1748  // rpm requires additional quoting of special chars:
1749  std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1750  opts.push_back ( quotedFilename.c_str() );
1752 
1753  // forward additional rpm output via report;
1754  std::string line;
1755  unsigned lineno = 0;
1756  callback::UserData cmdout( InstallResolvableReport::contentRpmout );
1757  // Key "solvable" injected by RpmInstallPackageReceiver
1758  cmdout.set( "line", std::cref(line) );
1759  cmdout.set( "lineno", lineno );
1760 
1761  // LEGACY: collect and forward additional rpm output in finish
1762  std::string rpmmsg;
1763  std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
1764  std::vector<std::string> runposttrans; // bsc#1041742: If rpm supports --runposttrans it injects 'dump_posttrans:' lines we do collect
1765 
1766  while ( systemReadLine( line ) )
1767  {
1768  if ( str::startsWith( line, "%%" ) )
1769  {
1770  int percent = 0;
1771  sscanf( line.c_str() + 2, "%d", &percent );
1772  report->progress( percent );
1773  continue;
1774  }
1775  if ( str::hasPrefix( line, "dump_posttrans:" ) ) {
1776  runposttrans.push_back( line );
1777  continue;
1778  }
1779  ++lineno;
1780  cmdout.set( "lineno", lineno );
1781  report->report( cmdout );
1782 
1783  if ( lineno >= MAXRPMMESSAGELINES ) {
1784  if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1785  continue;
1786  }
1787 
1788  rpmmsg += line+'\n';
1789 
1790  if ( str::startsWith( line, "warning:" ) )
1791  configwarnings.push_back(line);
1792  }
1793  if ( lineno >= MAXRPMMESSAGELINES )
1794  rpmmsg += "[truncated]\n";
1795 
1796  int rpm_status = systemStatus();
1797  if ( postTransCollector_r && rpm_status == 0 ) {
1798  // Before doing anything else, handle any pending %posttrans script or dump_posttrans lines.
1799  postTransCollector_r->collectPosttransInfo( filename, runposttrans );
1800  }
1801 
1802  // evaluate result
1803  for (std::vector<std::string>::iterator it = configwarnings.begin();
1804  it != configwarnings.end(); ++it)
1805  {
1806  processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1807  // %s = filenames
1808  _("rpm saved %s as %s, but it was impossible to determine the difference"),
1809  // %s = filenames
1810  _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1811  processConfigFiles(*it, Pathname::basename(filename), " created as ",
1812  // %s = filenames
1813  _("rpm created %s as %s, but it was impossible to determine the difference"),
1814  // %s = filenames
1815  _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1816  }
1817 
1818  if ( rpm_status != 0 )
1819  {
1820  historylog.comment(
1821  str::form("%s install failed", Pathname::basename(filename).c_str()),
1822  true /*timestamp*/);
1823  std::ostringstream sstr;
1824  sstr << "rpm output:" << endl << rpmmsg << endl;
1825  historylog.comment(sstr.str());
1826  // TranslatorExplanation the colon is followed by an error message
1827  auto excpt { RpmSubprocessException(_("RPM failed: ") + error_message ) };
1828  if ( not rpmmsg.empty() )
1829  excpt.addHistory( rpmmsg );
1830  ZYPP_THROW(excpt);
1831  }
1832  else if ( ! rpmmsg.empty() )
1833  {
1834  historylog.comment(
1835  str::form("%s installed ok", Pathname::basename(filename).c_str()),
1836  true /*timestamp*/);
1837  std::ostringstream sstr;
1838  sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1839  historylog.comment(sstr.str());
1840 
1841  // report additional rpm output in finish (LEGACY! Lines are immediately reported as InstallResolvableReport::contentRpmout)
1842  // TranslatorExplanation Text is followed by a ':' and the actual output.
1843  report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1844  }
1845 }
1846 
1848 //
1849 // METHOD NAME : RpmDb::removePackage
1850 //
1851 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
1852 { removePackage( std::move(package), flags, nullptr ); }
1853 
1854 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
1855 { removePackage( name_r, flags, nullptr ); }
1856 
1857 void RpmDb::removePackage( const Package::constPtr& package, RpmInstFlags flags, RpmPostTransCollector* postTransCollector_r )
1858 { // 'rpm -e' does not like epochs
1859  removePackage( package->name()
1860  + "-" + package->edition().version()
1861  + "-" + package->edition().release()
1862  + "." + package->arch().asString(), flags, postTransCollector_r );
1863 }
1864 
1865 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags, RpmPostTransCollector* postTransCollector_r )
1866 {
1868 
1869  report->start( name_r );
1870 
1871  do
1872  try
1873  {
1874  doRemovePackage( name_r, flags, postTransCollector_r, report );
1875  report->finish();
1876  break;
1877  }
1878  catch (RpmException & excpt_r)
1879  {
1880  RpmRemoveReport::Action user = report->problem( excpt_r );
1881 
1882  if ( user == RpmRemoveReport::ABORT )
1883  {
1884  report->finish( excpt_r );
1885  ZYPP_RETHROW(excpt_r);
1886  }
1887  else if ( user == RpmRemoveReport::IGNORE )
1888  {
1889  break;
1890  }
1891  }
1892  while (true);
1893 }
1894 
1895 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, RpmPostTransCollector* postTransCollector_r, callback::SendReport<RpmRemoveReport> & report )
1896 {
1898  HistoryLog historylog;
1899 
1900  MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1901 
1902  // backup
1903  if ( _packagebackups )
1904  {
1905  // FIXME solve this status report somehow
1906  // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1907  if ( ! backupPackage( name_r ) )
1908  {
1909  ERR << "backup of " << name_r << " failed" << endl;
1910  }
1911  report->progress( 0 );
1912  }
1913  else
1914  {
1915  report->progress( 100 );
1916  }
1917 
1918  // run rpm
1919  RpmArgVec opts;
1920  if ( postTransCollector_r && ( _root == "/" || not workaroundDUMPPOSTTRANS_BUG_1216091() ) ) {
1921  opts.push_back("--define"); // bsc#1041742: Attempt to delay %transfiletrigger(postun|in) execution iff rpm supports it.
1922  opts.push_back("_dump_posttrans 1"); // Old rpm ignores the --define, new rpm injects 'dump_posttrans:' lines to collect and execute later.
1923  }
1924  opts.push_back("-e");
1925  opts.push_back("--allmatches");
1926 
1927  if (flags & RPMINST_NOSCRIPTS)
1928  opts.push_back("--noscripts");
1929  if (flags & RPMINST_NODEPS)
1930  opts.push_back("--nodeps");
1931  if (flags & RPMINST_JUSTDB)
1932  opts.push_back("--justdb");
1933  if (flags & RPMINST_TEST)
1934  opts.push_back ("--test");
1935  if (flags & RPMINST_FORCE)
1936  {
1937  WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1938  }
1939 
1940  opts.push_back("--");
1941  opts.push_back(name_r.c_str());
1943 
1944  // forward additional rpm output via report;
1945  std::string line;
1946  unsigned lineno = 0;
1947  callback::UserData cmdout( RemoveResolvableReport::contentRpmout );
1948  // Key "solvable" injected by RpmInstallPackageReceiver
1949  cmdout.set( "line", std::cref(line) );
1950  cmdout.set( "lineno", lineno );
1951 
1952 
1953  // LEGACY: collect and forward additional rpm output in finish
1954  std::string rpmmsg;
1955  std::vector<std::string> runposttrans; // bsc#1041742: If rpm supports --runposttrans it injects 'dump_posttrans:' lines we do collect
1956 
1957  // got no progress from command, so we fake it:
1958  // 5 - command started
1959  // 50 - command completed
1960  // 100 if no error
1961  report->progress( 5 );
1962  while (systemReadLine(line))
1963  {
1964  if ( str::hasPrefix( line, "dump_posttrans:" ) ) {
1965  runposttrans.push_back( line );
1966  continue;
1967  }
1968  ++lineno;
1969  cmdout.set( "lineno", lineno );
1970  report->report( cmdout );
1971 
1972  if ( lineno >= MAXRPMMESSAGELINES ) {
1973  if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1974  continue;
1975  }
1976  rpmmsg += line+'\n';
1977  }
1978  if ( lineno >= MAXRPMMESSAGELINES )
1979  rpmmsg += "[truncated]\n";
1980  report->progress( 50 );
1981  int rpm_status = systemStatus();
1982  if ( postTransCollector_r && rpm_status == 0 ) {
1983  // Before doing anything else, handle any pending %posttrans script or dump_posttrans lines.
1984  // 'remove' does not trigger %posttrans, but it may trigger %transfiletriggers.
1985  postTransCollector_r->collectPosttransInfo( runposttrans );
1986  }
1987 
1988  if ( rpm_status != 0 )
1989  {
1990  historylog.comment(
1991  str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
1992  std::ostringstream sstr;
1993  sstr << "rpm output:" << endl << rpmmsg << endl;
1994  historylog.comment(sstr.str());
1995  // TranslatorExplanation the colon is followed by an error message
1996  auto excpt { RpmSubprocessException(_("RPM failed: ") + error_message ) };
1997  if ( not rpmmsg.empty() )
1998  excpt.addHistory( rpmmsg );
1999  ZYPP_THROW(excpt);
2000  }
2001  else if ( ! rpmmsg.empty() )
2002  {
2003  historylog.comment(
2004  str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2005 
2006  std::ostringstream sstr;
2007  sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2008  historylog.comment(sstr.str());
2009 
2010  // report additional rpm output in finish (LEGACY! Lines are immediately reported as RemoveResolvableReport::contentRpmout)
2011  // TranslatorExplanation Text is followed by a ':' and the actual output.
2012  report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2013  }
2014 }
2015 
2017 //
2018 // METHOD NAME : RpmDb::runposttrans
2019 //
2020 int RpmDb::runposttrans( const Pathname & filename_r, const std::function<void(const std::string&)>& output_r )
2021 {
2023  HistoryLog historylog;
2024 
2025  MIL << "RpmDb::runposttrans(" << filename_r << ")" << endl;
2026 
2027  RpmArgVec opts;
2028 #if 1
2029  // Bug 1218459 rpm scriptlets left over after snapshot updates
2030  // Until 'rpm --runposttrans' is fixed to properly indicate script
2031  // execution without -vv we redirect rpm's tmpdir. This will wipe
2032  // the rpm-tmp.* files 'rpm -vv' otherwise leaves in /var/tmp.
2033  std::string _tmppath { "_tmppath " + Pathname::stripprefix( _root, filename_r.dirname() ).asString() };
2034  opts.push_back("--define");
2035  opts.push_back(_tmppath.c_str());
2036 #endif
2037  opts.push_back("-vv"); // want vverbose output to see scriptlet execution in the log
2038  opts.push_back("--runposttrans");
2039  opts.push_back(filename_r.c_str());
2041 
2042  // Tailored to suit RpmPostTransCollector.
2043  // It's a pity, but we need all those verbose debug lines just
2044  // to figure out which script is currently executed. Otherwise we
2045  // can't tell which output belongs to which script.
2046  static const str::regex rx( "^D: (%.*): (scriptlet start|running .* scriptlet)" );
2047  static const str::regex rx2( "^Running (%[^)]*[)])$" );
2048  str::smatch what;
2049  std::string line;
2050  bool silent = true; // discard everything before 1st scriptlet
2051  while ( systemReadLine(line) )
2052  {
2053  if ( not output_r )
2054  continue;
2055 
2056  if ( str::startsWith( line, "D:" ) ) { // rpm debug output
2057  if ( str::regex_match( line, what, rx ) ) {
2058  // forward ripoff header
2059  DBG << "Verbose RIPOFF:"+what[1] << endl;
2060  output_r( "RIPOFF:"+what[1] );
2061  if ( silent )
2062  silent = false;
2063  }
2064  continue;
2065  }
2066  if ( str::regex_match( line, what, rx2 ) ) { // preliminary for 1218459, but rpm needs fixing
2067  // forward ripoff header
2068  DBG << "NonVerbose RIPOFF:"+what[1] << endl;
2069  // output_r( "RIPOFF:"+what[1] );
2070  // if ( silent )
2071  // silent = false;
2072  continue;
2073  }
2074  if ( silent ) {
2075  continue;
2076  }
2077  if ( str::startsWith( line, "+ " ) ) { // shell -x debug output
2078  continue;
2079  }
2080  // forward output line
2081  output_r( line );
2082  }
2083 
2084  int rpm_status = systemStatus();
2085  if ( rpm_status != 0 ) {
2086  WAR << "rpm --runposttrans returned " << rpm_status << endl;
2087  }
2088  return rpm_status;
2089 }
2090 
2092 //
2093 //
2094 // METHOD NAME : RpmDb::backupPackage
2095 // METHOD TYPE : bool
2096 //
2097 bool RpmDb::backupPackage( const Pathname & filename )
2098 {
2100  if ( ! h )
2101  return false;
2102 
2103  return backupPackage( h->tag_name() );
2104 }
2105 
2107 //
2108 //
2109 // METHOD NAME : RpmDb::backupPackage
2110 // METHOD TYPE : bool
2111 //
2112 bool RpmDb::backupPackage(const std::string& packageName)
2113 {
2114  HistoryLog progresslog;
2115  bool ret = true;
2116  Pathname backupFilename;
2117  Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2118 
2119  if (_backuppath.empty())
2120  {
2121  INT << "_backuppath empty" << endl;
2122  return false;
2123  }
2124 
2126 
2127  if (!queryChangedFiles(fileList, packageName))
2128  {
2129  ERR << "Error while getting changed files for package " <<
2130  packageName << endl;
2131  return false;
2132  }
2133 
2134  if (fileList.size() <= 0)
2135  {
2136  DBG << "package " << packageName << " not changed -> no backup" << endl;
2137  return true;
2138  }
2139 
2141  {
2142  return false;
2143  }
2144 
2145  {
2146  // build up archive name
2147  time_t currentTime = time(0);
2148  struct tm *currentLocalTime = localtime(&currentTime);
2149 
2150  int date = (currentLocalTime->tm_year + 1900) * 10000
2151  + (currentLocalTime->tm_mon + 1) * 100
2152  + currentLocalTime->tm_mday;
2153 
2154  int num = 0;
2155  do
2156  {
2157  backupFilename = _root + _backuppath
2158  + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2159 
2160  }
2161  while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2162 
2163  PathInfo pi(filestobackupfile);
2164  if (pi.isExist() && !pi.isFile())
2165  {
2166  ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2167  return false;
2168  }
2169 
2170  std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2171 
2172  if (!fp)
2173  {
2174  ERR << "could not open " << filestobackupfile.asString() << endl;
2175  return false;
2176  }
2177 
2178  for (FileList::const_iterator cit = fileList.begin();
2179  cit != fileList.end(); ++cit)
2180  {
2181  std::string name = *cit;
2182  if ( name[0] == '/' )
2183  {
2184  // remove slash, file must be relative to -C parameter of tar
2185  name = name.substr( 1 );
2186  }
2187  DBG << "saving file "<< name << endl;
2188  fp << name << endl;
2189  }
2190  fp.close();
2191 
2192  const char* const argv[] =
2193  {
2194  "tar",
2195  "-czhP",
2196  "-C",
2197  _root.asString().c_str(),
2198  "--ignore-failed-read",
2199  "-f",
2200  backupFilename.asString().c_str(),
2201  "-T",
2202  filestobackupfile.asString().c_str(),
2203  NULL
2204  };
2205 
2206  // execute tar in inst-sys (we dont know if there is a tar below _root !)
2207  ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2208 
2209  std::string tarmsg;
2210 
2211  // TODO: it is probably possible to start tar with -v and watch it adding
2212  // files to report progress
2213  for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2214  {
2215  tarmsg+=output;
2216  }
2217 
2218  int ret = tar.close();
2219 
2220  if ( ret != 0)
2221  {
2222  ERR << "tar failed: " << tarmsg << endl;
2223  ret = false;
2224  }
2225  else
2226  {
2227  MIL << "tar backup ok" << endl;
2228  progresslog.comment(
2229  str::form(_("created backup %s"), backupFilename.asString().c_str())
2230  , /*timestamp*/true);
2231  }
2232 
2233  filesystem::unlink(filestobackupfile);
2234  }
2235 
2236  return ret;
2237 }
2238 
2240 {
2241  _backuppath = path;
2242 }
2243 
2244 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2245 {
2246  switch ( obj )
2247  {
2248 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2249  // translators: possible rpm package signature check result [brief]
2250  OUTS( CHK_OK, _("Signature is OK") );
2251  // translators: possible rpm package signature check result [brief]
2252  OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2253  // translators: possible rpm package signature check result [brief]
2254  OUTS( CHK_FAIL, _("Signature does not verify") );
2255  // translators: possible rpm package signature check result [brief]
2256  OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2257  // translators: possible rpm package signature check result [brief]
2258  OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2259  // translators: possible rpm package signature check result [brief]
2260  OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2261  // translators: possible rpm package signature check result [brief]
2262  OUTS( CHK_NOSIG, _("File is unsigned") );
2263 #undef OUTS
2264  }
2265  return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2266 }
2267 
2268 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2269 {
2270  for ( const auto & el : obj )
2271  str << el.second << endl;
2272  return str;
2273 }
2274 
2275 } // namespace rpm
2276 } // namespace target
2277 } // namespace zypp
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition: String.cc:180
Interface to the rpm program.
Definition: RpmDb.h:50
#define MIL
Definition: Logger.h:103
TraitsType::constPtrType constPtr
Definition: Package.h:39
~RpmDb() override
Destructor.
Definition: RpmDb.cc:247
CheckPackageResult checkPackageSignature(const Pathname &path_r, CheckPackageDetail &detail_r)
Check signature of rpm file on disk (strict check returning CHK_NOSIG if file is unsigned).
Definition: RpmDb.cc:1354
bool hasRequiredBy(const std::string &tag_r) const
Return true if at least one package requires a certain tag.
Definition: RpmDb.cc:1014
Namespace intended to collect all environment variables we use.
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:338
intrusive_ptr< const RpmHeader > constPtr
Definition: RpmHeader.h:65
void getData(const std::string &name_r, RpmHeader::constPtr &result_r) const
Get an installed packages data from rpmdb.
Definition: RpmDb.cc:1070
void sendTo(const ReceiverFnc &fnc_r)
Set ReceiverFnc.
Definition: progressdata.h:229
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:459
Regular expression.
Definition: Regex.h:94
bool kill()
Kill the program.
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:756
Pathname _root
Root directory for all operations.
Definition: RpmDb.h:86
std::string asString(const Patch::Category &obj)
relates: Patch::Category string representation.
Definition: Patch.cc:122
void trustedKeyRemoved(const PublicKey &key) override
Definition: RpmDb.cc:173
Class representing one GPG Public Keys data.
Definition: PublicKey.h:200
const std::string & execError() const
Some detail telling why the execution failed, if it failed.
std::string id() const
Definition: PublicKey.cc:657
void exportTrustedKeysInZyppKeyRing()
insert all rpm trusted keys into zypp trusted keyring
Definition: RpmDb.cc:676
#define INT
Definition: Logger.h:107
void doInstallPackage(const Pathname &filename, RpmInstFlags flags, RpmPostTransCollector *postTransCollector_r, callback::SendReport< RpmInstallReport > &report)
Definition: RpmDb.cc:1688
void rebuildDatabase()
Rebuild the rpm database (rpm –rebuilddb).
Definition: RpmDb.cc:373
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1652
void doRemovePackage(const std::string &name_r, RpmInstFlags flags, RpmPostTransCollector *postTransCollector_r, callback::SendReport< RpmRemoveReport > &report)
Definition: RpmDb.cc:1895
const char * c_str() const
String representation.
Definition: Pathname.h:113
void addHistory(const std::string &msg_r)
Add some message text to the history.
Definition: Exception.cc:189
String related utilities and Regular expression matching.
bool toMax()
Set counter value to current max value (unless no range).
Definition: progressdata.h:276
std::vector< const char * > RpmArgVec
Definition: RpmDb.h:303
static librpmDb::constPtr dbOpenCreate(const Pathname &root_r, const Pathname &dbPath_r=Pathname())
Assert the rpmdb below the system at root_r exists.
Definition: librpmDb.cc:198
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
Pathname path() const
Definition: TmpPath.cc:124
Edition represents [epoch:]version[-release]
Definition: Edition.h:59
bool running()
Return whether program is running.
std::string receiveLine()
Read one line from the input stream.
long long value_type
Definition: progressdata.h:134
bool hasSubkeys() const
!<
Definition: PublicKey.h:433
Convenient building of std::string with boost::format.
Definition: String.h:253
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:137
static KeyManagerCtx createForOpenPGP()
Creates a new KeyManagerCtx for PGP using a volatile temp.
Definition: KeyManager.cc:320
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:117
void importZyppKeyRingTrustedKeys()
iterates through zypp keyring and import all non-existent keys into rpm keyring
Definition: RpmDb.cc:673
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:39
bool backupPackage(const std::string &packageName)
create tar.gz of all changed files in a Package
Definition: RpmDb.cc:2112
Wrapper providing a librpmDb::db_const_iterator for this RpmDb.
Definition: RpmDb.h:64
void collectPosttransInfo(const Pathname &rpmPackage_r, const std::vector< std::string > &runposttrans_r)
Extract and remember a packages posttrans script or dump_posttrans lines for later execution...
#define ERR
Definition: Logger.h:105
CheckPackageResult checkPackage(const Pathname &path_r, CheckPackageDetail &detail_r)
Check signature of rpm file on disk (legacy version returning CHK_OK if file is unsigned, like &#39;rpm -K&#39;)
Definition: RpmDb.cc:1348
std::ostream & operator<<(std::ostream &str, const librpmDb::db_const_iterator &obj)
relates: librpmDb::db_const_iterator stream output
Definition: librpmDb.cc:412
#define FILEFORBACKUPFILES
Definition: RpmDb.cc:64
void range(value_type max_r)
Set new [0,max].
Definition: progressdata.h:216
Extract and remember posttrans scripts for later execution.
Temporarily connect a ReceiveReport then restore the previous one.
Definition: Callback.h:284
void importPubkey(const PublicKey &pubkey_r)
Import ascii armored public key in file pubkey_r.
Definition: RpmDb.cc:685
bool hasPosttransScript(const Pathname &rpmPackage_r)
Test whether a package defines a posttrans script.
Assign a vaiable a certain value when going out of scope.
Definition: dtorreset.h:49
_dumpPath dumpPath(const Pathname &root_r, const Pathname &sub_r)
dumpPath iomaip to dump &#39;(root_r)sub_r&#39; output,
Definition: librpmDb.h:42
bool hasPackage(const std::string &name_r) const
Return true if package is installed.
Definition: RpmDb.cc:1042
void systemKill()
Forcably kill the system process.
Definition: RpmDb.cc:1550
bool empty() const
Test for an empty path.
Definition: Pathname.h:117
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:479
void moveToHistory(TContainer &&msgc_r)
addHistory from string container types (oldest first) moving
Definition: Exception.h:258
bool toMin()
Set counter value to current min value.
Definition: progressdata.h:272
void syncTrustedKeys(SyncTrustedKeyBits mode_r=SYNC_BOTH)
Sync trusted keys stored in rpm database and zypp trusted keyring.
Definition: RpmDb.cc:572
#define FAILIFNOTINITIALIZED
Definition: RpmDb.cc:218
Store and operate on date (time_t).
Definition: Date.h:32
Pathname _backuppath
/var/adm/backup
Definition: RpmDb.h:347
std::string version() const
Version.
Definition: Edition.cc:96
std::string form(const std::string &format_r) const
Return string representation according to format as localtime.
Definition: Date.h:112
std::string asString() const
Definition: IdStringType.h:116
int exit_code
The exit code of the rpm process, or -1 if not yet known.
Definition: RpmDb.h:338
std::list< PublicKey > pubkeys() const
Return the long ids of all installed public keys.
Definition: RpmDb.cc:865
void trustedKeyAdded(const PublicKey &key) override
Definition: RpmDb.cc:167
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition: UserData.h:119
std::string gpgPubkeyVersion() const
Definition: PublicKey.cc:684
Subclass to retrieve rpm database content.
SyncTrustedKeyBits
Sync mode for syncTrustedKeys.
Definition: RpmDb.h:277
bool systemReadLine(std::string &line)
Read a line from the general rpm query.
Definition: RpmDb.cc:1474
const std::string & asString() const
String representation.
Definition: Pathname.h:94
#define WARNINGMAILPATH
Definition: RpmDb.cc:63
int systemStatus()
Return the exit status of the general rpm process, closing the connection if not already done...
Definition: RpmDb.cc:1527
std::set< Edition > pubkeyEditions() const
Return the edition of all installed public keys.
Definition: RpmDb.cc:903
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:140
std::ostream & dumpOn(std::ostream &str) const override
Dump debug info.
Definition: RpmDb.cc:262
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:133
std::string release() const
Release.
Definition: Edition.cc:112
#define WAR
Definition: Logger.h:104
Detailed rpm signature check log messages A single multiline message if CHK_OK.
Definition: RpmDb.h:391
static int match(const Edition &lhs, const Edition &rhs)
Definition: Edition.h:129
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1156
Types and functions for filesystem operations.
Definition: Glob.cc:23
int close() override
Wait for the progamm to complete.
static shared_ptr< KeyRingSignalReceiver > sKeyRingReceiver
Definition: RpmDb.cc:182
Maintain [min,max] and counter (value) for progress counting.
Definition: progressdata.h:131
Pathname expandlink(const Pathname &path_r)
Recursively follows the symlink pointed to by path_r and returns the Pathname to the real file or dir...
Definition: PathInfo.cc:964
ExternalProgram * process
The connection to the rpm process.
Definition: RpmDb.h:301
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
void doRebuildDatabase(callback::SendReport< RebuildDBReport > &report)
Definition: RpmDb.cc:391
bool incr(value_type val_r=1)
Increment counter value (default by 1).
Definition: progressdata.h:264
void initDatabase(Pathname root_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database below root_r.
Definition: RpmDb.cc:281
#define _(MSG)
Definition: Gettext.h:39
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition: RpmDb.cc:353
Stderr_Disposition
Define symbols for different policies on the handling of stderr.
bool hasProvides(const std::string &tag_r) const
Return true if at least one package provides a certain tag.
Definition: RpmDb.cc:1000
Just inherits Exception to separate media exceptions.
Definition: RpmException.h:38
static RpmHeader::constPtr readPackage(const Pathname &path, VERIFICATION verification=VERIFY)
Get an accessible packages data from disk.
Definition: RpmHeader.cc:212
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition: String.h:1163
std::string numstring(char n, int w=0)
Definition: String.h:290
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:719
SolvableIdType size_type
Definition: poolconstants.h:59
import zypp trusted keys into rpm database.
Definition: RpmDb.h:280
Editions with v-r setparator highlighted.
#define OUTS(E, S)
std::ostream & copy(std::istream &from_r, std::ostream &to_r)
Copy istream to ostream.
Definition: IOStream.h:51
void removePubkey(const PublicKey &pubkey_r)
Remove a public key from the rpm database.
Definition: RpmDb.cc:798
void processConfigFiles(const std::string &line, const std::string &name, const char *typemsg, const char *difffailmsg, const char *diffgenmsg)
handle rpm messages like "/etc/testrc saved as /etc/testrc.rpmorig"
Definition: RpmDb.cc:1557
ZYpp::Ptr getZYpp()
relates: ZYppFactory Convenience to get the Pointer to the ZYpp instance.
Definition: ZYppFactory.h:77
#define L_DBG(GROUP)
Definition: Logger.h:111
bool _packagebackups
create package backups?
Definition: RpmDb.h:350
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like &#39;symlink&#39;.
Definition: PathInfo.cc:874
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
bool ZYPP_RPM_DEBUG()
Definition: RpmDb.cc:80
Regular expression match result.
Definition: Regex.h:167
std::string gpgPubkeyRelease() const
Definition: PublicKey.cc:687
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition: PublicKey.h:374
std::pair< ReceiveUpToResult, std::string > receiveUpto(FILE *file, char c, timeout_type timeout, bool failOnUnblockError)
Definition: IOTools.cc:85
constexpr std::string_view FILE("file")
unsigned diffFiles(const std::string &file1, const std::string &file2, std::string &out, int maxlines)
Definition: RpmDb.cc:184
Base class for Exception.
Definition: Exception.h:152
void setBackupPath(const Pathname &path)
set path where package backups are stored
Definition: RpmDb.cc:2239
const Pathname & root() const
Definition: RpmDb.h:109
bool hasConflicts(const std::string &tag_r) const
Return true if at least one package conflicts with a certain tag.
Definition: RpmDb.cc:1028
Pathname path() const
File containing the ASCII armored key.
Definition: PublicKey.cc:640
const Pathname & dbPath() const
Definition: RpmDb.h:117
static Date now()
Return the current time.
Definition: Date.h:78
std::string error_message
Error message from running rpm as external program.
Definition: RpmDb.h:344
std::string whoOwnsFile(const std::string &file_r) const
Return name of package owning file or empty string if no installed package owns file.
Definition: RpmDb.cc:982
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition: RpmDb.cc:1854
static bool globalInit()
Initialize lib librpm (read configfiles etc.).
Definition: librpmDb.cc:139
std::list< FileInfo > fileList(const std::string &name_r, const Edition &edition_r) const
return complete file list for installed package name_r (in FileInfo.filename) if edition_r != Edition...
Definition: RpmDb.cc:927
Typesafe passing of user data via callbacks.
Definition: UserData.h:39
std::string asString() const
Definition: PublicKey.cc:690
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:500
bool relative() const
Test for a relative path.
Definition: Pathname.h:121
value_type reportValue() const
Definition: progressdata.h:322
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:956
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Definition: HistoryLog.cc:190
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
void setBlocking(bool mode)
Set the blocking mode of the input stream.
CheckPackageResult
checkPackage result
Definition: RpmDb.h:376
bool regex_match(const char *s, smatch &matches, const regex &regex) ZYPP_API
Regular expression matching.
Definition: Regex.cc:80
int runposttrans(const Pathname &filename_r, const std::function< void(const std::string &)> &output_r)
Run collected posttrans and transfiletrigger(postun|in) if rpm --runposttrans is supported.
Definition: RpmDb.cc:2020
bool queryChangedFiles(FileList &fileList, const std::string &packageName)
determine which files of an installed package have been modified.
Definition: RpmDb.cc:1360
int _oldMask
Definition: RpmDb.cc:1135
std::set< std::string > FileList
Definition: RpmDb.h:370
FILE * inputFile() const
Return the input stream.
std::string & replaceAll(std::string &str_r, const std::string &from_r, const std::string &to_r)
Replace all occurrences of from_r with to_r in str_r (inplace).
Definition: String.cc:333
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
static Pathname suggestedDbPath(const Pathname &root_r)
Definition: librpmDb.cc:171
void run_rpm(const RpmArgVec &options, ExternalProgram::Stderr_Disposition stderr_disp=ExternalProgram::Stderr_To_Stdout)
Run rpm with the specified arguments and handle stderr.
Definition: RpmDb.cc:1425
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1098
static Pathname stripprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r with any root_r dir prefix striped.
Definition: Pathname.cc:281
export rpm trusted keys into zypp trusted keyring
Definition: RpmDb.h:279
db_const_iterator dbConstIterator() const
Definition: RpmDb.cc:268
bool initialized() const
Definition: RpmDb.h:125
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:65
#define DBG
Definition: Logger.h:102
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
Definition: Edition.h:72
Pathname _dbPath
Directory that contains the rpmdb.
Definition: RpmDb.h:91