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