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