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 const Pathname & dbPath_r { librpmDb::suggestedDbPath( root_r ) }; // also asserts root_r is absolute
276 // The rpmdb compat symlink.
277 // Required at least until rpmdb2solv takes a dppath argument.
278 // Otherwise it creates a db at "/var/lib/rpm".
279 if ( dbPath_r != "/var/lib/rpm" && ! PathInfo( root_r/"/var/lib/rpm" ).isExist() )
281 WAR << "Inject missing /var/lib/rpm compat symlink to " << dbPath_r << endl;
282 filesystem::assert_dir( root_r/"/var/lib" );
283 filesystem::symlink( "../../"/dbPath_r, root_r/"/var/lib/rpm" );
286 ///////////////////////////////////////////////////////////////////
287 // Check whether already initialized
288 ///////////////////////////////////////////////////////////////////
291 // Just check for a changing root because the librpmDb::suggestedDbPath
292 // may indeed change: rpm %post moving the db from /var/lib/rpm
293 // to /usr/lib/sysimage/rpm. We continue to use the old dbpath
294 // (via the compat symlink) until a re-init.
295 if ( root_r == _root ) {
296 MIL << "Calling initDatabase: already initialized at " << stringPath( _root, _dbPath ) << endl;
300 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
303 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
304 << ( doRebuild_r ? " (rebuilddb)" : "" )
305 << ( quickinit ? " (quickinit)" : "" ) << endl;
307 ///////////////////////////////////////////////////////////////////
309 ///////////////////////////////////////////////////////////////////
310 librpmDb::unblockAccess();
314 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
320 // creates dbdir and empty rpm database if not present
321 librpmDb::dbAccess( root_r );
323 catch (const RpmException & excpt_r)
325 ZYPP_CAUGHT(excpt_r);
326 librpmDb::blockAccess();
327 ZYPP_RETHROW(excpt_r);
336 MIL << "Synchronizing keys with zypp keyring" << endl;
339 // Close the database in case any write acces (create/convert)
340 // happened during init. This should drop any lock acquired
341 // by librpm. On demand it will be reopened readonly and should
342 // not hold any lock.
343 librpmDb::dbRelease( true );
345 MIL << "InitDatabase: " << *this << endl;
348 ///////////////////////////////////////////////////////////////////
351 // METHOD NAME : RpmDb::closeDatabase
352 // METHOD TYPE : PMError
354 void RpmDb::closeDatabase()
356 if ( ! initialized() )
361 MIL << "Calling closeDatabase: " << *this << endl;
363 ///////////////////////////////////////////////////////////////////
364 // Block further database access
365 ///////////////////////////////////////////////////////////////////
366 librpmDb::blockAccess();
368 ///////////////////////////////////////////////////////////////////
370 ///////////////////////////////////////////////////////////////////
371 _root = _dbPath = Pathname();
373 MIL << "closeDatabase: " << *this << endl;
376 ///////////////////////////////////////////////////////////////////
379 // METHOD NAME : RpmDb::rebuildDatabase
380 // METHOD TYPE : PMError
382 void RpmDb::rebuildDatabase()
384 callback::SendReport<RebuildDBReport> report;
386 report->start( root() + dbPath() );
390 doRebuildDatabase(report);
392 catch (RpmException & excpt_r)
394 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
395 ZYPP_RETHROW(excpt_r);
397 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
400 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
402 FAILIFNOTINITIALIZED;
403 MIL << "RpmDb::rebuildDatabase" << *this << endl;
405 const Pathname mydbpath { root()/dbPath() }; // the configured path used in reports
407 // For --rebuilddb take care we're using the real db directory
408 // and not a symlink. Otherwise rpm will rename the symlink and
409 // replace it with a real directory containing the converted db.
410 DtorReset guardRoot { _root };
411 DtorReset guardDbPath{ _dbPath };
413 _dbPath = filesystem::expandlink( mydbpath );
417 opts.push_back("--rebuilddb");
418 opts.push_back("-vv");
419 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
422 // generate and report progress
425 ProgressData::value_type hdrTotal = 0;
426 for ( librpmDb::db_const_iterator it; *it; ++it, ++hdrTotal )
428 tics.range( hdrTotal );
430 tics.sendTo( [&report,&mydbpath]( const ProgressData & tics_r ) -> bool {
431 return report->progress( tics_r.reportValue(), mydbpath );
437 while ( systemReadLine( line ) )
439 static const std::string debugPrefix { "D:" };
440 static const std::string progressPrefix { "D: read h#" };
441 static const std::string ignoreSuffix { "digest: OK" };
443 if ( ! str::startsWith( line, debugPrefix ) )
445 if ( ! str::endsWith( line, ignoreSuffix ) )
452 else if ( str::startsWith( line, progressPrefix ) )
456 WAR << "User requested abort." << endl;
462 if ( systemStatus() != 0 )
464 //TranslatorExplanation after semicolon is error message
465 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
473 ///////////////////////////////////////////////////////////////////
476 /** \ref RpmDb::syncTrustedKeys helper
477 * Compute which keys need to be exprted to / imported from the zypp keyring.
478 * Return result via argument list.
480 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
482 ///////////////////////////////////////////////////////////////////
483 // Remember latest release and where it ocurred
487 : _inRpmKeys( nullptr )
488 , _inZyppKeys( nullptr )
491 void updateIf( const Edition & rpmKey_r )
493 std::string keyRelease( rpmKey_r.release() );
494 int comp = _release.compare( keyRelease );
497 // update to newer release
498 _release.swap( keyRelease );
499 _inRpmKeys = &rpmKey_r;
500 _inZyppKeys = nullptr;
501 if ( !keyRelease.empty() )
502 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
504 else if ( comp == 0 )
506 // stay with this release
508 _inRpmKeys = &rpmKey_r;
510 // else: this is an old release
512 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
515 void updateIf( const PublicKeyData & zyppKey_r )
517 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
518 int comp = _release.compare( keyRelease );
521 // update to newer release
522 _release.swap( keyRelease );
523 _inRpmKeys = nullptr;
524 _inZyppKeys = &zyppKey_r;
525 if ( !keyRelease.empty() )
526 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
528 else if ( comp == 0 )
530 // stay with this release
532 _inZyppKeys = &zyppKey_r;
534 // else: this is an old release
536 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
539 std::string _release;
540 const Edition * _inRpmKeys;
541 const PublicKeyData * _inZyppKeys;
543 ///////////////////////////////////////////////////////////////////
545 // collect keys by ID(version) and latest creation(release)
546 std::map<std::string,Key> _keymap;
548 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
550 _keymap[(*it).version()].updateIf( *it );
553 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
555 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
558 // compute missing keys
559 std::set<Edition> rpmKeys;
560 std::list<PublicKeyData> zyppKeys;
561 for_( it, _keymap.begin(), _keymap.end() )
563 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
564 << ( (*it).second._inRpmKeys ? "R" : "_" )
565 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
566 if ( ! (*it).second._inRpmKeys )
568 zyppKeys.push_back( *(*it).second._inZyppKeys );
570 if ( ! (*it).second._inZyppKeys )
572 rpmKeys.insert( *(*it).second._inRpmKeys );
575 rpmKeys_r.swap( rpmKeys );
576 zyppKeys_r.swap( zyppKeys );
579 ///////////////////////////////////////////////////////////////////
581 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
583 MIL << "Going to sync trusted keys..." << endl;
584 std::set<Edition> rpmKeys( pubkeyEditions() );
585 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
587 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
589 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
590 // when re-acquiring the zyppp lock. For now we remove all excess keys.
591 // TODO: Once we can safely assume that all PK versions are updated we
592 // can think about re-importing newer key versions found in the zypp keyring and
593 // removing only excess ones (but case is not very likely). Unfixed PK versions
594 // however will remove the newer version found in the zypp keyring and by doing
595 // this, the key here will be removed via callback as well (keys are deleted
596 // via gpg id, regardless of the edition).
597 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
598 // Temporarily disconnect to prevent the attempt to pass back the delete request.
599 callback::TempConnect<KeyRingSignals> tempDisconnect;
601 for ( const PublicKeyData & keyData : zyppKeys )
603 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
605 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
606 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
607 if ( !dirty ) dirty = true;
611 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
614 computeKeyRingSync( rpmKeys, zyppKeys );
615 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
616 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
618 ///////////////////////////////////////////////////////////////////
619 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
621 // export to zypp keyring
622 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
623 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
624 callback::TempConnect<KeyRingSignals> tempDisconnect;
625 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
627 TmpFile tmpfile( getZYpp()->tmpPath() );
629 std::ofstream tmpos( tmpfile.path().c_str() );
630 for_( it, rpmKeys.begin(), rpmKeys.end() )
632 // we export the rpm key into a file
633 RpmHeader::constPtr result;
634 getData( "gpg-pubkey", *it, result );
635 tmpos << result->tag_description() << endl;
640 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
641 // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
642 // Modern rpm does not import those keys, but when migrating a pre SLE12 system
643 // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
644 std::set<Edition> missingKeys;
645 for ( const Edition & key : rpmKeys )
647 if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
649 ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
650 missingKeys.insert( key );
652 if ( ! missingKeys.empty() )
653 callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
655 catch ( const Exception & excpt )
657 ZYPP_CAUGHT( excpt );
658 ERR << "Could not import keys into zypp keyring: " << endl;
662 ///////////////////////////////////////////////////////////////////
663 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
665 // import from zypp keyring
666 MIL << "Importing zypp trusted keyring" << std::endl;
667 for_( it, zyppKeys.begin(), zyppKeys.end() )
671 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
673 catch ( const RpmException & exp )
679 MIL << "Trusted keys synced." << endl;
682 void RpmDb::importZyppKeyRingTrustedKeys()
683 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
685 void RpmDb::exportTrustedKeysInZyppKeyRing()
686 { syncTrustedKeys( SYNC_TO_KEYRING ); }
688 ///////////////////////////////////////////////////////////////////
691 // METHOD NAME : RpmDb::importPubkey
692 // METHOD TYPE : PMError
694 void RpmDb::importPubkey( const PublicKey & pubkey_r )
696 FAILIFNOTINITIALIZED;
698 // bnc#828672: On the fly key import in READONLY
699 if ( zypp_readonly_hack::IGotIt() )
701 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
705 // check if the key is already in the rpm database
706 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
707 std::set<Edition> rpmKeys = pubkeyEditions();
708 bool hasOldkeys = false;
710 for_( it, rpmKeys.begin(), rpmKeys.end() )
712 // bsc#1008325: Keys using subkeys for signing don't get a higher release
713 // if new subkeys are added, because the primary key remains unchanged.
714 // For now always re-import keys with subkeys. Here we don't want to export the
715 // keys in the rpm database to check whether the subkeys are the same. The calling
716 // code should take care, we don't re-import the same kesy over and over again.
717 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
719 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
723 if ( keyEd.version() != (*it).version() )
724 continue; // different key ID (version)
726 if ( keyEd.release() < (*it).release() )
728 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
736 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
740 // We must explicitly delete old key IDs first (all releases,
741 // that's why we don't call removePubkey here).
742 std::string keyName( "gpg-pubkey-" + keyEd.version() );
744 opts.push_back ( "-e" );
745 opts.push_back ( "--allmatches" );
746 opts.push_back ( "--" );
747 opts.push_back ( keyName.c_str() );
748 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
751 while ( systemReadLine( line ) )
753 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
756 if ( systemStatus() != 0 )
758 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
762 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
766 // import the new key
768 opts.push_back ( "--import" );
769 opts.push_back ( "--" );
770 std::string pubkeypath( pubkey_r.path().asString() );
771 opts.push_back ( pubkeypath.c_str() );
772 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
775 std::vector<std::string> excplines;
776 while ( systemReadLine( line ) )
778 if ( str::startsWith( line, "error:" ) )
781 excplines.push_back( std::move(line) );
787 if ( systemStatus() != 0 )
789 // Translator: %1% is a gpg public key
790 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
791 excp.moveToHistory( excplines );
792 excp.addHistory( std::move(error_message) );
793 ZYPP_THROW( std::move(excp) );
797 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
801 ///////////////////////////////////////////////////////////////////
804 // METHOD NAME : RpmDb::removePubkey
805 // METHOD TYPE : PMError
807 void RpmDb::removePubkey( const PublicKey & pubkey_r )
809 FAILIFNOTINITIALIZED;
811 // check if the key is in the rpm database and just
812 // return if it does not.
813 std::set<Edition> rpm_keys = pubkeyEditions();
814 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
815 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
817 for_( it, rpm_keys.begin(), rpm_keys.end() )
819 if ( (*it).version() == pubkeyVersion )
826 // the key does not exist, cannot be removed
827 if (found_edition == rpm_keys.end())
829 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
833 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
836 opts.push_back ( "-e" );
837 opts.push_back ( "--" );
838 opts.push_back ( rpm_name.c_str() );
839 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
842 std::vector<std::string> excplines;
843 while ( systemReadLine( line ) )
845 if ( str::startsWith( line, "error:" ) )
848 excplines.push_back( std::move(line) );
854 if ( systemStatus() != 0 )
856 // Translator: %1% is a gpg public key
857 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
858 excp.moveToHistory( excplines );
859 excp.addHistory( std::move(error_message) );
860 ZYPP_THROW( std::move(excp) );
864 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
868 ///////////////////////////////////////////////////////////////////
871 // METHOD NAME : RpmDb::pubkeys
872 // METHOD TYPE : std::set<Edition>
874 std::list<PublicKey> RpmDb::pubkeys() const
876 std::list<PublicKey> ret;
878 librpmDb::db_const_iterator it;
879 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
881 Edition edition = it->tag_edition();
882 if (edition != Edition::noedition)
884 // we export the rpm key into a file
885 RpmHeader::constPtr result;
886 getData( "gpg-pubkey", edition, result );
887 TmpFile file(getZYpp()->tmpPath());
891 os.open(file.path().asString().c_str());
892 // dump rpm key into the tmp file
893 os << result->tag_description();
894 //MIL << "-----------------------------------------------" << endl;
895 //MIL << result->tag_description() <<endl;
896 //MIL << "-----------------------------------------------" << endl;
898 // read the public key from the dumped file
902 catch ( std::exception & e )
904 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
905 // just ignore the key
912 std::set<Edition> RpmDb::pubkeyEditions() const
914 std::set<Edition> ret;
916 librpmDb::db_const_iterator it;
917 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
919 Edition edition = it->tag_edition();
920 if (edition != Edition::noedition)
921 ret.insert( edition );
927 ///////////////////////////////////////////////////////////////////
930 // METHOD NAME : RpmDb::fileList
931 // METHOD TYPE : bool
936 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
938 std::list<FileInfo> result;
940 librpmDb::db_const_iterator it;
942 if (edition_r == Edition::noedition)
944 found = it.findPackage( name_r );
948 found = it.findPackage( name_r, edition_r );
957 ///////////////////////////////////////////////////////////////////
960 // METHOD NAME : RpmDb::hasFile
961 // METHOD TYPE : bool
965 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
967 librpmDb::db_const_iterator it;
971 res = it.findByFile( file_r );
975 res = (it->tag_name() == name_r);
983 ///////////////////////////////////////////////////////////////////
986 // METHOD NAME : RpmDb::whoOwnsFile
987 // METHOD TYPE : std::string
991 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
993 librpmDb::db_const_iterator it;
994 if (it.findByFile( file_r ))
996 return it->tag_name();
1001 ///////////////////////////////////////////////////////////////////
1004 // METHOD NAME : RpmDb::hasProvides
1005 // METHOD TYPE : bool
1009 bool RpmDb::hasProvides( const std::string & tag_r ) const
1011 librpmDb::db_const_iterator it;
1012 return it.findByProvides( tag_r );
1015 ///////////////////////////////////////////////////////////////////
1018 // METHOD NAME : RpmDb::hasRequiredBy
1019 // METHOD TYPE : bool
1023 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1025 librpmDb::db_const_iterator it;
1026 return it.findByRequiredBy( tag_r );
1029 ///////////////////////////////////////////////////////////////////
1032 // METHOD NAME : RpmDb::hasConflicts
1033 // METHOD TYPE : bool
1037 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1039 librpmDb::db_const_iterator it;
1040 return it.findByConflicts( tag_r );
1043 ///////////////////////////////////////////////////////////////////
1046 // METHOD NAME : RpmDb::hasPackage
1047 // METHOD TYPE : bool
1051 bool RpmDb::hasPackage( const std::string & name_r ) const
1053 librpmDb::db_const_iterator it;
1054 return it.findPackage( name_r );
1057 ///////////////////////////////////////////////////////////////////
1060 // METHOD NAME : RpmDb::hasPackage
1061 // METHOD TYPE : bool
1065 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1067 librpmDb::db_const_iterator it;
1068 return it.findPackage( name_r, ed_r );
1071 ///////////////////////////////////////////////////////////////////
1074 // METHOD NAME : RpmDb::getData
1075 // METHOD TYPE : PMError
1079 void RpmDb::getData( const std::string & name_r,
1080 RpmHeader::constPtr & result_r ) const
1082 librpmDb::db_const_iterator it;
1083 it.findPackage( name_r );
1086 ZYPP_THROW(*(it.dbError()));
1089 ///////////////////////////////////////////////////////////////////
1092 // METHOD NAME : RpmDb::getData
1093 // METHOD TYPE : void
1097 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1098 RpmHeader::constPtr & result_r ) const
1100 librpmDb::db_const_iterator it;
1101 it.findPackage( name_r, ed_r );
1104 ZYPP_THROW(*(it.dbError()));
1107 ///////////////////////////////////////////////////////////////////
1110 struct RpmlogCapture : public std::string
1113 { rpmlog()._cap = this; }
1116 { rpmlog()._cap = nullptr; }
1124 rpmlogSetCallback( rpmLogCB, this );
1125 rpmSetVerbosity( RPMLOG_INFO );
1126 _f = ::fopen( "/dev/null","w");
1127 rpmlogSetFile( _f );
1131 { if ( _f ) ::fclose( _f ); }
1133 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1134 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1136 int rpmLog( rpmlogRec rec_r )
1138 if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1139 return RPMLOG_DEFAULT;
1146 static Rpmlog & rpmlog()
1147 { static Rpmlog _rpmlog; return _rpmlog; }
1150 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1151 const Pathname & root_r, // target root
1152 bool requireGPGSig_r, // whether no gpg signature is to be reported
1153 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1155 PathInfo file( path_r );
1156 if ( ! file.isFile() )
1158 ERR << "Not a file: " << file << endl;
1159 return RpmDb::CHK_ERROR;
1162 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1163 if ( fd == 0 || ::Ferror(fd) )
1165 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1168 return RpmDb::CHK_ERROR;
1170 rpmts ts = ::rpmtsCreate();
1171 ::rpmtsSetRootDir( ts, root_r.c_str() );
1172 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1174 rpmQVKArguments_s qva;
1175 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1176 #ifdef HAVE_NO_RPMTSSETVFYFLAGS
1177 // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
1178 // and qva_flags is not used in signature checking at all.
1179 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1181 ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
1183 RpmlogCapture vresult;
1184 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1185 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1191 // results per line...
1192 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1193 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1194 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1195 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1197 // TODO: try to get SIG info from the header rather than parsing the output
1198 std::vector<std::string> lines;
1199 str::split( vresult, std::back_inserter(lines), "\n" );
1200 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1202 for ( unsigned i = 1; i < lines.size(); ++i )
1204 std::string & line( lines[i] );
1205 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1206 if ( line.find( ": OK" ) != std::string::npos )
1208 lineres = RpmDb::CHK_OK;
1209 if ( line.find( "Signature, key ID" ) == std::string::npos )
1210 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1212 else if ( line.find( ": NOKEY" ) != std::string::npos )
1213 { lineres = RpmDb::CHK_NOKEY; }
1214 else if ( line.find( ": BAD" ) != std::string::npos )
1215 { lineres = RpmDb::CHK_FAIL; }
1216 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1217 { lineres = RpmDb::CHK_NOTFOUND; }
1218 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1219 { lineres = RpmDb::CHK_NOTTRUSTED; }
1220 else if ( line.find( ": NOTFOUND" ) != std::string::npos )
1221 { continue; } // just collect details for signatures found (#229)
1224 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1227 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1229 if ( count[RpmDb::CHK_FAIL] )
1230 ret = RpmDb::CHK_FAIL;
1232 else if ( count[RpmDb::CHK_NOTFOUND] )
1233 ret = RpmDb::CHK_NOTFOUND;
1235 else if ( count[RpmDb::CHK_NOKEY] )
1236 ret = RpmDb::CHK_NOKEY;
1238 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1239 ret = RpmDb::CHK_NOTTRUSTED;
1241 else if ( ret == RpmDb::CHK_OK )
1243 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1245 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1246 if ( requireGPGSig_r )
1247 ret = RpmDb::CHK_NOSIG;
1251 if ( ret != RpmDb::CHK_OK )
1253 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1260 ///////////////////////////////////////////////////////////////////
1262 // METHOD NAME : RpmDb::checkPackage
1263 // METHOD TYPE : RpmDb::CheckPackageResult
1265 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1266 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1268 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1269 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1271 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1272 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1275 // determine changed files of installed package
1277 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1283 if ( ! initialized() ) return false;
1287 opts.push_back ("-V");
1288 opts.push_back ("--nodeps");
1289 opts.push_back ("--noscripts");
1290 opts.push_back ("--nomd5");
1291 opts.push_back ("--");
1292 opts.push_back (packageName.c_str());
1294 run_rpm (opts, ExternalProgram::Discard_Stderr);
1296 if ( process == NULL )
1307 M Mode (includes permissions and file type)
1311 while (systemReadLine(line))
1313 if (line.length() > 12 &&
1314 (line[0] == 'S' || line[0] == 's' ||
1315 (line[0] == '.' && line[7] == 'T')))
1317 // file has been changed
1318 std::string filename;
1320 filename.assign(line, 11, line.length() - 11);
1321 fileList.insert(filename);
1326 // exit code ignored, rpm returns 1 no matter if package is installed or
1334 /****************************************************************/
1335 /* private member-functions */
1336 /****************************************************************/
1338 /*--------------------------------------------------------------*/
1339 /* Run rpm with the specified arguments, handling stderr */
1340 /* as specified by disp */
1341 /*--------------------------------------------------------------*/
1343 RpmDb::run_rpm (const RpmArgVec& opts,
1344 ExternalProgram::Stderr_Disposition disp)
1353 if ( ! initialized() )
1355 ZYPP_THROW(RpmDbNotOpenException());
1360 // always set root and dbpath
1361 #if defined(WORKAROUNDRPMPWDBUG)
1362 args.push_back("#/"); // chdir to / to workaround bnc#819354
1364 args.push_back("rpm");
1365 args.push_back("--root");
1366 args.push_back(_root.asString().c_str());
1367 args.push_back("--dbpath");
1368 args.push_back(_dbPath.asString().c_str());
1369 if ( env::ZYPP_RPM_DEBUG() )
1370 args.push_back("-vv");
1371 const char* argv[args.size() + opts.size() + 1];
1373 const char** p = argv;
1374 p = copy (args.begin (), args.end (), p);
1375 p = copy (opts.begin (), opts.end (), p);
1378 // Invalidate all outstanding database handles in case
1379 // the database gets modified.
1380 librpmDb::dbRelease( true );
1382 // Launch the program with default locale
1383 process = new ExternalProgram(argv, disp, false, -1, true);
1387 /*--------------------------------------------------------------*/
1388 /* Read a line from the rpm process */
1389 /*--------------------------------------------------------------*/
1390 bool RpmDb::systemReadLine( std::string & line )
1394 if ( process == NULL )
1397 if ( process->inputFile() )
1399 process->setBlocking( false );
1400 FILE * inputfile = process->inputFile();
1402 // Check every 5 seconds if the process is still running to prevent against
1403 // daemons launched in rpm %post that do not close their filedescriptors,
1404 // causing us to block for infinity. (bnc#174548)
1405 const auto &readResult = io::receiveUpto( inputfile, '\n', 5 * 1000, false );
1406 switch ( readResult.first ) {
1407 case io::ReceiveUpToResult::Timeout: {
1408 if ( !process->running() )
1411 // we might have received a partial line, lets not forget about it
1412 line += readResult.second;
1414 case io::ReceiveUpToResult::Error:
1415 case io::ReceiveUpToResult::EndOfFile: {
1416 line += readResult.second;
1417 if ( line.size() && line.back() == '\n')
1419 return line.size(); // in case of pending output
1421 case io::ReceiveUpToResult::Success: {
1422 line += readResult.second;
1424 if ( line.size() && line.back() == '\n')
1427 if ( env::ZYPP_RPM_DEBUG() )
1428 L_DBG("RPM_DEBUG") << line << endl;
1429 return true; // complete line
1437 /*--------------------------------------------------------------*/
1438 /* Return the exit status of the rpm process, closing the */
1439 /* connection if not already done */
1440 /*--------------------------------------------------------------*/
1442 RpmDb::systemStatus()
1444 if ( process == NULL )
1447 exit_code = process->close();
1451 error_message = process->execError();
1456 // DBG << "exit code " << exit_code << endl;
1461 /*--------------------------------------------------------------*/
1462 /* Forcably kill the rpm process */
1463 /*--------------------------------------------------------------*/
1467 if (process) process->kill();
1471 // generate diff mails for config files
1472 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1474 std::string msg = line.substr(9);
1475 std::string::size_type pos1 = std::string::npos;
1476 std::string::size_type pos2 = std::string::npos;
1477 std::string file1s, file2s;
1481 pos1 = msg.find (typemsg);
1484 if ( pos1 == std::string::npos )
1487 pos2 = pos1 + strlen (typemsg);
1489 if (pos2 >= msg.length() )
1492 file1 = msg.substr (0, pos1);
1493 file2 = msg.substr (pos2);
1495 file1s = file1.asString();
1496 file2s = file2.asString();
1498 if (!_root.empty() && _root != "/")
1500 file1 = _root + file1;
1501 file2 = _root + file2;
1505 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1508 Pathname file = _root + WARNINGMAILPATH;
1509 if (filesystem::assert_dir(file) != 0)
1511 ERR << "Could not create " << file.asString() << endl;
1514 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1515 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1518 ERR << "Could not open " << file << endl;
1522 // Translator: %s = name of an rpm package. A list of diffs follows
1524 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1527 ERR << "diff failed" << endl;
1528 notify << str::form(difffailmsg,
1529 file1s.c_str(), file2s.c_str()) << endl;
1533 notify << str::form(diffgenmsg,
1534 file1s.c_str(), file2s.c_str()) << endl;
1536 // remove root for the viewer's pleasure (#38240)
1537 if (!_root.empty() && _root != "/")
1539 if (out.substr(0,4) == "--- ")
1541 out.replace(4, file1.asString().length(), file1s);
1543 std::string::size_type pos = out.find("\n+++ ");
1544 if (pos != std::string::npos)
1546 out.replace(pos+5, file2.asString().length(), file2s);
1549 notify << out << endl;
1552 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1557 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1563 ///////////////////////////////////////////////////////////////////
1566 // METHOD NAME : RpmDb::installPackage
1567 // METHOD TYPE : PMError
1569 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1571 callback::SendReport<RpmInstallReport> report;
1573 report->start(filename);
1578 doInstallPackage(filename, flags, report);
1582 catch (RpmException & excpt_r)
1584 RpmInstallReport::Action user = report->problem( excpt_r );
1586 if ( user == RpmInstallReport::ABORT )
1588 report->finish( excpt_r );
1589 ZYPP_RETHROW(excpt_r);
1591 else if ( user == RpmInstallReport::IGNORE )
1599 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1601 FAILIFNOTINITIALIZED;
1602 HistoryLog historylog;
1604 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1608 if ( _packagebackups )
1610 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1611 if ( ! backupPackage( filename ) )
1613 ERR << "backup of " << filename.asString() << " failed" << endl;
1615 // FIXME status handling
1616 report->progress( 0 ); // allow 1% for backup creation.
1621 if (flags & RPMINST_NOUPGRADE)
1622 opts.push_back("-i");
1624 opts.push_back("-U");
1626 opts.push_back("--percent");
1627 opts.push_back("--noglob");
1629 // ZConfig defines cross-arch installation
1630 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1631 opts.push_back("--ignorearch");
1633 if (flags & RPMINST_NODIGEST)
1634 opts.push_back("--nodigest");
1635 if (flags & RPMINST_NOSIGNATURE)
1636 opts.push_back("--nosignature");
1637 if (flags & RPMINST_EXCLUDEDOCS)
1638 opts.push_back ("--excludedocs");
1639 if (flags & RPMINST_NOSCRIPTS)
1640 opts.push_back ("--noscripts");
1641 if (flags & RPMINST_FORCE)
1642 opts.push_back ("--force");
1643 if (flags & RPMINST_NODEPS)
1644 opts.push_back ("--nodeps");
1645 if (flags & RPMINST_IGNORESIZE)
1646 opts.push_back ("--ignoresize");
1647 if (flags & RPMINST_JUSTDB)
1648 opts.push_back ("--justdb");
1649 if (flags & RPMINST_TEST)
1650 opts.push_back ("--test");
1651 if (flags & RPMINST_NOPOSTTRANS)
1652 opts.push_back ("--noposttrans");
1654 opts.push_back("--");
1656 // rpm requires additional quoting of special chars:
1657 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1658 opts.push_back ( quotedFilename.c_str() );
1659 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1661 // forward additional rpm output via report;
1663 unsigned lineno = 0;
1664 callback::UserData cmdout( InstallResolvableReport::contentRpmout );
1665 // Key "solvable" injected by RpmInstallPackageReceiver
1666 cmdout.set( "line", std::cref(line) );
1667 cmdout.set( "lineno", lineno );
1669 // LEGACY: collect and forward additional rpm output in finish
1670 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1671 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
1673 while ( systemReadLine( line ) )
1675 if ( str::startsWith( line, "%%" ) )
1678 sscanf( line.c_str() + 2, "%d", &percent );
1679 report->progress( percent );
1683 cmdout.set( "lineno", lineno );
1684 report->report( cmdout );
1686 if ( lineno >= MAXRPMMESSAGELINES ) {
1687 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1691 rpmmsg += line+'\n';
1693 if ( str::startsWith( line, "warning:" ) )
1694 configwarnings.push_back(line);
1696 if ( lineno >= MAXRPMMESSAGELINES )
1697 rpmmsg += "[truncated]\n";
1699 int rpm_status = systemStatus();
1702 for (std::vector<std::string>::iterator it = configwarnings.begin();
1703 it != configwarnings.end(); ++it)
1705 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1707 _("rpm saved %s as %s, but it was impossible to determine the difference"),
1709 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1710 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1712 _("rpm created %s as %s, but it was impossible to determine the difference"),
1714 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1717 if ( rpm_status != 0 )
1720 str::form("%s install failed", Pathname::basename(filename).c_str()),
1721 true /*timestamp*/);
1722 std::ostringstream sstr;
1723 sstr << "rpm output:" << endl << rpmmsg << endl;
1724 historylog.comment(sstr.str());
1725 // TranslatorExplanation the colon is followed by an error message
1726 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
1728 else if ( ! rpmmsg.empty() )
1731 str::form("%s installed ok", Pathname::basename(filename).c_str()),
1732 true /*timestamp*/);
1733 std::ostringstream sstr;
1734 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1735 historylog.comment(sstr.str());
1737 // report additional rpm output in finish
1738 // TranslatorExplanation Text is followed by a ':' and the actual output.
1739 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1743 ///////////////////////////////////////////////////////////////////
1746 // METHOD NAME : RpmDb::removePackage
1747 // METHOD TYPE : PMError
1749 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
1751 // 'rpm -e' does not like epochs
1752 return removePackage( package->name()
1753 + "-" + package->edition().version()
1754 + "-" + package->edition().release()
1755 + "." + package->arch().asString(), flags );
1758 ///////////////////////////////////////////////////////////////////
1761 // METHOD NAME : RpmDb::removePackage
1762 // METHOD TYPE : PMError
1764 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
1766 callback::SendReport<RpmRemoveReport> report;
1768 report->start( name_r );
1773 doRemovePackage(name_r, flags, report);
1777 catch (RpmException & excpt_r)
1779 RpmRemoveReport::Action user = report->problem( excpt_r );
1781 if ( user == RpmRemoveReport::ABORT )
1783 report->finish( excpt_r );
1784 ZYPP_RETHROW(excpt_r);
1786 else if ( user == RpmRemoveReport::IGNORE )
1795 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
1797 FAILIFNOTINITIALIZED;
1798 HistoryLog historylog;
1800 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1803 if ( _packagebackups )
1805 // FIXME solve this status report somehow
1806 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1807 if ( ! backupPackage( name_r ) )
1809 ERR << "backup of " << name_r << " failed" << endl;
1811 report->progress( 0 );
1815 report->progress( 100 );
1820 opts.push_back("-e");
1821 opts.push_back("--allmatches");
1823 if (flags & RPMINST_NOSCRIPTS)
1824 opts.push_back("--noscripts");
1825 if (flags & RPMINST_NODEPS)
1826 opts.push_back("--nodeps");
1827 if (flags & RPMINST_JUSTDB)
1828 opts.push_back("--justdb");
1829 if (flags & RPMINST_TEST)
1830 opts.push_back ("--test");
1831 if (flags & RPMINST_FORCE)
1833 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1836 opts.push_back("--");
1837 opts.push_back(name_r.c_str());
1838 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1840 // forward additional rpm output via report;
1842 unsigned lineno = 0;
1843 callback::UserData cmdout( RemoveResolvableReport::contentRpmout );
1844 // Key "solvable" injected by RpmInstallPackageReceiver
1845 cmdout.set( "line", std::cref(line) );
1846 cmdout.set( "lineno", lineno );
1849 // LEGACY: collect and forward additional rpm output in finish
1850 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1852 // got no progress from command, so we fake it:
1853 // 5 - command started
1854 // 50 - command completed
1856 report->progress( 5 );
1857 while (systemReadLine(line))
1860 cmdout.set( "lineno", lineno );
1861 report->report( cmdout );
1863 if ( lineno >= MAXRPMMESSAGELINES ) {
1864 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1867 rpmmsg += line+'\n';
1869 if ( lineno >= MAXRPMMESSAGELINES )
1870 rpmmsg += "[truncated]\n";
1871 report->progress( 50 );
1872 int rpm_status = systemStatus();
1874 if ( rpm_status != 0 )
1877 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
1878 std::ostringstream sstr;
1879 sstr << "rpm output:" << endl << rpmmsg << endl;
1880 historylog.comment(sstr.str());
1881 // TranslatorExplanation the colon is followed by an error message
1882 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
1884 else if ( ! rpmmsg.empty() )
1887 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
1889 std::ostringstream sstr;
1890 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1891 historylog.comment(sstr.str());
1893 // report additional rpm output in finish
1894 // TranslatorExplanation Text is followed by a ':' and the actual output.
1895 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1899 ///////////////////////////////////////////////////////////////////
1902 // METHOD NAME : RpmDb::backupPackage
1903 // METHOD TYPE : bool
1905 bool RpmDb::backupPackage( const Pathname & filename )
1907 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1911 return backupPackage( h->tag_name() );
1914 ///////////////////////////////////////////////////////////////////
1917 // METHOD NAME : RpmDb::backupPackage
1918 // METHOD TYPE : bool
1920 bool RpmDb::backupPackage(const std::string& packageName)
1922 HistoryLog progresslog;
1924 Pathname backupFilename;
1925 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1927 if (_backuppath.empty())
1929 INT << "_backuppath empty" << endl;
1935 if (!queryChangedFiles(fileList, packageName))
1937 ERR << "Error while getting changed files for package " <<
1938 packageName << endl;
1942 if (fileList.size() <= 0)
1944 DBG << "package " << packageName << " not changed -> no backup" << endl;
1948 if (filesystem::assert_dir(_root + _backuppath) != 0)
1954 // build up archive name
1955 time_t currentTime = time(0);
1956 struct tm *currentLocalTime = localtime(¤tTime);
1958 int date = (currentLocalTime->tm_year + 1900) * 10000
1959 + (currentLocalTime->tm_mon + 1) * 100
1960 + currentLocalTime->tm_mday;
1965 backupFilename = _root + _backuppath
1966 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1969 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1971 PathInfo pi(filestobackupfile);
1972 if (pi.isExist() && !pi.isFile())
1974 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1978 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1982 ERR << "could not open " << filestobackupfile.asString() << endl;
1986 for (FileList::const_iterator cit = fileList.begin();
1987 cit != fileList.end(); ++cit)
1989 std::string name = *cit;
1990 if ( name[0] == '/' )
1992 // remove slash, file must be relative to -C parameter of tar
1993 name = name.substr( 1 );
1995 DBG << "saving file "<< name << endl;
2000 const char* const argv[] =
2005 _root.asString().c_str(),
2006 "--ignore-failed-read",
2008 backupFilename.asString().c_str(),
2010 filestobackupfile.asString().c_str(),
2014 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2015 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2019 // TODO: its probably possible to start tar with -v and watch it adding
2020 // files to report progress
2021 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2026 int ret = tar.close();
2030 ERR << "tar failed: " << tarmsg << endl;
2035 MIL << "tar backup ok" << endl;
2036 progresslog.comment(
2037 str::form(_("created backup %s"), backupFilename.asString().c_str())
2038 , /*timestamp*/true);
2041 filesystem::unlink(filestobackupfile);
2047 void RpmDb::setBackupPath(const Pathname& path)
2052 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2056 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2057 // translators: possible rpm package signature check result [brief]
2058 OUTS( CHK_OK, _("Signature is OK") );
2059 // translators: possible rpm package signature check result [brief]
2060 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2061 // translators: possible rpm package signature check result [brief]
2062 OUTS( CHK_FAIL, _("Signature does not verify") );
2063 // translators: possible rpm package signature check result [brief]
2064 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2065 // translators: possible rpm package signature check result [brief]
2066 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2067 // translators: possible rpm package signature check result [brief]
2068 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2069 // translators: possible rpm package signature check result [brief]
2070 OUTS( CHK_NOSIG, _("File is unsigned") );
2073 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2076 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2078 for ( const auto & el : obj )
2079 str << el.second << endl;
2084 } // namespace target