1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
32 #include <zypp/base/Logger.h>
33 #include <zypp/base/String.h>
34 #include <zypp/base/Gettext.h>
35 #include <zypp/base/LocaleGuard.h>
36 #include <zypp/base/DtorReset.h>
38 #include <zypp/Date.h>
39 #include <zypp/Pathname.h>
40 #include <zypp/PathInfo.h>
41 #include <zypp/PublicKey.h>
42 #include <zypp/ProgressData.h>
44 #include <zypp/target/rpm/RpmDb.h>
45 #include <zypp/target/rpm/RpmCallbacks.h>
47 #include <zypp/HistoryLog.h>
48 #include <zypp/target/rpm/librpmDb.h>
49 #include <zypp/target/rpm/RpmException.h>
50 #include <zypp/TmpPath.h>
51 #include <zypp/KeyRing.h>
52 #include <zypp/ZYppFactory.h>
53 #include <zypp/ZConfig.h>
54 #include <zypp/base/IOTools.h>
57 using namespace zypp::filesystem;
59 #define WARNINGMAILPATH "/var/log/YaST2/"
60 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
61 #define MAXRPMMESSAGELINES 10000
63 #define WORKAROUNDRPMPWDBUG
65 #undef ZYPP_BASE_LOGGER_LOGGROUP
66 #define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
70 namespace zypp_readonly_hack
72 bool IGotIt(); // in readonly-mode
76 inline bool ZYPP_RPM_DEBUG()
78 static bool val = [](){
79 const char * env = getenv("ZYPP_RPM_DEBUG");
80 return( env && str::strToBool( env, true ) );
89 const callback::UserData::ContentType InstallResolvableReport::contentRpmout( "rpmout","installpkg" );
90 const callback::UserData::ContentType RemoveResolvableReport::contentRpmout( "rpmout","removepkg" );
94 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
95 const char* quoteInFilename_m = "\'\"";
97 const char* quoteInFilename_m = " \t\'\"";
99 inline std::string rpmQuoteFilename( const Pathname & path_r )
101 std::string path( path_r.asString() );
102 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
103 pos != std::string::npos;
104 pos = path.find_first_of( quoteInFilename_m, pos ) )
106 path.insert( pos, "\\" );
107 pos += 2; // skip '\\' and the quoted char.
113 /** Workaround bnc#827609 - rpm needs a readable pwd so we
114 * chdir to /. Turn realtive pathnames into absolute ones
115 * by prepending cwd so rpm still finds them
117 inline Pathname workaroundRpmPwdBug( Pathname path_r )
119 #if defined(WORKAROUNDRPMPWDBUG)
120 if ( path_r.relative() )
122 // try to prepend cwd
123 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
125 return Pathname( cwd ) / path_r;
126 WAR << "Can't get cwd!" << endl;
129 return path_r; // no problem with absolute pathnames
133 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
135 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
140 ~KeyRingSignalReceiver()
145 virtual void trustedKeyAdded( const PublicKey &key )
147 MIL << "trusted key added to zypp Keyring. Importing..." << endl;
148 _rpmdb.importPubkey( key );
151 virtual void trustedKeyRemoved( const PublicKey &key )
153 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
154 _rpmdb.removePubkey( key );
160 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
162 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
172 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
179 for (line = prog.receiveLine(), count=0;
181 line = prog.receiveLine(), count++ )
183 if (maxlines<0?true:count<maxlines)
192 /******************************************************************
195 ** FUNCTION NAME : stringPath
196 ** FUNCTION TYPE : inline std::string
198 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
200 return librpmDb::stringPath( root_r, sub_r );
203 ///////////////////////////////////////////////////////////////////
205 // CLASS NAME : RpmDb
207 ///////////////////////////////////////////////////////////////////
209 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
211 ///////////////////////////////////////////////////////////////////
213 ///////////////////////////////////////////////////////////////////
216 // METHOD NAME : RpmDb::RpmDb
217 // METHOD TYPE : Constructor
220 : _backuppath ("/var/adm/backup")
221 , _packagebackups(false)
225 librpmDb::globalInit();
226 // Some rpm versions are patched not to abort installation if
227 // symlink creation failed.
228 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
229 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
232 ///////////////////////////////////////////////////////////////////
235 // METHOD NAME : RpmDb::~RpmDb
236 // METHOD TYPE : Destructor
240 MIL << "~RpmDb()" << endl;
243 MIL << "~RpmDb() end" << endl;
244 sKeyRingReceiver.reset();
247 ///////////////////////////////////////////////////////////////////
250 // METHOD NAME : RpmDb::dumpOn
251 // METHOD TYPE : std::ostream &
253 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
255 return str << "RpmDb[" << stringPath( _root, _dbPath ) << "]";
258 ///////////////////////////////////////////////////////////////////
261 // METHOD NAME : RpmDb::initDatabase
262 // METHOD TYPE : PMError
264 void RpmDb::initDatabase( Pathname root_r, bool doRebuild_r )
266 ///////////////////////////////////////////////////////////////////
268 ///////////////////////////////////////////////////////////////////
269 bool quickinit( root_r.empty() );
271 if ( root_r.empty() )
274 // NOTE: Former argument, but now locked to "/var/lib/rpm".
275 // A custom dbPath is not actually needed and would only work
276 // reliably if libsolv also supports it. By now no further
277 // cleanup in the code.
278 const Pathname & dbPath_r { librpmDb::defaultDbPath() };
280 if ( ! root_r.absolute() )
282 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
283 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
286 if ( ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
287 && PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
289 WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
290 filesystem::symlink( "../../usr/lib/sysimage/rpm", root_r/"/var/lib/rpm" );
293 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
294 << ( doRebuild_r ? " (rebuilddb)" : "" )
295 << ( quickinit ? " (quickinit)" : "" ) << endl;
297 ///////////////////////////////////////////////////////////////////
298 // Check whether already initialized
299 ///////////////////////////////////////////////////////////////////
302 if ( root_r == _root && dbPath_r == _dbPath )
308 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
312 ///////////////////////////////////////////////////////////////////
314 ///////////////////////////////////////////////////////////////////
315 librpmDb::unblockAccess();
319 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
325 // creates dbdir and empty rpm database if not present
326 librpmDb::dbAccess( root_r );
328 catch (const RpmException & excpt_r)
330 ZYPP_CAUGHT(excpt_r);
331 librpmDb::blockAccess();
332 ZYPP_RETHROW(excpt_r);
341 MIL << "Synchronizing keys with zypp keyring" << endl;
344 // Close the database in case any write acces (create/convert)
345 // happened during init. This should drop any lock acquired
346 // by librpm. On demand it will be reopened readonly and should
347 // not hold any lock.
348 librpmDb::dbRelease( true );
350 MIL << "InitDatabase: " << *this << endl;
353 ///////////////////////////////////////////////////////////////////
356 // METHOD NAME : RpmDb::closeDatabase
357 // METHOD TYPE : PMError
359 void RpmDb::closeDatabase()
361 if ( ! initialized() )
366 MIL << "Calling closeDatabase: " << *this << endl;
368 ///////////////////////////////////////////////////////////////////
369 // Block further database access
370 ///////////////////////////////////////////////////////////////////
371 librpmDb::blockAccess();
373 ///////////////////////////////////////////////////////////////////
375 ///////////////////////////////////////////////////////////////////
376 _root = _dbPath = Pathname();
378 MIL << "closeDatabase: " << *this << endl;
381 ///////////////////////////////////////////////////////////////////
384 // METHOD NAME : RpmDb::rebuildDatabase
385 // METHOD TYPE : PMError
387 void RpmDb::rebuildDatabase()
389 callback::SendReport<RebuildDBReport> report;
391 report->start( root() + dbPath() );
395 doRebuildDatabase(report);
397 catch (RpmException & excpt_r)
399 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
400 ZYPP_RETHROW(excpt_r);
402 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
405 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
407 FAILIFNOTINITIALIZED;
408 MIL << "RpmDb::rebuildDatabase" << *this << endl;
410 const Pathname mydbpath { root()/dbPath() }; // the configured path used in reports
412 // For --rebuilddb take care we're using the real db directory
413 // and not a symlink. Otherwise rpm will rename the symlink and
414 // replace it with a real directory containing the converted db.
415 DtorReset guardRoot { _root };
416 DtorReset guardDbPath{ _dbPath };
418 _dbPath = filesystem::expandlink( mydbpath );
422 opts.push_back("--rebuilddb");
423 opts.push_back("-vv");
424 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
427 // generate and report progress
430 ProgressData::value_type hdrTotal = 0;
431 for ( librpmDb::db_const_iterator it; *it; ++it, ++hdrTotal )
433 tics.range( hdrTotal );
435 tics.sendTo( [&report,&mydbpath]( const ProgressData & tics_r ) -> bool {
436 return report->progress( tics_r.reportValue(), mydbpath );
442 while ( systemReadLine( line ) )
444 static const std::string debugPrefix { "D:" };
445 static const std::string progressPrefix { "D: read h#" };
446 static const std::string ignoreSuffix { "digest: OK" };
448 if ( ! str::startsWith( line, debugPrefix ) )
450 if ( ! str::endsWith( line, ignoreSuffix ) )
457 else if ( str::startsWith( line, progressPrefix ) )
461 WAR << "User requested abort." << endl;
467 if ( systemStatus() != 0 )
469 //TranslatorExplanation after semicolon is error message
470 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
478 ///////////////////////////////////////////////////////////////////
481 /** \ref RpmDb::syncTrustedKeys helper
482 * Compute which keys need to be exprted to / imported from the zypp keyring.
483 * Return result via argument list.
485 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
487 ///////////////////////////////////////////////////////////////////
488 // Remember latest release and where it ocurred
492 : _inRpmKeys( nullptr )
493 , _inZyppKeys( nullptr )
496 void updateIf( const Edition & rpmKey_r )
498 std::string keyRelease( rpmKey_r.release() );
499 int comp = _release.compare( keyRelease );
502 // update to newer release
503 _release.swap( keyRelease );
504 _inRpmKeys = &rpmKey_r;
505 _inZyppKeys = nullptr;
506 if ( !keyRelease.empty() )
507 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
509 else if ( comp == 0 )
511 // stay with this release
513 _inRpmKeys = &rpmKey_r;
515 // else: this is an old release
517 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
520 void updateIf( const PublicKeyData & zyppKey_r )
522 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
523 int comp = _release.compare( keyRelease );
526 // update to newer release
527 _release.swap( keyRelease );
528 _inRpmKeys = nullptr;
529 _inZyppKeys = &zyppKey_r;
530 if ( !keyRelease.empty() )
531 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
533 else if ( comp == 0 )
535 // stay with this release
537 _inZyppKeys = &zyppKey_r;
539 // else: this is an old release
541 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
544 std::string _release;
545 const Edition * _inRpmKeys;
546 const PublicKeyData * _inZyppKeys;
548 ///////////////////////////////////////////////////////////////////
550 // collect keys by ID(version) and latest creation(release)
551 std::map<std::string,Key> _keymap;
553 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
555 _keymap[(*it).version()].updateIf( *it );
558 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
560 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
563 // compute missing keys
564 std::set<Edition> rpmKeys;
565 std::list<PublicKeyData> zyppKeys;
566 for_( it, _keymap.begin(), _keymap.end() )
568 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
569 << ( (*it).second._inRpmKeys ? "R" : "_" )
570 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
571 if ( ! (*it).second._inRpmKeys )
573 zyppKeys.push_back( *(*it).second._inZyppKeys );
575 if ( ! (*it).second._inZyppKeys )
577 rpmKeys.insert( *(*it).second._inRpmKeys );
580 rpmKeys_r.swap( rpmKeys );
581 zyppKeys_r.swap( zyppKeys );
584 ///////////////////////////////////////////////////////////////////
586 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
588 MIL << "Going to sync trusted keys..." << endl;
589 std::set<Edition> rpmKeys( pubkeyEditions() );
590 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
592 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
594 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
595 // when re-acquiring the zyppp lock. For now we remove all excess keys.
596 // TODO: Once we can safely assume that all PK versions are updated we
597 // can think about re-importing newer key versions found in the zypp keyring and
598 // removing only excess ones (but case is not very likely). Unfixed PK versions
599 // however will remove the newer version found in the zypp keyring and by doing
600 // this, the key here will be removed via callback as well (keys are deleted
601 // via gpg id, regardless of the edition).
602 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
603 // Temporarily disconnect to prevent the attempt to pass back the delete request.
604 callback::TempConnect<KeyRingSignals> tempDisconnect;
606 for ( const PublicKeyData & keyData : zyppKeys )
608 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
610 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
611 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
612 if ( !dirty ) dirty = true;
616 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
619 computeKeyRingSync( rpmKeys, zyppKeys );
620 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
621 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
623 ///////////////////////////////////////////////////////////////////
624 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
626 // export to zypp keyring
627 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
628 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
629 callback::TempConnect<KeyRingSignals> tempDisconnect;
630 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
632 TmpFile tmpfile( getZYpp()->tmpPath() );
634 std::ofstream tmpos( tmpfile.path().c_str() );
635 for_( it, rpmKeys.begin(), rpmKeys.end() )
637 // we export the rpm key into a file
638 RpmHeader::constPtr result;
639 getData( "gpg-pubkey", *it, result );
640 tmpos << result->tag_description() << endl;
645 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
646 // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
647 // Modern rpm does not import those keys, but when migrating a pre SLE12 system
648 // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
649 std::set<Edition> missingKeys;
650 for ( const Edition & key : rpmKeys )
652 if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
654 ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
655 missingKeys.insert( key );
657 if ( ! missingKeys.empty() )
658 callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
660 catch ( const Exception & excpt )
662 ZYPP_CAUGHT( excpt );
663 ERR << "Could not import keys into zypp keyring: " << endl;
667 ///////////////////////////////////////////////////////////////////
668 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
670 // import from zypp keyring
671 MIL << "Importing zypp trusted keyring" << std::endl;
672 for_( it, zyppKeys.begin(), zyppKeys.end() )
676 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
678 catch ( const RpmException & exp )
684 MIL << "Trusted keys synced." << endl;
687 void RpmDb::importZyppKeyRingTrustedKeys()
688 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
690 void RpmDb::exportTrustedKeysInZyppKeyRing()
691 { syncTrustedKeys( SYNC_TO_KEYRING ); }
693 ///////////////////////////////////////////////////////////////////
696 // METHOD NAME : RpmDb::importPubkey
697 // METHOD TYPE : PMError
699 void RpmDb::importPubkey( const PublicKey & pubkey_r )
701 FAILIFNOTINITIALIZED;
703 // bnc#828672: On the fly key import in READONLY
704 if ( zypp_readonly_hack::IGotIt() )
706 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
710 // check if the key is already in the rpm database
711 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
712 std::set<Edition> rpmKeys = pubkeyEditions();
713 bool hasOldkeys = false;
715 for_( it, rpmKeys.begin(), rpmKeys.end() )
717 // bsc#1008325: Keys using subkeys for signing don't get a higher release
718 // if new subkeys are added, because the primary key remains unchanged.
719 // For now always re-import keys with subkeys. Here we don't want to export the
720 // keys in the rpm database to check whether the subkeys are the same. The calling
721 // code should take care, we don't re-import the same kesy over and over again.
722 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
724 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
728 if ( keyEd.version() != (*it).version() )
729 continue; // different key ID (version)
731 if ( keyEd.release() < (*it).release() )
733 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
741 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
745 // We must explicitly delete old key IDs first (all releases,
746 // that's why we don't call removePubkey here).
747 std::string keyName( "gpg-pubkey-" + keyEd.version() );
749 opts.push_back ( "-e" );
750 opts.push_back ( "--allmatches" );
751 opts.push_back ( "--" );
752 opts.push_back ( keyName.c_str() );
753 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
756 while ( systemReadLine( line ) )
758 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
761 if ( systemStatus() != 0 )
763 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
767 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
771 // import the new key
773 opts.push_back ( "--import" );
774 opts.push_back ( "--" );
775 std::string pubkeypath( pubkey_r.path().asString() );
776 opts.push_back ( pubkeypath.c_str() );
777 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
780 std::vector<std::string> excplines;
781 while ( systemReadLine( line ) )
783 if ( str::startsWith( line, "error:" ) )
786 excplines.push_back( std::move(line) );
792 if ( systemStatus() != 0 )
794 // Translator: %1% is a gpg public key
795 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
796 excp.moveToHistory( excplines );
797 excp.addHistory( std::move(error_message) );
798 ZYPP_THROW( std::move(excp) );
802 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
806 ///////////////////////////////////////////////////////////////////
809 // METHOD NAME : RpmDb::removePubkey
810 // METHOD TYPE : PMError
812 void RpmDb::removePubkey( const PublicKey & pubkey_r )
814 FAILIFNOTINITIALIZED;
816 // check if the key is in the rpm database and just
817 // return if it does not.
818 std::set<Edition> rpm_keys = pubkeyEditions();
819 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
820 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
822 for_( it, rpm_keys.begin(), rpm_keys.end() )
824 if ( (*it).version() == pubkeyVersion )
831 // the key does not exist, cannot be removed
832 if (found_edition == rpm_keys.end())
834 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
838 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
841 opts.push_back ( "-e" );
842 opts.push_back ( "--" );
843 opts.push_back ( rpm_name.c_str() );
844 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
847 std::vector<std::string> excplines;
848 while ( systemReadLine( line ) )
850 if ( str::startsWith( line, "error:" ) )
853 excplines.push_back( std::move(line) );
859 if ( systemStatus() != 0 )
861 // Translator: %1% is a gpg public key
862 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
863 excp.moveToHistory( excplines );
864 excp.addHistory( std::move(error_message) );
865 ZYPP_THROW( std::move(excp) );
869 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
873 ///////////////////////////////////////////////////////////////////
876 // METHOD NAME : RpmDb::pubkeys
877 // METHOD TYPE : std::set<Edition>
879 std::list<PublicKey> RpmDb::pubkeys() const
881 std::list<PublicKey> ret;
883 librpmDb::db_const_iterator it;
884 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
886 Edition edition = it->tag_edition();
887 if (edition != Edition::noedition)
889 // we export the rpm key into a file
890 RpmHeader::constPtr result;
891 getData( "gpg-pubkey", edition, result );
892 TmpFile file(getZYpp()->tmpPath());
896 os.open(file.path().asString().c_str());
897 // dump rpm key into the tmp file
898 os << result->tag_description();
899 //MIL << "-----------------------------------------------" << endl;
900 //MIL << result->tag_description() <<endl;
901 //MIL << "-----------------------------------------------" << endl;
903 // read the public key from the dumped file
907 catch ( std::exception & e )
909 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
910 // just ignore the key
917 std::set<Edition> RpmDb::pubkeyEditions() const
919 std::set<Edition> ret;
921 librpmDb::db_const_iterator it;
922 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
924 Edition edition = it->tag_edition();
925 if (edition != Edition::noedition)
926 ret.insert( edition );
932 ///////////////////////////////////////////////////////////////////
935 // METHOD NAME : RpmDb::fileList
936 // METHOD TYPE : bool
941 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
943 std::list<FileInfo> result;
945 librpmDb::db_const_iterator it;
947 if (edition_r == Edition::noedition)
949 found = it.findPackage( name_r );
953 found = it.findPackage( name_r, edition_r );
962 ///////////////////////////////////////////////////////////////////
965 // METHOD NAME : RpmDb::hasFile
966 // METHOD TYPE : bool
970 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
972 librpmDb::db_const_iterator it;
976 res = it.findByFile( file_r );
980 res = (it->tag_name() == name_r);
988 ///////////////////////////////////////////////////////////////////
991 // METHOD NAME : RpmDb::whoOwnsFile
992 // METHOD TYPE : std::string
996 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
998 librpmDb::db_const_iterator it;
999 if (it.findByFile( file_r ))
1001 return it->tag_name();
1006 ///////////////////////////////////////////////////////////////////
1009 // METHOD NAME : RpmDb::hasProvides
1010 // METHOD TYPE : bool
1014 bool RpmDb::hasProvides( const std::string & tag_r ) const
1016 librpmDb::db_const_iterator it;
1017 return it.findByProvides( tag_r );
1020 ///////////////////////////////////////////////////////////////////
1023 // METHOD NAME : RpmDb::hasRequiredBy
1024 // METHOD TYPE : bool
1028 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1030 librpmDb::db_const_iterator it;
1031 return it.findByRequiredBy( tag_r );
1034 ///////////////////////////////////////////////////////////////////
1037 // METHOD NAME : RpmDb::hasConflicts
1038 // METHOD TYPE : bool
1042 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1044 librpmDb::db_const_iterator it;
1045 return it.findByConflicts( tag_r );
1048 ///////////////////////////////////////////////////////////////////
1051 // METHOD NAME : RpmDb::hasPackage
1052 // METHOD TYPE : bool
1056 bool RpmDb::hasPackage( const std::string & name_r ) const
1058 librpmDb::db_const_iterator it;
1059 return it.findPackage( name_r );
1062 ///////////////////////////////////////////////////////////////////
1065 // METHOD NAME : RpmDb::hasPackage
1066 // METHOD TYPE : bool
1070 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1072 librpmDb::db_const_iterator it;
1073 return it.findPackage( name_r, ed_r );
1076 ///////////////////////////////////////////////////////////////////
1079 // METHOD NAME : RpmDb::getData
1080 // METHOD TYPE : PMError
1084 void RpmDb::getData( const std::string & name_r,
1085 RpmHeader::constPtr & result_r ) const
1087 librpmDb::db_const_iterator it;
1088 it.findPackage( name_r );
1091 ZYPP_THROW(*(it.dbError()));
1094 ///////////////////////////////////////////////////////////////////
1097 // METHOD NAME : RpmDb::getData
1098 // METHOD TYPE : void
1102 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1103 RpmHeader::constPtr & result_r ) const
1105 librpmDb::db_const_iterator it;
1106 it.findPackage( name_r, ed_r );
1109 ZYPP_THROW(*(it.dbError()));
1112 ///////////////////////////////////////////////////////////////////
1115 struct RpmlogCapture : public std::string
1118 { rpmlog()._cap = this; }
1121 { rpmlog()._cap = nullptr; }
1129 rpmlogSetCallback( rpmLogCB, this );
1130 rpmSetVerbosity( RPMLOG_INFO );
1131 _f = ::fopen( "/dev/null","w");
1132 rpmlogSetFile( _f );
1136 { if ( _f ) ::fclose( _f ); }
1138 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1139 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1141 int rpmLog( rpmlogRec rec_r )
1143 if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1144 return RPMLOG_DEFAULT;
1151 static Rpmlog & rpmlog()
1152 { static Rpmlog _rpmlog; return _rpmlog; }
1155 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1156 const Pathname & root_r, // target root
1157 bool requireGPGSig_r, // whether no gpg signature is to be reported
1158 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1160 PathInfo file( path_r );
1161 if ( ! file.isFile() )
1163 ERR << "Not a file: " << file << endl;
1164 return RpmDb::CHK_ERROR;
1167 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1168 if ( fd == 0 || ::Ferror(fd) )
1170 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1173 return RpmDb::CHK_ERROR;
1175 rpmts ts = ::rpmtsCreate();
1176 ::rpmtsSetRootDir( ts, root_r.c_str() );
1177 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1179 rpmQVKArguments_s qva;
1180 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1181 #ifdef HAVE_NO_RPMTSSETVFYFLAGS
1182 // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
1183 // and qva_flags is not used in signature checking at all.
1184 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1186 ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
1188 RpmlogCapture vresult;
1189 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1190 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1196 // results per line...
1197 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1198 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1199 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1200 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1202 // TODO: try to get SIG info from the header rather than parsing the output
1203 std::vector<std::string> lines;
1204 str::split( vresult, std::back_inserter(lines), "\n" );
1205 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1207 for ( unsigned i = 1; i < lines.size(); ++i )
1209 std::string & line( lines[i] );
1210 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1211 if ( line.find( ": OK" ) != std::string::npos )
1213 lineres = RpmDb::CHK_OK;
1214 if ( line.find( "Signature, key ID" ) == std::string::npos )
1215 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1217 else if ( line.find( ": NOKEY" ) != std::string::npos )
1218 { lineres = RpmDb::CHK_NOKEY; }
1219 else if ( line.find( ": BAD" ) != std::string::npos )
1220 { lineres = RpmDb::CHK_FAIL; }
1221 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1222 { lineres = RpmDb::CHK_NOTFOUND; }
1223 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1224 { lineres = RpmDb::CHK_NOTTRUSTED; }
1225 else if ( line.find( ": NOTFOUND" ) != std::string::npos )
1226 { continue; } // just collect details for signatures found (#229)
1229 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1232 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1234 if ( count[RpmDb::CHK_FAIL] )
1235 ret = RpmDb::CHK_FAIL;
1237 else if ( count[RpmDb::CHK_NOTFOUND] )
1238 ret = RpmDb::CHK_NOTFOUND;
1240 else if ( count[RpmDb::CHK_NOKEY] )
1241 ret = RpmDb::CHK_NOKEY;
1243 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1244 ret = RpmDb::CHK_NOTTRUSTED;
1246 else if ( ret == RpmDb::CHK_OK )
1248 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1250 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1251 if ( requireGPGSig_r )
1252 ret = RpmDb::CHK_NOSIG;
1256 if ( ret != RpmDb::CHK_OK )
1258 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1265 ///////////////////////////////////////////////////////////////////
1267 // METHOD NAME : RpmDb::checkPackage
1268 // METHOD TYPE : RpmDb::CheckPackageResult
1270 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1271 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1273 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1274 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1276 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1277 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1280 // determine changed files of installed package
1282 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1288 if ( ! initialized() ) return false;
1292 opts.push_back ("-V");
1293 opts.push_back ("--nodeps");
1294 opts.push_back ("--noscripts");
1295 opts.push_back ("--nomd5");
1296 opts.push_back ("--");
1297 opts.push_back (packageName.c_str());
1299 run_rpm (opts, ExternalProgram::Discard_Stderr);
1301 if ( process == NULL )
1312 M Mode (includes permissions and file type)
1316 while (systemReadLine(line))
1318 if (line.length() > 12 &&
1319 (line[0] == 'S' || line[0] == 's' ||
1320 (line[0] == '.' && line[7] == 'T')))
1322 // file has been changed
1323 std::string filename;
1325 filename.assign(line, 11, line.length() - 11);
1326 fileList.insert(filename);
1331 // exit code ignored, rpm returns 1 no matter if package is installed or
1339 /****************************************************************/
1340 /* private member-functions */
1341 /****************************************************************/
1343 /*--------------------------------------------------------------*/
1344 /* Run rpm with the specified arguments, handling stderr */
1345 /* as specified by disp */
1346 /*--------------------------------------------------------------*/
1348 RpmDb::run_rpm (const RpmArgVec& opts,
1349 ExternalProgram::Stderr_Disposition disp)
1358 if ( ! initialized() )
1360 ZYPP_THROW(RpmDbNotOpenException());
1365 // always set root and dbpath
1366 #if defined(WORKAROUNDRPMPWDBUG)
1367 args.push_back("#/"); // chdir to / to workaround bnc#819354
1369 args.push_back("rpm");
1370 args.push_back("--root");
1371 args.push_back(_root.asString().c_str());
1372 args.push_back("--dbpath");
1373 args.push_back(_dbPath.asString().c_str());
1374 if ( env::ZYPP_RPM_DEBUG() )
1375 args.push_back("-vv");
1376 const char* argv[args.size() + opts.size() + 1];
1378 const char** p = argv;
1379 p = copy (args.begin (), args.end (), p);
1380 p = copy (opts.begin (), opts.end (), p);
1383 // Invalidate all outstanding database handles in case
1384 // the database gets modified.
1385 librpmDb::dbRelease( true );
1387 // Launch the program with default locale
1388 process = new ExternalProgram(argv, disp, false, -1, true);
1392 /*--------------------------------------------------------------*/
1393 /* Read a line from the rpm process */
1394 /*--------------------------------------------------------------*/
1395 bool RpmDb::systemReadLine( std::string & line )
1399 if ( process == NULL )
1402 if ( process->inputFile() )
1404 process->setBlocking( false );
1405 FILE * inputfile = process->inputFile();
1407 // Check every 5 seconds if the process is still running to prevent against
1408 // daemons launched in rpm %post that do not close their filedescriptors,
1409 // causing us to block for infinity. (bnc#174548)
1410 const auto &readResult = io::receiveUpto( inputfile, '\n', 5 * 1000, false );
1411 switch ( readResult.first ) {
1412 case io::ReceiveUpToResult::Timeout: {
1413 if ( !process->running() )
1416 // we might have received a partial line, lets not forget about it
1417 line += readResult.second;
1419 case io::ReceiveUpToResult::Error:
1420 case io::ReceiveUpToResult::EndOfFile: {
1421 line += readResult.second;
1422 if ( line.size() && line.back() == '\n')
1424 return line.size(); // in case of pending output
1426 case io::ReceiveUpToResult::Success: {
1427 line += readResult.second;
1429 if ( line.size() && line.back() == '\n')
1432 if ( env::ZYPP_RPM_DEBUG() )
1433 L_DBG("RPM_DEBUG") << line << endl;
1434 return true; // complete line
1442 /*--------------------------------------------------------------*/
1443 /* Return the exit status of the rpm process, closing the */
1444 /* connection if not already done */
1445 /*--------------------------------------------------------------*/
1447 RpmDb::systemStatus()
1449 if ( process == NULL )
1452 exit_code = process->close();
1456 error_message = process->execError();
1461 // DBG << "exit code " << exit_code << endl;
1466 /*--------------------------------------------------------------*/
1467 /* Forcably kill the rpm process */
1468 /*--------------------------------------------------------------*/
1472 if (process) process->kill();
1476 // generate diff mails for config files
1477 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1479 std::string msg = line.substr(9);
1480 std::string::size_type pos1 = std::string::npos;
1481 std::string::size_type pos2 = std::string::npos;
1482 std::string file1s, file2s;
1486 pos1 = msg.find (typemsg);
1489 if ( pos1 == std::string::npos )
1492 pos2 = pos1 + strlen (typemsg);
1494 if (pos2 >= msg.length() )
1497 file1 = msg.substr (0, pos1);
1498 file2 = msg.substr (pos2);
1500 file1s = file1.asString();
1501 file2s = file2.asString();
1503 if (!_root.empty() && _root != "/")
1505 file1 = _root + file1;
1506 file2 = _root + file2;
1510 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1513 Pathname file = _root + WARNINGMAILPATH;
1514 if (filesystem::assert_dir(file) != 0)
1516 ERR << "Could not create " << file.asString() << endl;
1519 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1520 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1523 ERR << "Could not open " << file << endl;
1527 // Translator: %s = name of an rpm package. A list of diffs follows
1529 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1532 ERR << "diff failed" << endl;
1533 notify << str::form(difffailmsg,
1534 file1s.c_str(), file2s.c_str()) << endl;
1538 notify << str::form(diffgenmsg,
1539 file1s.c_str(), file2s.c_str()) << endl;
1541 // remove root for the viewer's pleasure (#38240)
1542 if (!_root.empty() && _root != "/")
1544 if (out.substr(0,4) == "--- ")
1546 out.replace(4, file1.asString().length(), file1s);
1548 std::string::size_type pos = out.find("\n+++ ");
1549 if (pos != std::string::npos)
1551 out.replace(pos+5, file2.asString().length(), file2s);
1554 notify << out << endl;
1557 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1562 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1568 ///////////////////////////////////////////////////////////////////
1571 // METHOD NAME : RpmDb::installPackage
1572 // METHOD TYPE : PMError
1574 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1576 callback::SendReport<RpmInstallReport> report;
1578 report->start(filename);
1583 doInstallPackage(filename, flags, report);
1587 catch (RpmException & excpt_r)
1589 RpmInstallReport::Action user = report->problem( excpt_r );
1591 if ( user == RpmInstallReport::ABORT )
1593 report->finish( excpt_r );
1594 ZYPP_RETHROW(excpt_r);
1596 else if ( user == RpmInstallReport::IGNORE )
1604 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1606 FAILIFNOTINITIALIZED;
1607 HistoryLog historylog;
1609 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1613 if ( _packagebackups )
1615 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1616 if ( ! backupPackage( filename ) )
1618 ERR << "backup of " << filename.asString() << " failed" << endl;
1620 // FIXME status handling
1621 report->progress( 0 ); // allow 1% for backup creation.
1626 if (flags & RPMINST_NOUPGRADE)
1627 opts.push_back("-i");
1629 opts.push_back("-U");
1631 opts.push_back("--percent");
1632 opts.push_back("--noglob");
1634 // ZConfig defines cross-arch installation
1635 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1636 opts.push_back("--ignorearch");
1638 if (flags & RPMINST_NODIGEST)
1639 opts.push_back("--nodigest");
1640 if (flags & RPMINST_NOSIGNATURE)
1641 opts.push_back("--nosignature");
1642 if (flags & RPMINST_EXCLUDEDOCS)
1643 opts.push_back ("--excludedocs");
1644 if (flags & RPMINST_NOSCRIPTS)
1645 opts.push_back ("--noscripts");
1646 if (flags & RPMINST_FORCE)
1647 opts.push_back ("--force");
1648 if (flags & RPMINST_NODEPS)
1649 opts.push_back ("--nodeps");
1650 if (flags & RPMINST_IGNORESIZE)
1651 opts.push_back ("--ignoresize");
1652 if (flags & RPMINST_JUSTDB)
1653 opts.push_back ("--justdb");
1654 if (flags & RPMINST_TEST)
1655 opts.push_back ("--test");
1656 if (flags & RPMINST_NOPOSTTRANS)
1657 opts.push_back ("--noposttrans");
1659 opts.push_back("--");
1661 // rpm requires additional quoting of special chars:
1662 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1663 opts.push_back ( quotedFilename.c_str() );
1664 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1666 // forward additional rpm output via report;
1668 unsigned lineno = 0;
1669 callback::UserData cmdout( InstallResolvableReport::contentRpmout );
1670 // Key "solvable" injected by RpmInstallPackageReceiver
1671 cmdout.set( "line", std::cref(line) );
1672 cmdout.set( "lineno", lineno );
1674 // LEGACY: collect and forward additional rpm output in finish
1675 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1676 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
1678 while ( systemReadLine( line ) )
1680 if ( str::startsWith( line, "%%" ) )
1683 sscanf( line.c_str() + 2, "%d", &percent );
1684 report->progress( percent );
1688 cmdout.set( "lineno", lineno );
1689 report->report( cmdout );
1691 if ( lineno >= MAXRPMMESSAGELINES ) {
1692 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1696 rpmmsg += line+'\n';
1698 if ( str::startsWith( line, "warning:" ) )
1699 configwarnings.push_back(line);
1701 if ( lineno >= MAXRPMMESSAGELINES )
1702 rpmmsg += "[truncated]\n";
1704 int rpm_status = systemStatus();
1707 for (std::vector<std::string>::iterator it = configwarnings.begin();
1708 it != configwarnings.end(); ++it)
1710 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1712 _("rpm saved %s as %s, but it was impossible to determine the difference"),
1714 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1715 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1717 _("rpm created %s as %s, but it was impossible to determine the difference"),
1719 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1722 if ( rpm_status != 0 )
1725 str::form("%s install failed", Pathname::basename(filename).c_str()),
1726 true /*timestamp*/);
1727 std::ostringstream sstr;
1728 sstr << "rpm output:" << endl << rpmmsg << endl;
1729 historylog.comment(sstr.str());
1730 // TranslatorExplanation the colon is followed by an error message
1731 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
1733 else if ( ! rpmmsg.empty() )
1736 str::form("%s installed ok", Pathname::basename(filename).c_str()),
1737 true /*timestamp*/);
1738 std::ostringstream sstr;
1739 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1740 historylog.comment(sstr.str());
1742 // report additional rpm output in finish
1743 // TranslatorExplanation Text is followed by a ':' and the actual output.
1744 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1748 ///////////////////////////////////////////////////////////////////
1751 // METHOD NAME : RpmDb::removePackage
1752 // METHOD TYPE : PMError
1754 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
1756 // 'rpm -e' does not like epochs
1757 return removePackage( package->name()
1758 + "-" + package->edition().version()
1759 + "-" + package->edition().release()
1760 + "." + package->arch().asString(), flags );
1763 ///////////////////////////////////////////////////////////////////
1766 // METHOD NAME : RpmDb::removePackage
1767 // METHOD TYPE : PMError
1769 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
1771 callback::SendReport<RpmRemoveReport> report;
1773 report->start( name_r );
1778 doRemovePackage(name_r, flags, report);
1782 catch (RpmException & excpt_r)
1784 RpmRemoveReport::Action user = report->problem( excpt_r );
1786 if ( user == RpmRemoveReport::ABORT )
1788 report->finish( excpt_r );
1789 ZYPP_RETHROW(excpt_r);
1791 else if ( user == RpmRemoveReport::IGNORE )
1800 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
1802 FAILIFNOTINITIALIZED;
1803 HistoryLog historylog;
1805 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1808 if ( _packagebackups )
1810 // FIXME solve this status report somehow
1811 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1812 if ( ! backupPackage( name_r ) )
1814 ERR << "backup of " << name_r << " failed" << endl;
1816 report->progress( 0 );
1820 report->progress( 100 );
1825 opts.push_back("-e");
1826 opts.push_back("--allmatches");
1828 if (flags & RPMINST_NOSCRIPTS)
1829 opts.push_back("--noscripts");
1830 if (flags & RPMINST_NODEPS)
1831 opts.push_back("--nodeps");
1832 if (flags & RPMINST_JUSTDB)
1833 opts.push_back("--justdb");
1834 if (flags & RPMINST_TEST)
1835 opts.push_back ("--test");
1836 if (flags & RPMINST_FORCE)
1838 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1841 opts.push_back("--");
1842 opts.push_back(name_r.c_str());
1843 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1845 // forward additional rpm output via report;
1847 unsigned lineno = 0;
1848 callback::UserData cmdout( RemoveResolvableReport::contentRpmout );
1849 // Key "solvable" injected by RpmInstallPackageReceiver
1850 cmdout.set( "line", std::cref(line) );
1851 cmdout.set( "lineno", lineno );
1854 // LEGACY: collect and forward additional rpm output in finish
1855 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1857 // got no progress from command, so we fake it:
1858 // 5 - command started
1859 // 50 - command completed
1861 report->progress( 5 );
1862 while (systemReadLine(line))
1865 cmdout.set( "lineno", lineno );
1866 report->report( cmdout );
1868 if ( lineno >= MAXRPMMESSAGELINES ) {
1869 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1872 rpmmsg += line+'\n';
1874 if ( lineno >= MAXRPMMESSAGELINES )
1875 rpmmsg += "[truncated]\n";
1876 report->progress( 50 );
1877 int rpm_status = systemStatus();
1879 if ( rpm_status != 0 )
1882 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
1883 std::ostringstream sstr;
1884 sstr << "rpm output:" << endl << rpmmsg << endl;
1885 historylog.comment(sstr.str());
1886 // TranslatorExplanation the colon is followed by an error message
1887 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
1889 else if ( ! rpmmsg.empty() )
1892 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
1894 std::ostringstream sstr;
1895 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1896 historylog.comment(sstr.str());
1898 // report additional rpm output in finish
1899 // TranslatorExplanation Text is followed by a ':' and the actual output.
1900 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1904 ///////////////////////////////////////////////////////////////////
1907 // METHOD NAME : RpmDb::backupPackage
1908 // METHOD TYPE : bool
1910 bool RpmDb::backupPackage( const Pathname & filename )
1912 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1916 return backupPackage( h->tag_name() );
1919 ///////////////////////////////////////////////////////////////////
1922 // METHOD NAME : RpmDb::backupPackage
1923 // METHOD TYPE : bool
1925 bool RpmDb::backupPackage(const std::string& packageName)
1927 HistoryLog progresslog;
1929 Pathname backupFilename;
1930 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1932 if (_backuppath.empty())
1934 INT << "_backuppath empty" << endl;
1940 if (!queryChangedFiles(fileList, packageName))
1942 ERR << "Error while getting changed files for package " <<
1943 packageName << endl;
1947 if (fileList.size() <= 0)
1949 DBG << "package " << packageName << " not changed -> no backup" << endl;
1953 if (filesystem::assert_dir(_root + _backuppath) != 0)
1959 // build up archive name
1960 time_t currentTime = time(0);
1961 struct tm *currentLocalTime = localtime(¤tTime);
1963 int date = (currentLocalTime->tm_year + 1900) * 10000
1964 + (currentLocalTime->tm_mon + 1) * 100
1965 + currentLocalTime->tm_mday;
1970 backupFilename = _root + _backuppath
1971 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1974 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1976 PathInfo pi(filestobackupfile);
1977 if (pi.isExist() && !pi.isFile())
1979 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1983 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1987 ERR << "could not open " << filestobackupfile.asString() << endl;
1991 for (FileList::const_iterator cit = fileList.begin();
1992 cit != fileList.end(); ++cit)
1994 std::string name = *cit;
1995 if ( name[0] == '/' )
1997 // remove slash, file must be relative to -C parameter of tar
1998 name = name.substr( 1 );
2000 DBG << "saving file "<< name << endl;
2005 const char* const argv[] =
2010 _root.asString().c_str(),
2011 "--ignore-failed-read",
2013 backupFilename.asString().c_str(),
2015 filestobackupfile.asString().c_str(),
2019 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2020 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2024 // TODO: its probably possible to start tar with -v and watch it adding
2025 // files to report progress
2026 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2031 int ret = tar.close();
2035 ERR << "tar failed: " << tarmsg << endl;
2040 MIL << "tar backup ok" << endl;
2041 progresslog.comment(
2042 str::form(_("created backup %s"), backupFilename.asString().c_str())
2043 , /*timestamp*/true);
2046 filesystem::unlink(filestobackupfile);
2052 void RpmDb::setBackupPath(const Pathname& path)
2057 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2061 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2062 // translators: possible rpm package signature check result [brief]
2063 OUTS( CHK_OK, _("Signature is OK") );
2064 // translators: possible rpm package signature check result [brief]
2065 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2066 // translators: possible rpm package signature check result [brief]
2067 OUTS( CHK_FAIL, _("Signature does not verify") );
2068 // translators: possible rpm package signature check result [brief]
2069 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2070 // translators: possible rpm package signature check result [brief]
2071 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2072 // translators: possible rpm package signature check result [brief]
2073 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2074 // translators: possible rpm package signature check result [brief]
2075 OUTS( CHK_NOSIG, _("File is unsigned") );
2078 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2081 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2083 for ( const auto & el : obj )
2084 str << el.second << endl;
2089 } // namespace target