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"
37 #include "zypp/Date.h"
38 #include "zypp/Pathname.h"
39 #include "zypp/PathInfo.h"
40 #include "zypp/PublicKey.h"
42 #include "zypp/target/rpm/RpmDb.h"
43 #include "zypp/target/rpm/RpmCallbacks.h"
45 #include "zypp/HistoryLog.h"
46 #include "zypp/target/rpm/librpmDb.h"
47 #include "zypp/target/rpm/RpmException.h"
48 #include "zypp/TmpPath.h"
49 #include "zypp/KeyRing.h"
50 #include "zypp/ZYppFactory.h"
51 #include "zypp/ZConfig.h"
54 using namespace zypp::filesystem;
56 #define WARNINGMAILPATH "/var/log/YaST2/"
57 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
58 #define MAXRPMMESSAGELINES 10000
60 #define WORKAROUNDRPMPWDBUG
62 #undef ZYPP_BASE_LOGGER_LOGGROUP
63 #define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
67 namespace zypp_readonly_hack
69 bool IGotIt(); // in readonly-mode
77 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
78 const char* quoteInFilename_m = "\'\"";
80 const char* quoteInFilename_m = " \t\'\"";
82 inline std::string rpmQuoteFilename( const Pathname & path_r )
84 std::string path( path_r.asString() );
85 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
86 pos != std::string::npos;
87 pos = path.find_first_of( quoteInFilename_m, pos ) )
89 path.insert( pos, "\\" );
90 pos += 2; // skip '\\' and the quoted char.
96 /** Workaround bnc#827609 - rpm needs a readable pwd so we
97 * chdir to /. Turn realtive pathnames into absolute ones
98 * by prepending cwd so rpm still finds them
100 inline Pathname workaroundRpmPwdBug( Pathname path_r )
102 #if defined(WORKAROUNDRPMPWDBUG)
103 if ( path_r.relative() )
105 // try to prepend cwd
106 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
108 return Pathname( cwd ) / path_r;
109 WAR << "Can't get cwd!" << endl;
112 return path_r; // no problem with absolute pathnames
116 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
118 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
123 ~KeyRingSignalReceiver()
128 virtual void trustedKeyAdded( const PublicKey &key )
130 MIL << "trusted key added to zypp Keyring. Importing..." << endl;
131 _rpmdb.importPubkey( key );
134 virtual void trustedKeyRemoved( const PublicKey &key )
136 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
137 _rpmdb.removePubkey( key );
143 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
145 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
155 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
162 for (line = prog.receiveLine(), count=0;
164 line = prog.receiveLine(), count++ )
166 if (maxlines<0?true:count<maxlines)
175 /******************************************************************
178 ** FUNCTION NAME : stringPath
179 ** FUNCTION TYPE : inline std::string
181 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
183 return librpmDb::stringPath( root_r, sub_r );
186 /******************************************************************
189 ** FUNCTION NAME : operator<<
190 ** FUNCTION TYPE : std::ostream &
192 std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
194 if ( obj == RpmDb::DbSI_NO_INIT )
200 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
202 ENUM_OUT( DbSI_HAVE_V4, 'X' );
203 ENUM_OUT( DbSI_MADE_V4, 'c' );
204 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
206 ENUM_OUT( DbSI_HAVE_V3, 'X' );
207 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
208 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
217 ///////////////////////////////////////////////////////////////////
219 // CLASS NAME : RpmDb
221 ///////////////////////////////////////////////////////////////////
223 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
225 ///////////////////////////////////////////////////////////////////
227 ///////////////////////////////////////////////////////////////////
230 // METHOD NAME : RpmDb::RpmDb
231 // METHOD TYPE : Constructor
234 : _dbStateInfo( DbSI_NO_INIT )
235 #warning Check for obsolete memebers
236 , _backuppath ("/var/adm/backup")
237 , _packagebackups(false)
238 , _warndirexists(false)
242 librpmDb::globalInit();
243 // Some rpm versions are patched not to abort installation if
244 // symlink creation failed.
245 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
246 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
249 ///////////////////////////////////////////////////////////////////
252 // METHOD NAME : RpmDb::~RpmDb
253 // METHOD TYPE : Destructor
257 MIL << "~RpmDb()" << endl;
260 MIL << "~RpmDb() end" << endl;
261 sKeyRingReceiver.reset();
264 Date RpmDb::timestamp() const
269 if ( dbPath().empty() )
270 db_path = "/var/lib/rpm";
274 PathInfo rpmdb_info(root() + db_path + "/Packages");
276 if ( rpmdb_info.isExist() )
277 return rpmdb_info.mtime();
281 ///////////////////////////////////////////////////////////////////
284 // METHOD NAME : RpmDb::dumpOn
285 // METHOD TYPE : std::ostream &
287 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
291 if ( _dbStateInfo == DbSI_NO_INIT )
297 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
299 ENUM_OUT( DbSI_HAVE_V4, 'X' );
300 ENUM_OUT( DbSI_MADE_V4, 'c' );
301 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
303 ENUM_OUT( DbSI_HAVE_V3, 'X' );
304 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
305 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
306 str << "): " << stringPath( _root, _dbPath );
312 ///////////////////////////////////////////////////////////////////
315 // METHOD NAME : RpmDb::initDatabase
316 // METHOD TYPE : PMError
318 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
320 ///////////////////////////////////////////////////////////////////
322 ///////////////////////////////////////////////////////////////////
323 bool quickinit( root_r.empty() );
325 if ( root_r.empty() )
328 if ( dbPath_r.empty() )
329 dbPath_r = "/var/lib/rpm";
331 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
333 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
334 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
337 if ( dbPath_r == "/var/lib/rpm"
338 && ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
339 && PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
341 WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
342 filesystem::symlink( "../../usr/lib/sysimage/rpm", root_r/"/var/lib/rpm" );
345 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
346 << ( doRebuild_r ? " (rebuilddb)" : "" )
347 << ( quickinit ? " (quickinit)" : "" ) << endl;
349 ///////////////////////////////////////////////////////////////////
350 // Check whether already initialized
351 ///////////////////////////////////////////////////////////////////
354 if ( root_r == _root && dbPath_r == _dbPath )
360 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
364 ///////////////////////////////////////////////////////////////////
366 ///////////////////////////////////////////////////////////////////
367 librpmDb::unblockAccess();
371 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
375 DbStateInfoBits info = DbSI_NO_INIT;
378 internal_initDatabase( root_r, dbPath_r, info );
380 catch (const RpmException & excpt_r)
382 ZYPP_CAUGHT(excpt_r);
383 librpmDb::blockAccess();
384 ERR << "Cleanup on error: state " << info << endl;
386 if ( dbsi_has( info, DbSI_MADE_V4 ) )
388 // remove the newly created rpm4 database and
389 // any backup created on conversion.
390 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
392 ZYPP_RETHROW(excpt_r);
394 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
396 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
398 // Move obsolete rpm3 database beside.
399 MIL << "Cleanup: state " << info << endl;
400 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
401 dbsi_clr( info, DbSI_HAVE_V3 );
405 // Performing an update: Keep the original rpm3 database
406 // and wait if the rpm4 database gets modified by installing
407 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
408 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
411 #warning CHECK: notify root about conversion backup.
419 if ( dbsi_has( info, DbSI_HAVE_V4 )
420 && ! dbsi_has( info, DbSI_MADE_V4 ) )
426 MIL << "Synchronizing keys with zypp keyring" << endl;
429 // Close the database in case any write acces (create/convert)
430 // happened during init. This should drop any lock acquired
431 // by librpm. On demand it will be reopened readonly and should
432 // not hold any lock.
433 librpmDb::dbRelease( true );
435 MIL << "InitDatabase: " << *this << endl;
438 ///////////////////////////////////////////////////////////////////
441 // METHOD NAME : RpmDb::internal_initDatabase
442 // METHOD TYPE : PMError
444 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
445 DbStateInfoBits & info_r )
447 info_r = DbSI_NO_INIT;
449 ///////////////////////////////////////////////////////////////////
450 // Get info about the desired database dir
451 ///////////////////////////////////////////////////////////////////
452 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
454 if ( dbInfo.illegalArgs() )
456 // should not happen (checked in initDatabase)
457 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
459 if ( ! dbInfo.usableArgs() )
461 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
462 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
465 if ( dbInfo.hasDbV4() )
467 dbsi_set( info_r, DbSI_HAVE_V4 );
468 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
472 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
475 if ( dbInfo.hasDbV3() )
477 dbsi_set( info_r, DbSI_HAVE_V3 );
479 if ( dbInfo.hasDbV3ToV4() )
481 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
484 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
485 librpmDb::dumpState( DBG ) << endl;
487 ///////////////////////////////////////////////////////////////////
488 // Access database, create if needed
489 ///////////////////////////////////////////////////////////////////
491 // creates dbdir and empty rpm4 database if not present
492 librpmDb::dbAccess( root_r, dbPath_r );
494 if ( ! dbInfo.hasDbV4() )
497 if ( dbInfo.hasDbV4() )
499 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
503 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
504 librpmDb::dumpState( DBG ) << endl;
506 ///////////////////////////////////////////////////////////////////
507 // Check whether to convert something. Create backup but do
508 // not remove anything here
509 ///////////////////////////////////////////////////////////////////
510 librpmDb::constPtr dbptr;
511 librpmDb::dbAccess( dbptr );
512 bool dbEmpty = dbptr->empty();
515 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
518 if ( dbInfo.hasDbV3() )
520 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
524 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
525 convertV3toV4( dbInfo.dbV3().path(), dbptr );
527 // create a backup copy
528 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
531 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
536 if ( dbInfo.hasDbV3ToV4() )
538 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
539 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
547 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
548 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
549 dbsi_set( info_r, DbSI_MODIFIED_V4 );
553 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
554 librpmDb::dumpState( DBG ) << endl;
557 if ( dbInfo.hasDbV3ToV4() )
559 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
563 ///////////////////////////////////////////////////////////////////
566 // METHOD NAME : RpmDb::removeV4
567 // METHOD TYPE : void
569 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
571 const char * v3backup = "packages.rpm3";
572 const char * master = "Packages";
573 const char * index[] =
595 PathInfo pi( dbdir_r );
598 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
602 for ( const char ** f = index; *f; ++f )
607 filesystem::unlink( pi.path() );
611 pi( dbdir_r + master );
614 MIL << "Removing rpm4 database " << pi << endl;
615 filesystem::unlink( pi.path() );
620 pi( dbdir_r + v3backup );
623 MIL << "Removing converted rpm3 database backup " << pi << endl;
624 filesystem::unlink( pi.path() );
629 ///////////////////////////////////////////////////////////////////
632 // METHOD NAME : RpmDb::removeV3
633 // METHOD TYPE : void
635 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
637 const char * master = "packages.rpm";
638 const char * index[] =
640 "conflictsindex.rpm",
651 PathInfo pi( dbdir_r );
654 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
658 for ( const char ** f = index; *f; ++f )
663 filesystem::unlink( pi.path() );
667 #warning CHECK: compare vs existing v3 backup. notify root
668 pi( dbdir_r + master );
671 Pathname m( pi.path() );
674 // backup was already created
675 filesystem::unlink( m );
676 Pathname b( m.extend( "3" ) );
677 pi( b ); // stat backup
681 Pathname b( m.extend( ".deleted" ) );
685 // rempve existing backup
686 filesystem::unlink( b );
688 filesystem::rename( m, b );
689 pi( b ); // stat backup
691 MIL << "(Re)moved rpm3 database to " << pi << endl;
695 ///////////////////////////////////////////////////////////////////
698 // METHOD NAME : RpmDb::modifyDatabase
699 // METHOD TYPE : void
701 void RpmDb::modifyDatabase()
703 if ( ! initialized() )
706 // tag database as modified
707 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
709 // Move outdated rpm3 database beside.
710 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
712 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
713 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
714 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
718 ///////////////////////////////////////////////////////////////////
721 // METHOD NAME : RpmDb::closeDatabase
722 // METHOD TYPE : PMError
724 void RpmDb::closeDatabase()
726 if ( ! initialized() )
731 MIL << "Calling closeDatabase: " << *this << endl;
733 ///////////////////////////////////////////////////////////////////
734 // Block further database access
735 ///////////////////////////////////////////////////////////////////
736 librpmDb::blockAccess();
738 ///////////////////////////////////////////////////////////////////
739 // Check fate if old version database still present
740 ///////////////////////////////////////////////////////////////////
741 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
743 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
744 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
746 // Move outdated rpm3 database beside.
747 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
751 // Remove unmodified rpm4 database
752 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
756 ///////////////////////////////////////////////////////////////////
758 ///////////////////////////////////////////////////////////////////
759 _root = _dbPath = Pathname();
760 _dbStateInfo = DbSI_NO_INIT;
762 MIL << "closeDatabase: " << *this << endl;
765 ///////////////////////////////////////////////////////////////////
768 // METHOD NAME : RpmDb::rebuildDatabase
769 // METHOD TYPE : PMError
771 void RpmDb::rebuildDatabase()
773 callback::SendReport<RebuildDBReport> report;
775 report->start( root() + dbPath() );
779 doRebuildDatabase(report);
781 catch (RpmException & excpt_r)
783 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
784 ZYPP_RETHROW(excpt_r);
786 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
789 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
791 FAILIFNOTINITIALIZED;
793 MIL << "RpmDb::rebuildDatabase" << *this << endl;
794 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
796 PathInfo dbMaster( root() + dbPath() + "Packages" );
797 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
801 opts.push_back("--rebuilddb");
802 opts.push_back("-vv");
804 // don't call modifyDatabase because it would remove the old
805 // rpm3 database, if the current database is a temporary one.
806 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
808 // progress report: watch this file growing
809 PathInfo newMaster( root()
810 + dbPath().extend( str::form( "rebuilddb.%d",
811 process?process->getpid():0) )
817 while ( systemReadLine( line ) )
820 { // file is removed at the end of rebuild.
821 // current size should be upper limit for new db
822 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
824 WAR << "User requested abort." << endl;
826 filesystem::recursive_rmdir( newMaster.path().dirname() );
830 if ( line.compare( 0, 2, "D:" ) )
832 errmsg += line + '\n';
833 // report.notify( line );
838 int rpm_status = systemStatus();
840 if ( rpm_status != 0 )
842 //TranslatorExplanation after semicolon is error message
843 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
847 report->progress( 100, root() + dbPath() ); // 100%
851 ///////////////////////////////////////////////////////////////////
854 /** \ref RpmDb::syncTrustedKeys helper
855 * Compute which keys need to be exprted to / imported from the zypp keyring.
856 * Return result via argument list.
858 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
860 ///////////////////////////////////////////////////////////////////
861 // Remember latest release and where it ocurred
865 : _inRpmKeys( nullptr )
866 , _inZyppKeys( nullptr )
869 void updateIf( const Edition & rpmKey_r )
871 std::string keyRelease( rpmKey_r.release() );
872 int comp = _release.compare( keyRelease );
875 // update to newer release
876 _release.swap( keyRelease );
877 _inRpmKeys = &rpmKey_r;
878 _inZyppKeys = nullptr;
879 if ( !keyRelease.empty() )
880 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
882 else if ( comp == 0 )
884 // stay with this release
886 _inRpmKeys = &rpmKey_r;
888 // else: this is an old release
890 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
893 void updateIf( const PublicKeyData & zyppKey_r )
895 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
896 int comp = _release.compare( keyRelease );
899 // update to newer release
900 _release.swap( keyRelease );
901 _inRpmKeys = nullptr;
902 _inZyppKeys = &zyppKey_r;
903 if ( !keyRelease.empty() )
904 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
906 else if ( comp == 0 )
908 // stay with this release
910 _inZyppKeys = &zyppKey_r;
912 // else: this is an old release
914 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
917 std::string _release;
918 const Edition * _inRpmKeys;
919 const PublicKeyData * _inZyppKeys;
921 ///////////////////////////////////////////////////////////////////
923 // collect keys by ID(version) and latest creation(release)
924 std::map<std::string,Key> _keymap;
926 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
928 _keymap[(*it).version()].updateIf( *it );
931 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
933 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
936 // compute missing keys
937 std::set<Edition> rpmKeys;
938 std::list<PublicKeyData> zyppKeys;
939 for_( it, _keymap.begin(), _keymap.end() )
941 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
942 << ( (*it).second._inRpmKeys ? "R" : "_" )
943 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
944 if ( ! (*it).second._inRpmKeys )
946 zyppKeys.push_back( *(*it).second._inZyppKeys );
948 if ( ! (*it).second._inZyppKeys )
950 rpmKeys.insert( *(*it).second._inRpmKeys );
953 rpmKeys_r.swap( rpmKeys );
954 zyppKeys_r.swap( zyppKeys );
957 ///////////////////////////////////////////////////////////////////
959 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
961 MIL << "Going to sync trusted keys..." << endl;
962 std::set<Edition> rpmKeys( pubkeyEditions() );
963 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
965 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
967 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
968 // when re-acquiring the zyppp lock. For now we remove all excess keys.
969 // TODO: Once we can safely assume that all PK versions are updated we
970 // can think about re-importing newer key versions found in the zypp keyring and
971 // removing only excess ones (but case is not very likely). Unfixed PK versions
972 // however will remove the newer version found in the zypp keyring and by doing
973 // this, the key here will be removed via callback as well (keys are deleted
974 // via gpg id, regardless of the edition).
975 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
976 // Temporarily disconnect to prevent the attempt to pass back the delete request.
977 callback::TempConnect<KeyRingSignals> tempDisconnect;
979 for ( const PublicKeyData & keyData : zyppKeys )
981 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
983 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
984 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
985 if ( !dirty ) dirty = true;
989 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
992 computeKeyRingSync( rpmKeys, zyppKeys );
993 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
994 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
996 ///////////////////////////////////////////////////////////////////
997 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
999 // export to zypp keyring
1000 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
1001 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
1002 callback::TempConnect<KeyRingSignals> tempDisconnect;
1003 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
1005 TmpFile tmpfile( getZYpp()->tmpPath() );
1007 std::ofstream tmpos( tmpfile.path().c_str() );
1008 for_( it, rpmKeys.begin(), rpmKeys.end() )
1010 // we export the rpm key into a file
1011 RpmHeader::constPtr result;
1012 getData( "gpg-pubkey", *it, result );
1013 tmpos << result->tag_description() << endl;
1018 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1019 // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
1020 // Modern rpm does not import those keys, but when migrating a pre SLE12 system
1021 // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
1022 std::set<Edition> missingKeys;
1023 for ( const Edition & key : rpmKeys )
1025 if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
1027 ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
1028 missingKeys.insert( key );
1030 if ( ! missingKeys.empty() )
1031 callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
1033 catch ( const Exception & excpt )
1035 ZYPP_CAUGHT( excpt );
1036 ERR << "Could not import keys into zypp keyring: " << endl;
1040 ///////////////////////////////////////////////////////////////////
1041 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1043 // import from zypp keyring
1044 MIL << "Importing zypp trusted keyring" << std::endl;
1045 for_( it, zyppKeys.begin(), zyppKeys.end() )
1049 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1051 catch ( const RpmException & exp )
1057 MIL << "Trusted keys synced." << endl;
1060 void RpmDb::importZyppKeyRingTrustedKeys()
1061 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1063 void RpmDb::exportTrustedKeysInZyppKeyRing()
1064 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1066 ///////////////////////////////////////////////////////////////////
1069 // METHOD NAME : RpmDb::importPubkey
1070 // METHOD TYPE : PMError
1072 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1074 FAILIFNOTINITIALIZED;
1076 // bnc#828672: On the fly key import in READONLY
1077 if ( zypp_readonly_hack::IGotIt() )
1079 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1083 // check if the key is already in the rpm database
1084 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1085 std::set<Edition> rpmKeys = pubkeyEditions();
1086 bool hasOldkeys = false;
1088 for_( it, rpmKeys.begin(), rpmKeys.end() )
1090 // bsc#1008325: Keys using subkeys for signing don't get a higher release
1091 // if new subkeys are added, because the primary key remains unchanged.
1092 // For now always re-import keys with subkeys. Here we don't want to export the
1093 // keys in the rpm database to check whether the subkeys are the same. The calling
1094 // code should take care, we don't re-import the same kesy over and over again.
1095 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
1097 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1101 if ( keyEd.version() != (*it).version() )
1102 continue; // different key ID (version)
1104 if ( keyEd.release() < (*it).release() )
1106 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1114 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1118 // We must explicitly delete old key IDs first (all releases,
1119 // that's why we don't call removePubkey here).
1120 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1122 opts.push_back ( "-e" );
1123 opts.push_back ( "--allmatches" );
1124 opts.push_back ( "--" );
1125 opts.push_back ( keyName.c_str() );
1126 // don't call modifyDatabase because it would remove the old
1127 // rpm3 database, if the current database is a temporary one.
1128 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1131 while ( systemReadLine( line ) )
1133 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1136 if ( systemStatus() != 0 )
1138 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1142 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1146 // import the new key
1148 opts.push_back ( "--import" );
1149 opts.push_back ( "--" );
1150 std::string pubkeypath( pubkey_r.path().asString() );
1151 opts.push_back ( pubkeypath.c_str() );
1153 // don't call modifyDatabase because it would remove the old
1154 // rpm3 database, if the current database is a temporary one.
1155 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1158 std::vector<std::string> excplines;
1159 while ( systemReadLine( line ) )
1161 if ( str::startsWith( line, "error:" ) )
1163 WAR << line << endl;
1164 excplines.push_back( std::move(line) );
1167 DBG << line << endl;
1170 if ( systemStatus() != 0 )
1172 // Translator: %1% is a gpg public key
1173 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
1174 excp.moveToHistory( excplines );
1175 excp.addHistory( std::move(error_message) );
1176 ZYPP_THROW( std::move(excp) );
1180 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1184 ///////////////////////////////////////////////////////////////////
1187 // METHOD NAME : RpmDb::removePubkey
1188 // METHOD TYPE : PMError
1190 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1192 FAILIFNOTINITIALIZED;
1194 // check if the key is in the rpm database and just
1195 // return if it does not.
1196 std::set<Edition> rpm_keys = pubkeyEditions();
1197 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
1198 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1200 for_( it, rpm_keys.begin(), rpm_keys.end() )
1202 if ( (*it).version() == pubkeyVersion )
1209 // the key does not exist, cannot be removed
1210 if (found_edition == rpm_keys.end())
1212 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1216 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
1219 opts.push_back ( "-e" );
1220 opts.push_back ( "--" );
1221 opts.push_back ( rpm_name.c_str() );
1223 // don't call modifyDatabase because it would remove the old
1224 // rpm3 database, if the current database is a temporary one.
1225 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1228 std::vector<std::string> excplines;
1229 while ( systemReadLine( line ) )
1231 if ( str::startsWith( line, "error:" ) )
1233 WAR << line << endl;
1234 excplines.push_back( std::move(line) );
1237 DBG << line << endl;
1240 if ( systemStatus() != 0 )
1242 // Translator: %1% is a gpg public key
1243 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
1244 excp.moveToHistory( excplines );
1245 excp.addHistory( std::move(error_message) );
1246 ZYPP_THROW( std::move(excp) );
1250 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1254 ///////////////////////////////////////////////////////////////////
1257 // METHOD NAME : RpmDb::pubkeys
1258 // METHOD TYPE : std::set<Edition>
1260 std::list<PublicKey> RpmDb::pubkeys() const
1262 std::list<PublicKey> ret;
1264 librpmDb::db_const_iterator it;
1265 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1267 Edition edition = it->tag_edition();
1268 if (edition != Edition::noedition)
1270 // we export the rpm key into a file
1271 RpmHeader::constPtr result;
1272 getData( "gpg-pubkey", edition, result );
1273 TmpFile file(getZYpp()->tmpPath());
1277 os.open(file.path().asString().c_str());
1278 // dump rpm key into the tmp file
1279 os << result->tag_description();
1280 //MIL << "-----------------------------------------------" << endl;
1281 //MIL << result->tag_description() <<endl;
1282 //MIL << "-----------------------------------------------" << endl;
1284 // read the public key from the dumped file
1285 PublicKey key(file);
1288 catch ( std::exception & e )
1290 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1291 // just ignore the key
1298 std::set<Edition> RpmDb::pubkeyEditions() const
1300 std::set<Edition> ret;
1302 librpmDb::db_const_iterator it;
1303 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1305 Edition edition = it->tag_edition();
1306 if (edition != Edition::noedition)
1307 ret.insert( edition );
1313 ///////////////////////////////////////////////////////////////////
1316 // METHOD NAME : RpmDb::fileList
1317 // METHOD TYPE : bool
1322 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
1324 std::list<FileInfo> result;
1326 librpmDb::db_const_iterator it;
1328 if (edition_r == Edition::noedition)
1330 found = it.findPackage( name_r );
1334 found = it.findPackage( name_r, edition_r );
1343 ///////////////////////////////////////////////////////////////////
1346 // METHOD NAME : RpmDb::hasFile
1347 // METHOD TYPE : bool
1351 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
1353 librpmDb::db_const_iterator it;
1357 res = it.findByFile( file_r );
1359 if (!name_r.empty())
1361 res = (it->tag_name() == name_r);
1369 ///////////////////////////////////////////////////////////////////
1372 // METHOD NAME : RpmDb::whoOwnsFile
1373 // METHOD TYPE : std::string
1377 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
1379 librpmDb::db_const_iterator it;
1380 if (it.findByFile( file_r ))
1382 return it->tag_name();
1387 ///////////////////////////////////////////////////////////////////
1390 // METHOD NAME : RpmDb::hasProvides
1391 // METHOD TYPE : bool
1395 bool RpmDb::hasProvides( const std::string & tag_r ) const
1397 librpmDb::db_const_iterator it;
1398 return it.findByProvides( tag_r );
1401 ///////////////////////////////////////////////////////////////////
1404 // METHOD NAME : RpmDb::hasRequiredBy
1405 // METHOD TYPE : bool
1409 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1411 librpmDb::db_const_iterator it;
1412 return it.findByRequiredBy( tag_r );
1415 ///////////////////////////////////////////////////////////////////
1418 // METHOD NAME : RpmDb::hasConflicts
1419 // METHOD TYPE : bool
1423 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1425 librpmDb::db_const_iterator it;
1426 return it.findByConflicts( tag_r );
1429 ///////////////////////////////////////////////////////////////////
1432 // METHOD NAME : RpmDb::hasPackage
1433 // METHOD TYPE : bool
1437 bool RpmDb::hasPackage( const std::string & name_r ) const
1439 librpmDb::db_const_iterator it;
1440 return it.findPackage( name_r );
1443 ///////////////////////////////////////////////////////////////////
1446 // METHOD NAME : RpmDb::hasPackage
1447 // METHOD TYPE : bool
1451 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1453 librpmDb::db_const_iterator it;
1454 return it.findPackage( name_r, ed_r );
1457 ///////////////////////////////////////////////////////////////////
1460 // METHOD NAME : RpmDb::getData
1461 // METHOD TYPE : PMError
1465 void RpmDb::getData( const std::string & name_r,
1466 RpmHeader::constPtr & result_r ) const
1468 librpmDb::db_const_iterator it;
1469 it.findPackage( name_r );
1472 ZYPP_THROW(*(it.dbError()));
1475 ///////////////////////////////////////////////////////////////////
1478 // METHOD NAME : RpmDb::getData
1479 // METHOD TYPE : void
1483 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1484 RpmHeader::constPtr & result_r ) const
1486 librpmDb::db_const_iterator it;
1487 it.findPackage( name_r, ed_r );
1490 ZYPP_THROW(*(it.dbError()));
1493 ///////////////////////////////////////////////////////////////////
1496 struct RpmlogCapture : public std::string
1499 { rpmlog()._cap = this; }
1502 { rpmlog()._cap = nullptr; }
1510 rpmlogSetCallback( rpmLogCB, this );
1511 rpmSetVerbosity( RPMLOG_INFO );
1512 _f = ::fopen( "/dev/null","w");
1513 rpmlogSetFile( _f );
1517 { if ( _f ) ::fclose( _f ); }
1519 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1520 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1522 int rpmLog( rpmlogRec rec_r )
1524 if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1525 return RPMLOG_DEFAULT;
1532 static Rpmlog & rpmlog()
1533 { static Rpmlog _rpmlog; return _rpmlog; }
1536 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1537 const Pathname & root_r, // target root
1538 bool requireGPGSig_r, // whether no gpg signature is to be reported
1539 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1541 PathInfo file( path_r );
1542 if ( ! file.isFile() )
1544 ERR << "Not a file: " << file << endl;
1545 return RpmDb::CHK_ERROR;
1548 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1549 if ( fd == 0 || ::Ferror(fd) )
1551 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1554 return RpmDb::CHK_ERROR;
1556 rpmts ts = ::rpmtsCreate();
1557 ::rpmtsSetRootDir( ts, root_r.c_str() );
1558 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1560 rpmQVKArguments_s qva;
1561 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1562 #ifdef HAVE_NO_RPMTSSETVFYFLAGS
1563 // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
1564 // and qva_flags is not used in signature checking at all.
1565 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1567 ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
1569 RpmlogCapture vresult;
1570 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1571 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1577 // results per line...
1578 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1579 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1580 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1581 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1583 // TODO: try to get SIG info from the header rather than parsing the output
1584 std::vector<std::string> lines;
1585 str::split( vresult, std::back_inserter(lines), "\n" );
1586 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1588 for ( unsigned i = 1; i < lines.size(); ++i )
1590 std::string & line( lines[i] );
1591 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1592 if ( line.find( ": OK" ) != std::string::npos )
1594 lineres = RpmDb::CHK_OK;
1595 if ( line.find( "Signature, key ID" ) == std::string::npos )
1596 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1598 else if ( line.find( ": NOKEY" ) != std::string::npos )
1599 { lineres = RpmDb::CHK_NOKEY; }
1600 else if ( line.find( ": BAD" ) != std::string::npos )
1601 { lineres = RpmDb::CHK_FAIL; }
1602 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1603 { lineres = RpmDb::CHK_NOTFOUND; }
1604 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1605 { lineres = RpmDb::CHK_NOTTRUSTED; }
1608 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1611 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1613 if ( count[RpmDb::CHK_FAIL] )
1614 ret = RpmDb::CHK_FAIL;
1616 else if ( count[RpmDb::CHK_NOTFOUND] )
1617 ret = RpmDb::CHK_NOTFOUND;
1619 else if ( count[RpmDb::CHK_NOKEY] )
1620 ret = RpmDb::CHK_NOKEY;
1622 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1623 ret = RpmDb::CHK_NOTTRUSTED;
1625 else if ( ret == RpmDb::CHK_OK )
1627 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1629 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1630 if ( requireGPGSig_r )
1631 ret = RpmDb::CHK_NOSIG;
1635 if ( ret != RpmDb::CHK_OK )
1637 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1644 ///////////////////////////////////////////////////////////////////
1646 // METHOD NAME : RpmDb::checkPackage
1647 // METHOD TYPE : RpmDb::CheckPackageResult
1649 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1650 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1652 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1653 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1655 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1656 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1659 // determine changed files of installed package
1661 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1667 if ( ! initialized() ) return false;
1671 opts.push_back ("-V");
1672 opts.push_back ("--nodeps");
1673 opts.push_back ("--noscripts");
1674 opts.push_back ("--nomd5");
1675 opts.push_back ("--");
1676 opts.push_back (packageName.c_str());
1678 run_rpm (opts, ExternalProgram::Discard_Stderr);
1680 if ( process == NULL )
1691 M Mode (includes permissions and file type)
1695 while (systemReadLine(line))
1697 if (line.length() > 12 &&
1698 (line[0] == 'S' || line[0] == 's' ||
1699 (line[0] == '.' && line[7] == 'T')))
1701 // file has been changed
1702 std::string filename;
1704 filename.assign(line, 11, line.length() - 11);
1705 fileList.insert(filename);
1710 // exit code ignored, rpm returns 1 no matter if package is installed or
1718 /****************************************************************/
1719 /* private member-functions */
1720 /****************************************************************/
1722 /*--------------------------------------------------------------*/
1723 /* Run rpm with the specified arguments, handling stderr */
1724 /* as specified by disp */
1725 /*--------------------------------------------------------------*/
1727 RpmDb::run_rpm (const RpmArgVec& opts,
1728 ExternalProgram::Stderr_Disposition disp)
1737 if ( ! initialized() )
1739 ZYPP_THROW(RpmDbNotOpenException());
1744 // always set root and dbpath
1745 #if defined(WORKAROUNDRPMPWDBUG)
1746 args.push_back("#/"); // chdir to / to workaround bnc#819354
1748 args.push_back("rpm");
1749 args.push_back("--root");
1750 args.push_back(_root.asString().c_str());
1751 args.push_back("--dbpath");
1752 args.push_back(_dbPath.asString().c_str());
1754 const char* argv[args.size() + opts.size() + 1];
1756 const char** p = argv;
1757 p = copy (args.begin (), args.end (), p);
1758 p = copy (opts.begin (), opts.end (), p);
1761 // Invalidate all outstanding database handles in case
1762 // the database gets modified.
1763 librpmDb::dbRelease( true );
1765 // Launch the program with default locale
1766 process = new ExternalProgram(argv, disp, false, -1, true);
1770 /*--------------------------------------------------------------*/
1771 /* Read a line from the rpm process */
1772 /*--------------------------------------------------------------*/
1773 bool RpmDb::systemReadLine( std::string & line )
1777 if ( process == NULL )
1780 if ( process->inputFile() )
1782 process->setBlocking( false );
1783 FILE * inputfile = process->inputFile();
1784 int inputfileFd = ::fileno( inputfile );
1787 /* Watch inputFile to see when it has input. */
1790 FD_SET( inputfileFd, &rfds );
1792 /* Wait up to 5 seconds. */
1797 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1801 ERR << "select error: " << strerror(errno) << endl;
1802 if ( errno != EINTR )
1807 // Data is available now.
1808 static size_t linebuffer_size = 0; // static because getline allocs
1809 static char * linebuffer = 0; // and reallocs if buffer is too small
1810 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1813 if ( ::feof( inputfile ) )
1814 return line.size(); // in case of pending output
1820 if ( linebuffer[nread-1] == '\n' )
1822 line += std::string( linebuffer, nread );
1825 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1826 return true; // complete line
1828 clearerr( inputfile );
1832 // No data within time.
1833 if ( ! process->running() )
1842 /*--------------------------------------------------------------*/
1843 /* Return the exit status of the rpm process, closing the */
1844 /* connection if not already done */
1845 /*--------------------------------------------------------------*/
1847 RpmDb::systemStatus()
1849 if ( process == NULL )
1852 exit_code = process->close();
1856 error_message = process->execError();
1861 // DBG << "exit code " << exit_code << endl;
1866 /*--------------------------------------------------------------*/
1867 /* Forcably kill the rpm process */
1868 /*--------------------------------------------------------------*/
1872 if (process) process->kill();
1876 // generate diff mails for config files
1877 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1879 std::string msg = line.substr(9);
1880 std::string::size_type pos1 = std::string::npos;
1881 std::string::size_type pos2 = std::string::npos;
1882 std::string file1s, file2s;
1886 pos1 = msg.find (typemsg);
1889 if ( pos1 == std::string::npos )
1892 pos2 = pos1 + strlen (typemsg);
1894 if (pos2 >= msg.length() )
1897 file1 = msg.substr (0, pos1);
1898 file2 = msg.substr (pos2);
1900 file1s = file1.asString();
1901 file2s = file2.asString();
1903 if (!_root.empty() && _root != "/")
1905 file1 = _root + file1;
1906 file2 = _root + file2;
1910 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1913 Pathname file = _root + WARNINGMAILPATH;
1914 if (filesystem::assert_dir(file) != 0)
1916 ERR << "Could not create " << file.asString() << endl;
1919 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1920 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1923 ERR << "Could not open " << file << endl;
1927 // Translator: %s = name of an rpm package. A list of diffs follows
1929 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1932 ERR << "diff failed" << endl;
1933 notify << str::form(difffailmsg,
1934 file1s.c_str(), file2s.c_str()) << endl;
1938 notify << str::form(diffgenmsg,
1939 file1s.c_str(), file2s.c_str()) << endl;
1941 // remove root for the viewer's pleasure (#38240)
1942 if (!_root.empty() && _root != "/")
1944 if (out.substr(0,4) == "--- ")
1946 out.replace(4, file1.asString().length(), file1s);
1948 std::string::size_type pos = out.find("\n+++ ");
1949 if (pos != std::string::npos)
1951 out.replace(pos+5, file2.asString().length(), file2s);
1954 notify << out << endl;
1957 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1962 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1968 ///////////////////////////////////////////////////////////////////
1971 // METHOD NAME : RpmDb::installPackage
1972 // METHOD TYPE : PMError
1974 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1976 callback::SendReport<RpmInstallReport> report;
1978 report->start(filename);
1983 doInstallPackage(filename, flags, report);
1987 catch (RpmException & excpt_r)
1989 RpmInstallReport::Action user = report->problem( excpt_r );
1991 if ( user == RpmInstallReport::ABORT )
1993 report->finish( excpt_r );
1994 ZYPP_RETHROW(excpt_r);
1996 else if ( user == RpmInstallReport::IGNORE )
2004 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
2006 FAILIFNOTINITIALIZED;
2007 HistoryLog historylog;
2009 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
2013 if ( _packagebackups )
2015 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2016 if ( ! backupPackage( filename ) )
2018 ERR << "backup of " << filename.asString() << " failed" << endl;
2020 // FIXME status handling
2021 report->progress( 0 ); // allow 1% for backup creation.
2026 if (flags & RPMINST_NOUPGRADE)
2027 opts.push_back("-i");
2029 opts.push_back("-U");
2031 opts.push_back("--percent");
2032 opts.push_back("--noglob");
2034 // ZConfig defines cross-arch installation
2035 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
2036 opts.push_back("--ignorearch");
2038 if (flags & RPMINST_NODIGEST)
2039 opts.push_back("--nodigest");
2040 if (flags & RPMINST_NOSIGNATURE)
2041 opts.push_back("--nosignature");
2042 if (flags & RPMINST_EXCLUDEDOCS)
2043 opts.push_back ("--excludedocs");
2044 if (flags & RPMINST_NOSCRIPTS)
2045 opts.push_back ("--noscripts");
2046 if (flags & RPMINST_FORCE)
2047 opts.push_back ("--force");
2048 if (flags & RPMINST_NODEPS)
2049 opts.push_back ("--nodeps");
2050 if (flags & RPMINST_IGNORESIZE)
2051 opts.push_back ("--ignoresize");
2052 if (flags & RPMINST_JUSTDB)
2053 opts.push_back ("--justdb");
2054 if (flags & RPMINST_TEST)
2055 opts.push_back ("--test");
2056 if (flags & RPMINST_NOPOSTTRANS)
2057 opts.push_back ("--noposttrans");
2059 opts.push_back("--");
2061 // rpm requires additional quoting of special chars:
2062 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
2063 opts.push_back ( quotedFilename.c_str() );
2065 modifyDatabase(); // BEFORE run_rpm
2066 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
2069 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2070 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
2072 unsigned linecnt = 0;
2073 while ( systemReadLine( line ) )
2075 if ( str::startsWith( line, "%%" ) )
2078 sscanf( line.c_str() + 2, "%d", &percent );
2079 report->progress( percent );
2083 if ( linecnt < MAXRPMMESSAGELINES )
2085 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2088 rpmmsg += line+'\n';
2090 if ( str::startsWith( line, "warning:" ) )
2091 configwarnings.push_back(line);
2093 if ( linecnt >= MAXRPMMESSAGELINES )
2094 rpmmsg += "[truncated]\n";
2096 int rpm_status = systemStatus();
2099 for (std::vector<std::string>::iterator it = configwarnings.begin();
2100 it != configwarnings.end(); ++it)
2102 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2104 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2106 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2107 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2109 _("rpm created %s as %s, but it was impossible to determine the difference"),
2111 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2114 if ( rpm_status != 0 )
2117 str::form("%s install failed", Pathname::basename(filename).c_str()),
2118 true /*timestamp*/);
2119 std::ostringstream sstr;
2120 sstr << "rpm output:" << endl << rpmmsg << endl;
2121 historylog.comment(sstr.str());
2122 // TranslatorExplanation the colon is followed by an error message
2123 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
2125 else if ( ! rpmmsg.empty() )
2128 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2129 true /*timestamp*/);
2130 std::ostringstream sstr;
2131 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2132 historylog.comment(sstr.str());
2134 // report additional rpm output in finish
2135 // TranslatorExplanation Text is followed by a ':' and the actual output.
2136 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2140 ///////////////////////////////////////////////////////////////////
2143 // METHOD NAME : RpmDb::removePackage
2144 // METHOD TYPE : PMError
2146 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2148 // 'rpm -e' does not like epochs
2149 return removePackage( package->name()
2150 + "-" + package->edition().version()
2151 + "-" + package->edition().release()
2152 + "." + package->arch().asString(), flags );
2155 ///////////////////////////////////////////////////////////////////
2158 // METHOD NAME : RpmDb::removePackage
2159 // METHOD TYPE : PMError
2161 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
2163 callback::SendReport<RpmRemoveReport> report;
2165 report->start( name_r );
2170 doRemovePackage(name_r, flags, report);
2174 catch (RpmException & excpt_r)
2176 RpmRemoveReport::Action user = report->problem( excpt_r );
2178 if ( user == RpmRemoveReport::ABORT )
2180 report->finish( excpt_r );
2181 ZYPP_RETHROW(excpt_r);
2183 else if ( user == RpmRemoveReport::IGNORE )
2192 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2194 FAILIFNOTINITIALIZED;
2195 HistoryLog historylog;
2197 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2200 if ( _packagebackups )
2202 // FIXME solve this status report somehow
2203 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2204 if ( ! backupPackage( name_r ) )
2206 ERR << "backup of " << name_r << " failed" << endl;
2208 report->progress( 0 );
2212 report->progress( 100 );
2217 opts.push_back("-e");
2218 opts.push_back("--allmatches");
2220 if (flags & RPMINST_NOSCRIPTS)
2221 opts.push_back("--noscripts");
2222 if (flags & RPMINST_NODEPS)
2223 opts.push_back("--nodeps");
2224 if (flags & RPMINST_JUSTDB)
2225 opts.push_back("--justdb");
2226 if (flags & RPMINST_TEST)
2227 opts.push_back ("--test");
2228 if (flags & RPMINST_FORCE)
2230 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2233 opts.push_back("--");
2234 opts.push_back(name_r.c_str());
2236 modifyDatabase(); // BEFORE run_rpm
2237 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2240 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2242 // got no progress from command, so we fake it:
2243 // 5 - command started
2244 // 50 - command completed
2246 report->progress( 5 );
2247 unsigned linecnt = 0;
2248 while (systemReadLine(line))
2250 if ( linecnt < MAXRPMMESSAGELINES )
2252 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2254 rpmmsg += line+'\n';
2256 if ( linecnt >= MAXRPMMESSAGELINES )
2257 rpmmsg += "[truncated]\n";
2258 report->progress( 50 );
2259 int rpm_status = systemStatus();
2261 if ( rpm_status != 0 )
2264 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2265 std::ostringstream sstr;
2266 sstr << "rpm output:" << endl << rpmmsg << endl;
2267 historylog.comment(sstr.str());
2268 // TranslatorExplanation the colon is followed by an error message
2269 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
2271 else if ( ! rpmmsg.empty() )
2274 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2276 std::ostringstream sstr;
2277 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2278 historylog.comment(sstr.str());
2280 // report additional rpm output in finish
2281 // TranslatorExplanation Text is followed by a ':' and the actual output.
2282 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2286 ///////////////////////////////////////////////////////////////////
2289 // METHOD NAME : RpmDb::backupPackage
2290 // METHOD TYPE : bool
2292 bool RpmDb::backupPackage( const Pathname & filename )
2294 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2298 return backupPackage( h->tag_name() );
2301 ///////////////////////////////////////////////////////////////////
2304 // METHOD NAME : RpmDb::backupPackage
2305 // METHOD TYPE : bool
2307 bool RpmDb::backupPackage(const std::string& packageName)
2309 HistoryLog progresslog;
2311 Pathname backupFilename;
2312 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2314 if (_backuppath.empty())
2316 INT << "_backuppath empty" << endl;
2322 if (!queryChangedFiles(fileList, packageName))
2324 ERR << "Error while getting changed files for package " <<
2325 packageName << endl;
2329 if (fileList.size() <= 0)
2331 DBG << "package " << packageName << " not changed -> no backup" << endl;
2335 if (filesystem::assert_dir(_root + _backuppath) != 0)
2341 // build up archive name
2342 time_t currentTime = time(0);
2343 struct tm *currentLocalTime = localtime(¤tTime);
2345 int date = (currentLocalTime->tm_year + 1900) * 10000
2346 + (currentLocalTime->tm_mon + 1) * 100
2347 + currentLocalTime->tm_mday;
2352 backupFilename = _root + _backuppath
2353 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2356 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2358 PathInfo pi(filestobackupfile);
2359 if (pi.isExist() && !pi.isFile())
2361 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2365 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2369 ERR << "could not open " << filestobackupfile.asString() << endl;
2373 for (FileList::const_iterator cit = fileList.begin();
2374 cit != fileList.end(); ++cit)
2376 std::string name = *cit;
2377 if ( name[0] == '/' )
2379 // remove slash, file must be relative to -C parameter of tar
2380 name = name.substr( 1 );
2382 DBG << "saving file "<< name << endl;
2387 const char* const argv[] =
2392 _root.asString().c_str(),
2393 "--ignore-failed-read",
2395 backupFilename.asString().c_str(),
2397 filestobackupfile.asString().c_str(),
2401 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2402 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2406 // TODO: its probably possible to start tar with -v and watch it adding
2407 // files to report progress
2408 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2413 int ret = tar.close();
2417 ERR << "tar failed: " << tarmsg << endl;
2422 MIL << "tar backup ok" << endl;
2423 progresslog.comment(
2424 str::form(_("created backup %s"), backupFilename.asString().c_str())
2425 , /*timestamp*/true);
2428 filesystem::unlink(filestobackupfile);
2434 void RpmDb::setBackupPath(const Pathname& path)
2439 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2443 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2444 // translators: possible rpm package signature check result [brief]
2445 OUTS( CHK_OK, _("Signature is OK") );
2446 // translators: possible rpm package signature check result [brief]
2447 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2448 // translators: possible rpm package signature check result [brief]
2449 OUTS( CHK_FAIL, _("Signature does not verify") );
2450 // translators: possible rpm package signature check result [brief]
2451 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2452 // translators: possible rpm package signature check result [brief]
2453 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2454 // translators: possible rpm package signature check result [brief]
2455 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2456 // translators: possible rpm package signature check result [brief]
2457 OUTS( CHK_NOSIG, _("File is unsigned") );
2460 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2463 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2465 for ( const auto & el : obj )
2466 str << el.second << endl;
2471 } // namespace target