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