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"
36 #include "zypp/Date.h"
37 #include "zypp/Pathname.h"
38 #include "zypp/PathInfo.h"
39 #include "zypp/PublicKey.h"
41 #include "zypp/target/rpm/RpmDb.h"
42 #include "zypp/target/rpm/RpmCallbacks.h"
44 #include "zypp/HistoryLog.h"
45 #include "zypp/target/rpm/librpmDb.h"
46 #include "zypp/target/rpm/RpmException.h"
47 #include "zypp/TmpPath.h"
48 #include "zypp/KeyRing.h"
49 #include "zypp/ZYppFactory.h"
50 #include "zypp/ZConfig.h"
53 using namespace zypp::filesystem;
55 #define WARNINGMAILPATH "/var/log/YaST2/"
56 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
57 #define MAXRPMMESSAGELINES 10000
59 #define WORKAROUNDRPMPWDBUG
63 namespace zypp_readonly_hack
65 bool IGotIt(); // in readonly-mode
73 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
74 const char* quoteInFilename_m = "\'\"";
76 const char* quoteInFilename_m = " \t\'\"";
78 inline std::string rpmQuoteFilename( const Pathname & path_r )
80 std::string path( path_r.asString() );
81 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
82 pos != std::string::npos;
83 pos = path.find_first_of( quoteInFilename_m, pos ) )
85 path.insert( pos, "\\" );
86 pos += 2; // skip '\\' and the quoted char.
92 /** Workaround bnc#827609 - rpm needs a readable pwd so we
93 * chdir to /. Turn realtive pathnames into absolute ones
94 * by prepending cwd so rpm still finds them
96 inline Pathname workaroundRpmPwdBug( Pathname path_r )
98 #if defined(WORKAROUNDRPMPWDBUG)
99 if ( path_r.relative() )
101 // try to prepend cwd
102 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
104 return Pathname( cwd ) / path_r;
105 WAR << "Can't get cwd!" << endl;
108 return path_r; // no problem with absolute pathnames
112 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
114 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
119 ~KeyRingSignalReceiver()
124 virtual void trustedKeyAdded( const PublicKey &key )
126 MIL << "trusted key added to zypp Keyring. Importing..." << endl;
127 _rpmdb.importPubkey( key );
130 virtual void trustedKeyRemoved( const PublicKey &key )
132 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
133 _rpmdb.removePubkey( key );
139 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
141 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
151 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
158 for (line = prog.receiveLine(), count=0;
160 line = prog.receiveLine(), count++ )
162 if (maxlines<0?true:count<maxlines)
171 /******************************************************************
174 ** FUNCTION NAME : stringPath
175 ** FUNCTION TYPE : inline std::string
177 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
179 return librpmDb::stringPath( root_r, sub_r );
182 /******************************************************************
185 ** FUNCTION NAME : operator<<
186 ** FUNCTION TYPE : std::ostream &
188 std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
190 if ( obj == RpmDb::DbSI_NO_INIT )
196 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
198 ENUM_OUT( DbSI_HAVE_V4, 'X' );
199 ENUM_OUT( DbSI_MADE_V4, 'c' );
200 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
202 ENUM_OUT( DbSI_HAVE_V3, 'X' );
203 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
204 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
213 ///////////////////////////////////////////////////////////////////
215 // CLASS NAME : RpmDb
217 ///////////////////////////////////////////////////////////////////
219 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
221 ///////////////////////////////////////////////////////////////////
223 ///////////////////////////////////////////////////////////////////
226 // METHOD NAME : RpmDb::RpmDb
227 // METHOD TYPE : Constructor
230 : _dbStateInfo( DbSI_NO_INIT )
231 #warning Check for obsolete memebers
232 , _backuppath ("/var/adm/backup")
233 , _packagebackups(false)
234 , _warndirexists(false)
238 librpmDb::globalInit();
239 // Some rpm versions are patched not to abort installation if
240 // symlink creation failed.
241 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
242 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
245 ///////////////////////////////////////////////////////////////////
248 // METHOD NAME : RpmDb::~RpmDb
249 // METHOD TYPE : Destructor
253 MIL << "~RpmDb()" << endl;
256 MIL << "~RpmDb() end" << endl;
257 sKeyRingReceiver.reset();
260 Date RpmDb::timestamp() const
265 if ( dbPath().empty() )
266 db_path = "/var/lib/rpm";
270 PathInfo rpmdb_info(root() + db_path + "/Packages");
272 if ( rpmdb_info.isExist() )
273 return rpmdb_info.mtime();
277 ///////////////////////////////////////////////////////////////////
280 // METHOD NAME : RpmDb::dumpOn
281 // METHOD TYPE : std::ostream &
283 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
287 if ( _dbStateInfo == DbSI_NO_INIT )
293 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
295 ENUM_OUT( DbSI_HAVE_V4, 'X' );
296 ENUM_OUT( DbSI_MADE_V4, 'c' );
297 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
299 ENUM_OUT( DbSI_HAVE_V3, 'X' );
300 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
301 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
302 str << "): " << stringPath( _root, _dbPath );
308 ///////////////////////////////////////////////////////////////////
311 // METHOD NAME : RpmDb::initDatabase
312 // METHOD TYPE : PMError
314 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
316 ///////////////////////////////////////////////////////////////////
318 ///////////////////////////////////////////////////////////////////
319 bool quickinit( root_r.empty() );
321 if ( root_r.empty() )
324 if ( dbPath_r.empty() )
325 dbPath_r = "/var/lib/rpm";
327 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
329 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
330 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
333 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
334 << ( doRebuild_r ? " (rebuilddb)" : "" )
335 << ( quickinit ? " (quickinit)" : "" ) << endl;
337 ///////////////////////////////////////////////////////////////////
338 // Check whether already initialized
339 ///////////////////////////////////////////////////////////////////
342 if ( root_r == _root && dbPath_r == _dbPath )
348 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
352 ///////////////////////////////////////////////////////////////////
354 ///////////////////////////////////////////////////////////////////
355 librpmDb::unblockAccess();
359 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
363 DbStateInfoBits info = DbSI_NO_INIT;
366 internal_initDatabase( root_r, dbPath_r, info );
368 catch (const RpmException & excpt_r)
370 ZYPP_CAUGHT(excpt_r);
371 librpmDb::blockAccess();
372 ERR << "Cleanup on error: state " << info << endl;
374 if ( dbsi_has( info, DbSI_MADE_V4 ) )
376 // remove the newly created rpm4 database and
377 // any backup created on conversion.
378 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
380 ZYPP_RETHROW(excpt_r);
382 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
384 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
386 // Move obsolete rpm3 database beside.
387 MIL << "Cleanup: state " << info << endl;
388 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
389 dbsi_clr( info, DbSI_HAVE_V3 );
393 // Performing an update: Keep the original rpm3 database
394 // and wait if the rpm4 database gets modified by installing
395 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
396 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
399 #warning CHECK: notify root about conversion backup.
407 if ( dbsi_has( info, DbSI_HAVE_V4 )
408 && ! dbsi_has( info, DbSI_MADE_V4 ) )
414 MIL << "Synchronizing keys with zypp keyring" << endl;
417 // Close the database in case any write acces (create/convert)
418 // happened during init. This should drop any lock acquired
419 // by librpm. On demand it will be reopened readonly and should
420 // not hold any lock.
421 librpmDb::dbRelease( true );
423 MIL << "InitDatabase: " << *this << endl;
426 ///////////////////////////////////////////////////////////////////
429 // METHOD NAME : RpmDb::internal_initDatabase
430 // METHOD TYPE : PMError
432 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
433 DbStateInfoBits & info_r )
435 info_r = DbSI_NO_INIT;
437 ///////////////////////////////////////////////////////////////////
438 // Get info about the desired database dir
439 ///////////////////////////////////////////////////////////////////
440 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
442 if ( dbInfo.illegalArgs() )
444 // should not happen (checked in initDatabase)
445 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
447 if ( ! dbInfo.usableArgs() )
449 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
450 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
453 if ( dbInfo.hasDbV4() )
455 dbsi_set( info_r, DbSI_HAVE_V4 );
456 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
460 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
463 if ( dbInfo.hasDbV3() )
465 dbsi_set( info_r, DbSI_HAVE_V3 );
467 if ( dbInfo.hasDbV3ToV4() )
469 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
472 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
473 librpmDb::dumpState( DBG ) << endl;
475 ///////////////////////////////////////////////////////////////////
476 // Access database, create if needed
477 ///////////////////////////////////////////////////////////////////
479 // creates dbdir and empty rpm4 database if not present
480 librpmDb::dbAccess( root_r, dbPath_r );
482 if ( ! dbInfo.hasDbV4() )
485 if ( dbInfo.hasDbV4() )
487 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
491 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
492 librpmDb::dumpState( DBG ) << endl;
494 ///////////////////////////////////////////////////////////////////
495 // Check whether to convert something. Create backup but do
496 // not remove anything here
497 ///////////////////////////////////////////////////////////////////
498 librpmDb::constPtr dbptr;
499 librpmDb::dbAccess( dbptr );
500 bool dbEmpty = dbptr->empty();
503 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
506 if ( dbInfo.hasDbV3() )
508 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
512 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
513 convertV3toV4( dbInfo.dbV3().path(), dbptr );
515 // create a backup copy
516 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
519 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
524 if ( dbInfo.hasDbV3ToV4() )
526 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
527 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
535 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
536 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
537 dbsi_set( info_r, DbSI_MODIFIED_V4 );
541 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
542 librpmDb::dumpState( DBG ) << endl;
545 if ( dbInfo.hasDbV3ToV4() )
547 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
551 ///////////////////////////////////////////////////////////////////
554 // METHOD NAME : RpmDb::removeV4
555 // METHOD TYPE : void
557 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
559 const char * v3backup = "packages.rpm3";
560 const char * master = "Packages";
561 const char * index[] =
583 PathInfo pi( dbdir_r );
586 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
590 for ( const char ** f = index; *f; ++f )
595 filesystem::unlink( pi.path() );
599 pi( dbdir_r + master );
602 MIL << "Removing rpm4 database " << pi << endl;
603 filesystem::unlink( pi.path() );
608 pi( dbdir_r + v3backup );
611 MIL << "Removing converted rpm3 database backup " << pi << endl;
612 filesystem::unlink( pi.path() );
617 ///////////////////////////////////////////////////////////////////
620 // METHOD NAME : RpmDb::removeV3
621 // METHOD TYPE : void
623 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
625 const char * master = "packages.rpm";
626 const char * index[] =
628 "conflictsindex.rpm",
639 PathInfo pi( dbdir_r );
642 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
646 for ( const char ** f = index; *f; ++f )
651 filesystem::unlink( pi.path() );
655 #warning CHECK: compare vs existing v3 backup. notify root
656 pi( dbdir_r + master );
659 Pathname m( pi.path() );
662 // backup was already created
663 filesystem::unlink( m );
664 Pathname b( m.extend( "3" ) );
665 pi( b ); // stat backup
669 Pathname b( m.extend( ".deleted" ) );
673 // rempve existing backup
674 filesystem::unlink( b );
676 filesystem::rename( m, b );
677 pi( b ); // stat backup
679 MIL << "(Re)moved rpm3 database to " << pi << endl;
683 ///////////////////////////////////////////////////////////////////
686 // METHOD NAME : RpmDb::modifyDatabase
687 // METHOD TYPE : void
689 void RpmDb::modifyDatabase()
691 if ( ! initialized() )
694 // tag database as modified
695 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
697 // Move outdated rpm3 database beside.
698 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
700 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
701 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
702 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
706 ///////////////////////////////////////////////////////////////////
709 // METHOD NAME : RpmDb::closeDatabase
710 // METHOD TYPE : PMError
712 void RpmDb::closeDatabase()
714 if ( ! initialized() )
719 MIL << "Calling closeDatabase: " << *this << endl;
721 ///////////////////////////////////////////////////////////////////
722 // Block further database access
723 ///////////////////////////////////////////////////////////////////
724 librpmDb::blockAccess();
726 ///////////////////////////////////////////////////////////////////
727 // Check fate if old version database still present
728 ///////////////////////////////////////////////////////////////////
729 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
731 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
732 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
734 // Move outdated rpm3 database beside.
735 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
739 // Remove unmodified rpm4 database
740 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
744 ///////////////////////////////////////////////////////////////////
746 ///////////////////////////////////////////////////////////////////
747 _root = _dbPath = Pathname();
748 _dbStateInfo = DbSI_NO_INIT;
750 MIL << "closeDatabase: " << *this << endl;
753 ///////////////////////////////////////////////////////////////////
756 // METHOD NAME : RpmDb::rebuildDatabase
757 // METHOD TYPE : PMError
759 void RpmDb::rebuildDatabase()
761 callback::SendReport<RebuildDBReport> report;
763 report->start( root() + dbPath() );
767 doRebuildDatabase(report);
769 catch (RpmException & excpt_r)
771 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
772 ZYPP_RETHROW(excpt_r);
774 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
777 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
779 FAILIFNOTINITIALIZED;
781 MIL << "RpmDb::rebuildDatabase" << *this << endl;
782 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
784 PathInfo dbMaster( root() + dbPath() + "Packages" );
785 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
789 opts.push_back("--rebuilddb");
790 opts.push_back("-vv");
792 // don't call modifyDatabase because it would remove the old
793 // rpm3 database, if the current database is a temporary one.
794 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
796 // progress report: watch this file growing
797 PathInfo newMaster( root()
798 + dbPath().extend( str::form( "rebuilddb.%d",
799 process?process->getpid():0) )
805 while ( systemReadLine( line ) )
808 { // file is removed at the end of rebuild.
809 // current size should be upper limit for new db
810 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
812 WAR << "User requested abort." << endl;
814 filesystem::recursive_rmdir( newMaster.path().dirname() );
818 if ( line.compare( 0, 2, "D:" ) )
820 errmsg += line + '\n';
821 // report.notify( line );
826 int rpm_status = systemStatus();
828 if ( rpm_status != 0 )
830 //TranslatorExplanation after semicolon is error message
831 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
835 report->progress( 100, root() + dbPath() ); // 100%
839 ///////////////////////////////////////////////////////////////////
842 /** \ref RpmDb::syncTrustedKeys helper
843 * Compute which keys need to be exprted to / imported from the zypp keyring.
844 * Return result via argument list.
846 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
848 ///////////////////////////////////////////////////////////////////
849 // Remember latest release and where it ocurred
853 : _inRpmKeys( nullptr )
854 , _inZyppKeys( nullptr )
857 void updateIf( const Edition & rpmKey_r )
859 std::string keyRelease( rpmKey_r.release() );
860 int comp = _release.compare( keyRelease );
863 // update to newer release
864 _release.swap( keyRelease );
865 _inRpmKeys = &rpmKey_r;
866 _inZyppKeys = nullptr;
867 if ( !keyRelease.empty() )
868 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
870 else if ( comp == 0 )
872 // stay with this release
874 _inRpmKeys = &rpmKey_r;
876 // else: this is an old release
878 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
881 void updateIf( const PublicKeyData & zyppKey_r )
883 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
884 int comp = _release.compare( keyRelease );
887 // update to newer release
888 _release.swap( keyRelease );
889 _inRpmKeys = nullptr;
890 _inZyppKeys = &zyppKey_r;
891 if ( !keyRelease.empty() )
892 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
894 else if ( comp == 0 )
896 // stay with this release
898 _inZyppKeys = &zyppKey_r;
900 // else: this is an old release
902 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
905 std::string _release;
906 const Edition * _inRpmKeys;
907 const PublicKeyData * _inZyppKeys;
909 ///////////////////////////////////////////////////////////////////
911 // collect keys by ID(version) and latest creation(release)
912 std::map<std::string,Key> _keymap;
914 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
916 _keymap[(*it).version()].updateIf( *it );
919 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
921 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
924 // compute missing keys
925 std::set<Edition> rpmKeys;
926 std::list<PublicKeyData> zyppKeys;
927 for_( it, _keymap.begin(), _keymap.end() )
929 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
930 << ( (*it).second._inRpmKeys ? "R" : "_" )
931 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
932 if ( ! (*it).second._inRpmKeys )
934 zyppKeys.push_back( *(*it).second._inZyppKeys );
936 if ( ! (*it).second._inZyppKeys )
938 rpmKeys.insert( *(*it).second._inRpmKeys );
941 rpmKeys_r.swap( rpmKeys );
942 zyppKeys_r.swap( zyppKeys );
945 ///////////////////////////////////////////////////////////////////
947 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
949 MIL << "Going to sync trusted keys..." << endl;
950 std::set<Edition> rpmKeys( pubkeyEditions() );
951 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
953 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
955 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
956 // when re-acquiring the zyppp lock. For now we remove all excess keys.
957 // TODO: Once we can safely assume that all PK versions are updated we
958 // can think about re-importing newer key versions found in the zypp keyring and
959 // removing only excess ones (but case is not very likely). Unfixed PK versions
960 // however will remove the newer version found in the zypp keyring and by doing
961 // this, the key here will be removed via callback as well (keys are deleted
962 // via gpg id, regardless of the edition).
963 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
964 // Temporarily disconnect to prevent the attempt to pass back the delete request.
965 callback::TempConnect<KeyRingSignals> tempDisconnect;
967 for ( const PublicKeyData & keyData : zyppKeys )
969 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
971 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
972 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
973 if ( !dirty ) dirty = true;
977 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
980 computeKeyRingSync( rpmKeys, zyppKeys );
981 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
982 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
984 ///////////////////////////////////////////////////////////////////
985 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
987 // export to zypp keyring
988 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
989 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
990 callback::TempConnect<KeyRingSignals> tempDisconnect;
991 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
993 TmpFile tmpfile( getZYpp()->tmpPath() );
995 std::ofstream tmpos( tmpfile.path().c_str() );
996 for_( it, rpmKeys.begin(), rpmKeys.end() )
998 // we export the rpm key into a file
999 RpmHeader::constPtr result;
1000 getData( "gpg-pubkey", *it, result );
1001 tmpos << result->tag_description() << endl;
1006 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1008 catch (Exception &e)
1010 ERR << "Could not import keys into in zypp keyring" << endl;
1014 ///////////////////////////////////////////////////////////////////
1015 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1017 // import from zypp keyring
1018 MIL << "Importing zypp trusted keyring" << std::endl;
1019 for_( it, zyppKeys.begin(), zyppKeys.end() )
1023 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1025 catch ( const RpmException & exp )
1031 MIL << "Trusted keys synced." << endl;
1034 void RpmDb::importZyppKeyRingTrustedKeys()
1035 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1037 void RpmDb::exportTrustedKeysInZyppKeyRing()
1038 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1040 ///////////////////////////////////////////////////////////////////
1043 // METHOD NAME : RpmDb::importPubkey
1044 // METHOD TYPE : PMError
1046 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1048 FAILIFNOTINITIALIZED;
1050 // bnc#828672: On the fly key import in READONLY
1051 if ( zypp_readonly_hack::IGotIt() )
1053 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1057 // check if the key is already in the rpm database
1058 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1059 std::set<Edition> rpmKeys = pubkeyEditions();
1060 bool hasOldkeys = false;
1062 for_( it, rpmKeys.begin(), rpmKeys.end() )
1064 // bsc#1008325: Keys using subkeys for signing don't get a higher release
1065 // if new subkeys are added, because the primary key remains unchanged.
1066 // For now always re-import keys with subkeys. Here we don't want to export the
1067 // keys in the rpm database to check whether the subkeys are the same. The calling
1068 // code should take care, we don't re-import the same kesy over and over again.
1069 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
1071 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1075 if ( keyEd.version() != (*it).version() )
1076 continue; // different key ID (version)
1078 if ( keyEd.release() < (*it).release() )
1080 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1088 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1092 // We must explicitly delete old key IDs first (all releases,
1093 // that's why we don't call removePubkey here).
1094 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1096 opts.push_back ( "-e" );
1097 opts.push_back ( "--allmatches" );
1098 opts.push_back ( "--" );
1099 opts.push_back ( keyName.c_str() );
1100 // don't call modifyDatabase because it would remove the old
1101 // rpm3 database, if the current database is a temporary one.
1102 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1105 while ( systemReadLine( line ) )
1107 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1110 if ( systemStatus() != 0 )
1112 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1116 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1120 // import the new key
1122 opts.push_back ( "--import" );
1123 opts.push_back ( "--" );
1124 std::string pubkeypath( pubkey_r.path().asString() );
1125 opts.push_back ( pubkeypath.c_str() );
1127 // don't call modifyDatabase because it would remove the old
1128 // rpm3 database, if the current database is a temporary one.
1129 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1132 std::vector<std::string> excplines;
1133 while ( systemReadLine( line ) )
1135 if ( str::startsWith( line, "error:" ) )
1137 WAR << line << endl;
1138 excplines.push_back( std::move(line) );
1141 DBG << line << endl;
1144 if ( systemStatus() != 0 )
1146 // Translator: %1% is a gpg public key
1147 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
1148 excp.moveToHistory( excplines );
1149 excp.addHistory( std::move(error_message) );
1150 ZYPP_THROW( std::move(excp) );
1154 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1158 ///////////////////////////////////////////////////////////////////
1161 // METHOD NAME : RpmDb::removePubkey
1162 // METHOD TYPE : PMError
1164 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1166 FAILIFNOTINITIALIZED;
1168 // check if the key is in the rpm database and just
1169 // return if it does not.
1170 std::set<Edition> rpm_keys = pubkeyEditions();
1171 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
1172 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1174 for_( it, rpm_keys.begin(), rpm_keys.end() )
1176 if ( (*it).version() == pubkeyVersion )
1183 // the key does not exist, cannot be removed
1184 if (found_edition == rpm_keys.end())
1186 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1190 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
1193 opts.push_back ( "-e" );
1194 opts.push_back ( "--" );
1195 opts.push_back ( rpm_name.c_str() );
1197 // don't call modifyDatabase because it would remove the old
1198 // rpm3 database, if the current database is a temporary one.
1199 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1202 std::vector<std::string> excplines;
1203 while ( systemReadLine( line ) )
1205 if ( str::startsWith( line, "error:" ) )
1207 WAR << line << endl;
1208 excplines.push_back( std::move(line) );
1211 DBG << line << endl;
1214 if ( systemStatus() != 0 )
1216 // Translator: %1% is a gpg public key
1217 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
1218 excp.moveToHistory( excplines );
1219 excp.addHistory( std::move(error_message) );
1220 ZYPP_THROW( std::move(excp) );
1224 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1228 ///////////////////////////////////////////////////////////////////
1231 // METHOD NAME : RpmDb::pubkeys
1232 // METHOD TYPE : std::set<Edition>
1234 std::list<PublicKey> RpmDb::pubkeys() const
1236 std::list<PublicKey> ret;
1238 librpmDb::db_const_iterator it;
1239 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1241 Edition edition = it->tag_edition();
1242 if (edition != Edition::noedition)
1244 // we export the rpm key into a file
1245 RpmHeader::constPtr result;
1246 getData( "gpg-pubkey", edition, result );
1247 TmpFile file(getZYpp()->tmpPath());
1251 os.open(file.path().asString().c_str());
1252 // dump rpm key into the tmp file
1253 os << result->tag_description();
1254 //MIL << "-----------------------------------------------" << endl;
1255 //MIL << result->tag_description() <<endl;
1256 //MIL << "-----------------------------------------------" << endl;
1258 // read the public key from the dumped file
1259 PublicKey key(file);
1262 catch ( std::exception & e )
1264 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1265 // just ignore the key
1272 std::set<Edition> RpmDb::pubkeyEditions() const
1274 std::set<Edition> ret;
1276 librpmDb::db_const_iterator it;
1277 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1279 Edition edition = it->tag_edition();
1280 if (edition != Edition::noedition)
1281 ret.insert( edition );
1287 ///////////////////////////////////////////////////////////////////
1290 // METHOD NAME : RpmDb::fileList
1291 // METHOD TYPE : bool
1296 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
1298 std::list<FileInfo> result;
1300 librpmDb::db_const_iterator it;
1302 if (edition_r == Edition::noedition)
1304 found = it.findPackage( name_r );
1308 found = it.findPackage( name_r, edition_r );
1317 ///////////////////////////////////////////////////////////////////
1320 // METHOD NAME : RpmDb::hasFile
1321 // METHOD TYPE : bool
1325 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
1327 librpmDb::db_const_iterator it;
1331 res = it.findByFile( file_r );
1333 if (!name_r.empty())
1335 res = (it->tag_name() == name_r);
1343 ///////////////////////////////////////////////////////////////////
1346 // METHOD NAME : RpmDb::whoOwnsFile
1347 // METHOD TYPE : std::string
1351 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
1353 librpmDb::db_const_iterator it;
1354 if (it.findByFile( file_r ))
1356 return it->tag_name();
1361 ///////////////////////////////////////////////////////////////////
1364 // METHOD NAME : RpmDb::hasProvides
1365 // METHOD TYPE : bool
1369 bool RpmDb::hasProvides( const std::string & tag_r ) const
1371 librpmDb::db_const_iterator it;
1372 return it.findByProvides( tag_r );
1375 ///////////////////////////////////////////////////////////////////
1378 // METHOD NAME : RpmDb::hasRequiredBy
1379 // METHOD TYPE : bool
1383 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1385 librpmDb::db_const_iterator it;
1386 return it.findByRequiredBy( tag_r );
1389 ///////////////////////////////////////////////////////////////////
1392 // METHOD NAME : RpmDb::hasConflicts
1393 // METHOD TYPE : bool
1397 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1399 librpmDb::db_const_iterator it;
1400 return it.findByConflicts( tag_r );
1403 ///////////////////////////////////////////////////////////////////
1406 // METHOD NAME : RpmDb::hasPackage
1407 // METHOD TYPE : bool
1411 bool RpmDb::hasPackage( const std::string & name_r ) const
1413 librpmDb::db_const_iterator it;
1414 return it.findPackage( name_r );
1417 ///////////////////////////////////////////////////////////////////
1420 // METHOD NAME : RpmDb::hasPackage
1421 // METHOD TYPE : bool
1425 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1427 librpmDb::db_const_iterator it;
1428 return it.findPackage( name_r, ed_r );
1431 ///////////////////////////////////////////////////////////////////
1434 // METHOD NAME : RpmDb::getData
1435 // METHOD TYPE : PMError
1439 void RpmDb::getData( const std::string & name_r,
1440 RpmHeader::constPtr & result_r ) const
1442 librpmDb::db_const_iterator it;
1443 it.findPackage( name_r );
1446 ZYPP_THROW(*(it.dbError()));
1449 ///////////////////////////////////////////////////////////////////
1452 // METHOD NAME : RpmDb::getData
1453 // METHOD TYPE : void
1457 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1458 RpmHeader::constPtr & result_r ) const
1460 librpmDb::db_const_iterator it;
1461 it.findPackage( name_r, ed_r );
1464 ZYPP_THROW(*(it.dbError()));
1467 ///////////////////////////////////////////////////////////////////
1470 struct RpmlogCapture : public std::string
1473 { rpmlog()._cap = this; }
1476 { rpmlog()._cap = nullptr; }
1484 rpmlogSetCallback( rpmLogCB, this );
1485 rpmSetVerbosity( RPMLOG_INFO );
1486 _f = ::fopen( "/dev/null","w");
1487 rpmlogSetFile( _f );
1491 { if ( _f ) ::fclose( _f ); }
1493 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1494 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1496 int rpmLog( rpmlogRec rec_r )
1498 if ( _cap ) (*_cap) = rpmlogRecMessage( rec_r );
1499 return RPMLOG_DEFAULT;
1506 static Rpmlog & rpmlog()
1507 { static Rpmlog _rpmlog; return _rpmlog; }
1510 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1511 const Pathname & root_r, // target root
1512 bool requireGPGSig_r, // whether no gpg signature is to be reported
1513 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1515 PathInfo file( path_r );
1516 if ( ! file.isFile() )
1518 ERR << "Not a file: " << file << endl;
1519 return RpmDb::CHK_ERROR;
1522 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1523 if ( fd == 0 || ::Ferror(fd) )
1525 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1528 return RpmDb::CHK_ERROR;
1530 rpmts ts = ::rpmtsCreate();
1531 ::rpmtsSetRootDir( ts, root_r.c_str() );
1532 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1534 rpmQVKArguments_s qva;
1535 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1536 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1538 RpmlogCapture vresult;
1539 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1544 // results per line...
1545 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1546 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1547 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1548 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1550 // TODO: try to get SIG info from the header rather than parsing the output
1551 std::vector<std::string> lines;
1552 str::split( vresult, std::back_inserter(lines), "\n" );
1553 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1555 for ( unsigned i = 1; i < lines.size(); ++i )
1557 std::string & line( lines[i] );
1558 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1559 if ( line.find( ": OK" ) != std::string::npos )
1561 lineres = RpmDb::CHK_OK;
1562 if ( line.find( "Signature, key ID" ) == std::string::npos )
1563 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1565 else if ( line.find( ": NOKEY" ) != std::string::npos )
1566 { lineres = RpmDb::CHK_NOKEY; }
1567 else if ( line.find( ": BAD" ) != std::string::npos )
1568 { lineres = RpmDb::CHK_FAIL; }
1569 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1570 { lineres = RpmDb::CHK_NOTFOUND; }
1571 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1572 { lineres = RpmDb::CHK_NOTTRUSTED; }
1575 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1578 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1580 if ( count[RpmDb::CHK_FAIL] )
1581 ret = RpmDb::CHK_FAIL;
1583 else if ( count[RpmDb::CHK_NOTFOUND] )
1584 ret = RpmDb::CHK_NOTFOUND;
1586 else if ( count[RpmDb::CHK_NOKEY] )
1587 ret = RpmDb::CHK_NOKEY;
1589 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1590 ret = RpmDb::CHK_NOTTRUSTED;
1592 else if ( ret == RpmDb::CHK_OK )
1594 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1596 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1597 if ( requireGPGSig_r )
1598 ret = RpmDb::CHK_NOSIG;
1602 if ( ret != RpmDb::CHK_OK )
1604 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1611 ///////////////////////////////////////////////////////////////////
1613 // METHOD NAME : RpmDb::checkPackage
1614 // METHOD TYPE : RpmDb::CheckPackageResult
1616 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1617 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1619 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1620 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1622 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1623 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1626 // determine changed files of installed package
1628 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1634 if ( ! initialized() ) return false;
1638 opts.push_back ("-V");
1639 opts.push_back ("--nodeps");
1640 opts.push_back ("--noscripts");
1641 opts.push_back ("--nomd5");
1642 opts.push_back ("--");
1643 opts.push_back (packageName.c_str());
1645 run_rpm (opts, ExternalProgram::Discard_Stderr);
1647 if ( process == NULL )
1658 M Mode (includes permissions and file type)
1662 while (systemReadLine(line))
1664 if (line.length() > 12 &&
1665 (line[0] == 'S' || line[0] == 's' ||
1666 (line[0] == '.' && line[7] == 'T')))
1668 // file has been changed
1669 std::string filename;
1671 filename.assign(line, 11, line.length() - 11);
1672 fileList.insert(filename);
1677 // exit code ignored, rpm returns 1 no matter if package is installed or
1685 /****************************************************************/
1686 /* private member-functions */
1687 /****************************************************************/
1689 /*--------------------------------------------------------------*/
1690 /* Run rpm with the specified arguments, handling stderr */
1691 /* as specified by disp */
1692 /*--------------------------------------------------------------*/
1694 RpmDb::run_rpm (const RpmArgVec& opts,
1695 ExternalProgram::Stderr_Disposition disp)
1704 if ( ! initialized() )
1706 ZYPP_THROW(RpmDbNotOpenException());
1711 // always set root and dbpath
1712 #if defined(WORKAROUNDRPMPWDBUG)
1713 args.push_back("#/"); // chdir to / to workaround bnc#819354
1715 args.push_back("rpm");
1716 args.push_back("--root");
1717 args.push_back(_root.asString().c_str());
1718 args.push_back("--dbpath");
1719 args.push_back(_dbPath.asString().c_str());
1721 const char* argv[args.size() + opts.size() + 1];
1723 const char** p = argv;
1724 p = copy (args.begin (), args.end (), p);
1725 p = copy (opts.begin (), opts.end (), p);
1728 // Invalidate all outstanding database handles in case
1729 // the database gets modified.
1730 librpmDb::dbRelease( true );
1732 // Launch the program with default locale
1733 process = new ExternalProgram(argv, disp, false, -1, true);
1737 /*--------------------------------------------------------------*/
1738 /* Read a line from the rpm process */
1739 /*--------------------------------------------------------------*/
1740 bool RpmDb::systemReadLine( std::string & line )
1744 if ( process == NULL )
1747 if ( process->inputFile() )
1749 process->setBlocking( false );
1750 FILE * inputfile = process->inputFile();
1751 int inputfileFd = ::fileno( inputfile );
1754 /* Watch inputFile to see when it has input. */
1757 FD_SET( inputfileFd, &rfds );
1759 /* Wait up to 5 seconds. */
1764 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1768 ERR << "select error: " << strerror(errno) << endl;
1769 if ( errno != EINTR )
1774 // Data is available now.
1775 static size_t linebuffer_size = 0; // static because getline allocs
1776 static char * linebuffer = 0; // and reallocs if buffer is too small
1777 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1780 if ( ::feof( inputfile ) )
1781 return line.size(); // in case of pending output
1787 if ( linebuffer[nread-1] == '\n' )
1789 line += std::string( linebuffer, nread );
1792 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1793 return true; // complete line
1795 clearerr( inputfile );
1799 // No data within time.
1800 if ( ! process->running() )
1809 /*--------------------------------------------------------------*/
1810 /* Return the exit status of the rpm process, closing the */
1811 /* connection if not already done */
1812 /*--------------------------------------------------------------*/
1814 RpmDb::systemStatus()
1816 if ( process == NULL )
1819 exit_code = process->close();
1823 error_message = process->execError();
1828 // DBG << "exit code " << exit_code << endl;
1833 /*--------------------------------------------------------------*/
1834 /* Forcably kill the rpm process */
1835 /*--------------------------------------------------------------*/
1839 if (process) process->kill();
1843 // generate diff mails for config files
1844 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1846 std::string msg = line.substr(9);
1847 std::string::size_type pos1 = std::string::npos;
1848 std::string::size_type pos2 = std::string::npos;
1849 std::string file1s, file2s;
1853 pos1 = msg.find (typemsg);
1856 if ( pos1 == std::string::npos )
1859 pos2 = pos1 + strlen (typemsg);
1861 if (pos2 >= msg.length() )
1864 file1 = msg.substr (0, pos1);
1865 file2 = msg.substr (pos2);
1867 file1s = file1.asString();
1868 file2s = file2.asString();
1870 if (!_root.empty() && _root != "/")
1872 file1 = _root + file1;
1873 file2 = _root + file2;
1877 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1880 Pathname file = _root + WARNINGMAILPATH;
1881 if (filesystem::assert_dir(file) != 0)
1883 ERR << "Could not create " << file.asString() << endl;
1886 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1887 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1890 ERR << "Could not open " << file << endl;
1894 // Translator: %s = name of an rpm package. A list of diffs follows
1896 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1899 ERR << "diff failed" << endl;
1900 notify << str::form(difffailmsg,
1901 file1s.c_str(), file2s.c_str()) << endl;
1905 notify << str::form(diffgenmsg,
1906 file1s.c_str(), file2s.c_str()) << endl;
1908 // remove root for the viewer's pleasure (#38240)
1909 if (!_root.empty() && _root != "/")
1911 if (out.substr(0,4) == "--- ")
1913 out.replace(4, file1.asString().length(), file1s);
1915 std::string::size_type pos = out.find("\n+++ ");
1916 if (pos != std::string::npos)
1918 out.replace(pos+5, file2.asString().length(), file2s);
1921 notify << out << endl;
1924 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1929 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1935 ///////////////////////////////////////////////////////////////////
1938 // METHOD NAME : RpmDb::installPackage
1939 // METHOD TYPE : PMError
1941 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1943 callback::SendReport<RpmInstallReport> report;
1945 report->start(filename);
1950 doInstallPackage(filename, flags, report);
1954 catch (RpmException & excpt_r)
1956 RpmInstallReport::Action user = report->problem( excpt_r );
1958 if ( user == RpmInstallReport::ABORT )
1960 report->finish( excpt_r );
1961 ZYPP_RETHROW(excpt_r);
1963 else if ( user == RpmInstallReport::IGNORE )
1971 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1973 FAILIFNOTINITIALIZED;
1974 HistoryLog historylog;
1976 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1980 if ( _packagebackups )
1982 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1983 if ( ! backupPackage( filename ) )
1985 ERR << "backup of " << filename.asString() << " failed" << endl;
1987 // FIXME status handling
1988 report->progress( 0 ); // allow 1% for backup creation.
1993 if (flags & RPMINST_NOUPGRADE)
1994 opts.push_back("-i");
1996 opts.push_back("-U");
1998 opts.push_back("--percent");
1999 opts.push_back("--noglob");
2001 // ZConfig defines cross-arch installation
2002 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
2003 opts.push_back("--ignorearch");
2005 if (flags & RPMINST_NODIGEST)
2006 opts.push_back("--nodigest");
2007 if (flags & RPMINST_NOSIGNATURE)
2008 opts.push_back("--nosignature");
2009 if (flags & RPMINST_EXCLUDEDOCS)
2010 opts.push_back ("--excludedocs");
2011 if (flags & RPMINST_NOSCRIPTS)
2012 opts.push_back ("--noscripts");
2013 if (flags & RPMINST_FORCE)
2014 opts.push_back ("--force");
2015 if (flags & RPMINST_NODEPS)
2016 opts.push_back ("--nodeps");
2017 if (flags & RPMINST_IGNORESIZE)
2018 opts.push_back ("--ignoresize");
2019 if (flags & RPMINST_JUSTDB)
2020 opts.push_back ("--justdb");
2021 if (flags & RPMINST_TEST)
2022 opts.push_back ("--test");
2023 if (flags & RPMINST_NOPOSTTRANS)
2024 opts.push_back ("--noposttrans");
2026 opts.push_back("--");
2028 // rpm requires additional quoting of special chars:
2029 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
2030 opts.push_back ( quotedFilename.c_str() );
2032 modifyDatabase(); // BEFORE run_rpm
2033 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
2036 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2037 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
2039 unsigned linecnt = 0;
2040 while ( systemReadLine( line ) )
2042 if ( str::startsWith( line, "%%" ) )
2045 sscanf( line.c_str() + 2, "%d", &percent );
2046 report->progress( percent );
2050 if ( linecnt < MAXRPMMESSAGELINES )
2052 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2055 rpmmsg += line+'\n';
2057 if ( str::startsWith( line, "warning:" ) )
2058 configwarnings.push_back(line);
2060 if ( linecnt >= MAXRPMMESSAGELINES )
2061 rpmmsg += "[truncated]\n";
2063 int rpm_status = systemStatus();
2066 for (std::vector<std::string>::iterator it = configwarnings.begin();
2067 it != configwarnings.end(); ++it)
2069 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2071 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2073 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2074 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2076 _("rpm created %s as %s, but it was impossible to determine the difference"),
2078 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2081 if ( rpm_status != 0 )
2084 str::form("%s install failed", Pathname::basename(filename).c_str()),
2085 true /*timestamp*/);
2086 std::ostringstream sstr;
2087 sstr << "rpm output:" << endl << rpmmsg << endl;
2088 historylog.comment(sstr.str());
2089 // TranslatorExplanation the colon is followed by an error message
2090 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
2092 else if ( ! rpmmsg.empty() )
2095 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2096 true /*timestamp*/);
2097 std::ostringstream sstr;
2098 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2099 historylog.comment(sstr.str());
2101 // report additional rpm output in finish
2102 // TranslatorExplanation Text is followed by a ':' and the actual output.
2103 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2107 ///////////////////////////////////////////////////////////////////
2110 // METHOD NAME : RpmDb::removePackage
2111 // METHOD TYPE : PMError
2113 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2115 // 'rpm -e' does not like epochs
2116 return removePackage( package->name()
2117 + "-" + package->edition().version()
2118 + "-" + package->edition().release()
2119 + "." + package->arch().asString(), flags );
2122 ///////////////////////////////////////////////////////////////////
2125 // METHOD NAME : RpmDb::removePackage
2126 // METHOD TYPE : PMError
2128 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
2130 callback::SendReport<RpmRemoveReport> report;
2132 report->start( name_r );
2137 doRemovePackage(name_r, flags, report);
2141 catch (RpmException & excpt_r)
2143 RpmRemoveReport::Action user = report->problem( excpt_r );
2145 if ( user == RpmRemoveReport::ABORT )
2147 report->finish( excpt_r );
2148 ZYPP_RETHROW(excpt_r);
2150 else if ( user == RpmRemoveReport::IGNORE )
2159 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2161 FAILIFNOTINITIALIZED;
2162 HistoryLog historylog;
2164 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2167 if ( _packagebackups )
2169 // FIXME solve this status report somehow
2170 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2171 if ( ! backupPackage( name_r ) )
2173 ERR << "backup of " << name_r << " failed" << endl;
2175 report->progress( 0 );
2179 report->progress( 100 );
2184 opts.push_back("-e");
2185 opts.push_back("--allmatches");
2187 if (flags & RPMINST_NOSCRIPTS)
2188 opts.push_back("--noscripts");
2189 if (flags & RPMINST_NODEPS)
2190 opts.push_back("--nodeps");
2191 if (flags & RPMINST_JUSTDB)
2192 opts.push_back("--justdb");
2193 if (flags & RPMINST_TEST)
2194 opts.push_back ("--test");
2195 if (flags & RPMINST_FORCE)
2197 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2200 opts.push_back("--");
2201 opts.push_back(name_r.c_str());
2203 modifyDatabase(); // BEFORE run_rpm
2204 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2207 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2209 // got no progress from command, so we fake it:
2210 // 5 - command started
2211 // 50 - command completed
2213 report->progress( 5 );
2214 unsigned linecnt = 0;
2215 while (systemReadLine(line))
2217 if ( linecnt < MAXRPMMESSAGELINES )
2219 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2221 rpmmsg += line+'\n';
2223 if ( linecnt >= MAXRPMMESSAGELINES )
2224 rpmmsg += "[truncated]\n";
2225 report->progress( 50 );
2226 int rpm_status = systemStatus();
2228 if ( rpm_status != 0 )
2231 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2232 std::ostringstream sstr;
2233 sstr << "rpm output:" << endl << rpmmsg << endl;
2234 historylog.comment(sstr.str());
2235 // TranslatorExplanation the colon is followed by an error message
2236 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
2238 else if ( ! rpmmsg.empty() )
2241 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2243 std::ostringstream sstr;
2244 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2245 historylog.comment(sstr.str());
2247 // report additional rpm output in finish
2248 // TranslatorExplanation Text is followed by a ':' and the actual output.
2249 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2253 ///////////////////////////////////////////////////////////////////
2256 // METHOD NAME : RpmDb::backupPackage
2257 // METHOD TYPE : bool
2259 bool RpmDb::backupPackage( const Pathname & filename )
2261 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2265 return backupPackage( h->tag_name() );
2268 ///////////////////////////////////////////////////////////////////
2271 // METHOD NAME : RpmDb::backupPackage
2272 // METHOD TYPE : bool
2274 bool RpmDb::backupPackage(const std::string& packageName)
2276 HistoryLog progresslog;
2278 Pathname backupFilename;
2279 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2281 if (_backuppath.empty())
2283 INT << "_backuppath empty" << endl;
2289 if (!queryChangedFiles(fileList, packageName))
2291 ERR << "Error while getting changed files for package " <<
2292 packageName << endl;
2296 if (fileList.size() <= 0)
2298 DBG << "package " << packageName << " not changed -> no backup" << endl;
2302 if (filesystem::assert_dir(_root + _backuppath) != 0)
2308 // build up archive name
2309 time_t currentTime = time(0);
2310 struct tm *currentLocalTime = localtime(¤tTime);
2312 int date = (currentLocalTime->tm_year + 1900) * 10000
2313 + (currentLocalTime->tm_mon + 1) * 100
2314 + currentLocalTime->tm_mday;
2319 backupFilename = _root + _backuppath
2320 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2323 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2325 PathInfo pi(filestobackupfile);
2326 if (pi.isExist() && !pi.isFile())
2328 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2332 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2336 ERR << "could not open " << filestobackupfile.asString() << endl;
2340 for (FileList::const_iterator cit = fileList.begin();
2341 cit != fileList.end(); ++cit)
2343 std::string name = *cit;
2344 if ( name[0] == '/' )
2346 // remove slash, file must be relative to -C parameter of tar
2347 name = name.substr( 1 );
2349 DBG << "saving file "<< name << endl;
2354 const char* const argv[] =
2359 _root.asString().c_str(),
2360 "--ignore-failed-read",
2362 backupFilename.asString().c_str(),
2364 filestobackupfile.asString().c_str(),
2368 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2369 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2373 // TODO: its probably possible to start tar with -v and watch it adding
2374 // files to report progress
2375 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2380 int ret = tar.close();
2384 ERR << "tar failed: " << tarmsg << endl;
2389 MIL << "tar backup ok" << endl;
2390 progresslog.comment(
2391 str::form(_("created backup %s"), backupFilename.asString().c_str())
2392 , /*timestamp*/true);
2395 filesystem::unlink(filestobackupfile);
2401 void RpmDb::setBackupPath(const Pathname& path)
2406 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2410 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2411 // translators: possible rpm package signature check result [brief]
2412 OUTS( CHK_OK, _("Signature is OK") );
2413 // translators: possible rpm package signature check result [brief]
2414 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2415 // translators: possible rpm package signature check result [brief]
2416 OUTS( CHK_FAIL, _("Signature does not verify") );
2417 // translators: possible rpm package signature check result [brief]
2418 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2419 // translators: possible rpm package signature check result [brief]
2420 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2421 // translators: possible rpm package signature check result [brief]
2422 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2423 // translators: possible rpm package signature check result [brief]
2424 OUTS( CHK_NOSIG, _("File is unsigned") );
2427 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2430 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2432 for ( const auto & el : obj )
2433 str << el.second << endl;
2438 } // namespace target