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
64 namespace zypp_readonly_hack
66 bool IGotIt(); // in readonly-mode
74 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
75 const char* quoteInFilename_m = "\'\"";
77 const char* quoteInFilename_m = " \t\'\"";
79 inline std::string rpmQuoteFilename( const Pathname & path_r )
81 std::string path( path_r.asString() );
82 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
83 pos != std::string::npos;
84 pos = path.find_first_of( quoteInFilename_m, pos ) )
86 path.insert( pos, "\\" );
87 pos += 2; // skip '\\' and the quoted char.
93 /** Workaround bnc#827609 - rpm needs a readable pwd so we
94 * chdir to /. Turn realtive pathnames into absolute ones
95 * by prepending cwd so rpm still finds them
97 inline Pathname workaroundRpmPwdBug( Pathname path_r )
99 #if defined(WORKAROUNDRPMPWDBUG)
100 if ( path_r.relative() )
102 // try to prepend cwd
103 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
105 return Pathname( cwd ) / path_r;
106 WAR << "Can't get cwd!" << endl;
109 return path_r; // no problem with absolute pathnames
113 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
115 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
120 ~KeyRingSignalReceiver()
125 virtual void trustedKeyAdded( const PublicKey &key )
127 MIL << "trusted key added to zypp Keyring. Importing..." << endl;
128 _rpmdb.importPubkey( key );
131 virtual void trustedKeyRemoved( const PublicKey &key )
133 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
134 _rpmdb.removePubkey( key );
140 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
142 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
152 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
159 for (line = prog.receiveLine(), count=0;
161 line = prog.receiveLine(), count++ )
163 if (maxlines<0?true:count<maxlines)
172 /******************************************************************
175 ** FUNCTION NAME : stringPath
176 ** FUNCTION TYPE : inline std::string
178 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
180 return librpmDb::stringPath( root_r, sub_r );
183 /******************************************************************
186 ** FUNCTION NAME : operator<<
187 ** FUNCTION TYPE : std::ostream &
189 std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
191 if ( obj == RpmDb::DbSI_NO_INIT )
197 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
199 ENUM_OUT( DbSI_HAVE_V4, 'X' );
200 ENUM_OUT( DbSI_MADE_V4, 'c' );
201 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
203 ENUM_OUT( DbSI_HAVE_V3, 'X' );
204 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
205 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
214 ///////////////////////////////////////////////////////////////////
216 // CLASS NAME : RpmDb
218 ///////////////////////////////////////////////////////////////////
220 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
222 ///////////////////////////////////////////////////////////////////
224 ///////////////////////////////////////////////////////////////////
227 // METHOD NAME : RpmDb::RpmDb
228 // METHOD TYPE : Constructor
231 : _dbStateInfo( DbSI_NO_INIT )
232 #warning Check for obsolete memebers
233 , _backuppath ("/var/adm/backup")
234 , _packagebackups(false)
235 , _warndirexists(false)
239 librpmDb::globalInit();
240 // Some rpm versions are patched not to abort installation if
241 // symlink creation failed.
242 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
243 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
246 ///////////////////////////////////////////////////////////////////
249 // METHOD NAME : RpmDb::~RpmDb
250 // METHOD TYPE : Destructor
254 MIL << "~RpmDb()" << endl;
257 MIL << "~RpmDb() end" << endl;
258 sKeyRingReceiver.reset();
261 Date RpmDb::timestamp() const
266 if ( dbPath().empty() )
267 db_path = "/var/lib/rpm";
271 PathInfo rpmdb_info(root() + db_path + "/Packages");
273 if ( rpmdb_info.isExist() )
274 return rpmdb_info.mtime();
278 ///////////////////////////////////////////////////////////////////
281 // METHOD NAME : RpmDb::dumpOn
282 // METHOD TYPE : std::ostream &
284 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
288 if ( _dbStateInfo == DbSI_NO_INIT )
294 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
296 ENUM_OUT( DbSI_HAVE_V4, 'X' );
297 ENUM_OUT( DbSI_MADE_V4, 'c' );
298 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
300 ENUM_OUT( DbSI_HAVE_V3, 'X' );
301 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
302 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
303 str << "): " << stringPath( _root, _dbPath );
309 ///////////////////////////////////////////////////////////////////
312 // METHOD NAME : RpmDb::initDatabase
313 // METHOD TYPE : PMError
315 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
317 ///////////////////////////////////////////////////////////////////
319 ///////////////////////////////////////////////////////////////////
320 bool quickinit( root_r.empty() );
322 if ( root_r.empty() )
325 if ( dbPath_r.empty() )
326 dbPath_r = "/var/lib/rpm";
328 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
330 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
331 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
334 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
335 << ( doRebuild_r ? " (rebuilddb)" : "" )
336 << ( quickinit ? " (quickinit)" : "" ) << endl;
338 ///////////////////////////////////////////////////////////////////
339 // Check whether already initialized
340 ///////////////////////////////////////////////////////////////////
343 if ( root_r == _root && dbPath_r == _dbPath )
349 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
353 ///////////////////////////////////////////////////////////////////
355 ///////////////////////////////////////////////////////////////////
356 librpmDb::unblockAccess();
360 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
364 DbStateInfoBits info = DbSI_NO_INIT;
367 internal_initDatabase( root_r, dbPath_r, info );
369 catch (const RpmException & excpt_r)
371 ZYPP_CAUGHT(excpt_r);
372 librpmDb::blockAccess();
373 ERR << "Cleanup on error: state " << info << endl;
375 if ( dbsi_has( info, DbSI_MADE_V4 ) )
377 // remove the newly created rpm4 database and
378 // any backup created on conversion.
379 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
381 ZYPP_RETHROW(excpt_r);
383 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
385 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
387 // Move obsolete rpm3 database beside.
388 MIL << "Cleanup: state " << info << endl;
389 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
390 dbsi_clr( info, DbSI_HAVE_V3 );
394 // Performing an update: Keep the original rpm3 database
395 // and wait if the rpm4 database gets modified by installing
396 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
397 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
400 #warning CHECK: notify root about conversion backup.
408 if ( dbsi_has( info, DbSI_HAVE_V4 )
409 && ! dbsi_has( info, DbSI_MADE_V4 ) )
415 MIL << "Synchronizing keys with zypp keyring" << endl;
418 // Close the database in case any write acces (create/convert)
419 // happened during init. This should drop any lock acquired
420 // by librpm. On demand it will be reopened readonly and should
421 // not hold any lock.
422 librpmDb::dbRelease( true );
424 MIL << "InitDatabase: " << *this << endl;
427 ///////////////////////////////////////////////////////////////////
430 // METHOD NAME : RpmDb::internal_initDatabase
431 // METHOD TYPE : PMError
433 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
434 DbStateInfoBits & info_r )
436 info_r = DbSI_NO_INIT;
438 ///////////////////////////////////////////////////////////////////
439 // Get info about the desired database dir
440 ///////////////////////////////////////////////////////////////////
441 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
443 if ( dbInfo.illegalArgs() )
445 // should not happen (checked in initDatabase)
446 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
448 if ( ! dbInfo.usableArgs() )
450 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
451 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
454 if ( dbInfo.hasDbV4() )
456 dbsi_set( info_r, DbSI_HAVE_V4 );
457 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
461 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
464 if ( dbInfo.hasDbV3() )
466 dbsi_set( info_r, DbSI_HAVE_V3 );
468 if ( dbInfo.hasDbV3ToV4() )
470 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
473 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
474 librpmDb::dumpState( DBG ) << endl;
476 ///////////////////////////////////////////////////////////////////
477 // Access database, create if needed
478 ///////////////////////////////////////////////////////////////////
480 // creates dbdir and empty rpm4 database if not present
481 librpmDb::dbAccess( root_r, dbPath_r );
483 if ( ! dbInfo.hasDbV4() )
486 if ( dbInfo.hasDbV4() )
488 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
492 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
493 librpmDb::dumpState( DBG ) << endl;
495 ///////////////////////////////////////////////////////////////////
496 // Check whether to convert something. Create backup but do
497 // not remove anything here
498 ///////////////////////////////////////////////////////////////////
499 librpmDb::constPtr dbptr;
500 librpmDb::dbAccess( dbptr );
501 bool dbEmpty = dbptr->empty();
504 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
507 if ( dbInfo.hasDbV3() )
509 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
513 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
514 convertV3toV4( dbInfo.dbV3().path(), dbptr );
516 // create a backup copy
517 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
520 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
525 if ( dbInfo.hasDbV3ToV4() )
527 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
528 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
536 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
537 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
538 dbsi_set( info_r, DbSI_MODIFIED_V4 );
542 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
543 librpmDb::dumpState( DBG ) << endl;
546 if ( dbInfo.hasDbV3ToV4() )
548 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
552 ///////////////////////////////////////////////////////////////////
555 // METHOD NAME : RpmDb::removeV4
556 // METHOD TYPE : void
558 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
560 const char * v3backup = "packages.rpm3";
561 const char * master = "Packages";
562 const char * index[] =
584 PathInfo pi( dbdir_r );
587 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
591 for ( const char ** f = index; *f; ++f )
596 filesystem::unlink( pi.path() );
600 pi( dbdir_r + master );
603 MIL << "Removing rpm4 database " << pi << endl;
604 filesystem::unlink( pi.path() );
609 pi( dbdir_r + v3backup );
612 MIL << "Removing converted rpm3 database backup " << pi << endl;
613 filesystem::unlink( pi.path() );
618 ///////////////////////////////////////////////////////////////////
621 // METHOD NAME : RpmDb::removeV3
622 // METHOD TYPE : void
624 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
626 const char * master = "packages.rpm";
627 const char * index[] =
629 "conflictsindex.rpm",
640 PathInfo pi( dbdir_r );
643 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
647 for ( const char ** f = index; *f; ++f )
652 filesystem::unlink( pi.path() );
656 #warning CHECK: compare vs existing v3 backup. notify root
657 pi( dbdir_r + master );
660 Pathname m( pi.path() );
663 // backup was already created
664 filesystem::unlink( m );
665 Pathname b( m.extend( "3" ) );
666 pi( b ); // stat backup
670 Pathname b( m.extend( ".deleted" ) );
674 // rempve existing backup
675 filesystem::unlink( b );
677 filesystem::rename( m, b );
678 pi( b ); // stat backup
680 MIL << "(Re)moved rpm3 database to " << pi << endl;
684 ///////////////////////////////////////////////////////////////////
687 // METHOD NAME : RpmDb::modifyDatabase
688 // METHOD TYPE : void
690 void RpmDb::modifyDatabase()
692 if ( ! initialized() )
695 // tag database as modified
696 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
698 // Move outdated rpm3 database beside.
699 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
701 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
702 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
703 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
707 ///////////////////////////////////////////////////////////////////
710 // METHOD NAME : RpmDb::closeDatabase
711 // METHOD TYPE : PMError
713 void RpmDb::closeDatabase()
715 if ( ! initialized() )
720 MIL << "Calling closeDatabase: " << *this << endl;
722 ///////////////////////////////////////////////////////////////////
723 // Block further database access
724 ///////////////////////////////////////////////////////////////////
725 librpmDb::blockAccess();
727 ///////////////////////////////////////////////////////////////////
728 // Check fate if old version database still present
729 ///////////////////////////////////////////////////////////////////
730 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
732 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
733 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
735 // Move outdated rpm3 database beside.
736 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
740 // Remove unmodified rpm4 database
741 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
745 ///////////////////////////////////////////////////////////////////
747 ///////////////////////////////////////////////////////////////////
748 _root = _dbPath = Pathname();
749 _dbStateInfo = DbSI_NO_INIT;
751 MIL << "closeDatabase: " << *this << endl;
754 ///////////////////////////////////////////////////////////////////
757 // METHOD NAME : RpmDb::rebuildDatabase
758 // METHOD TYPE : PMError
760 void RpmDb::rebuildDatabase()
762 callback::SendReport<RebuildDBReport> report;
764 report->start( root() + dbPath() );
768 doRebuildDatabase(report);
770 catch (RpmException & excpt_r)
772 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
773 ZYPP_RETHROW(excpt_r);
775 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
778 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
780 FAILIFNOTINITIALIZED;
782 MIL << "RpmDb::rebuildDatabase" << *this << endl;
783 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
785 PathInfo dbMaster( root() + dbPath() + "Packages" );
786 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
790 opts.push_back("--rebuilddb");
791 opts.push_back("-vv");
793 // don't call modifyDatabase because it would remove the old
794 // rpm3 database, if the current database is a temporary one.
795 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
797 // progress report: watch this file growing
798 PathInfo newMaster( root()
799 + dbPath().extend( str::form( "rebuilddb.%d",
800 process?process->getpid():0) )
806 while ( systemReadLine( line ) )
809 { // file is removed at the end of rebuild.
810 // current size should be upper limit for new db
811 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
813 WAR << "User requested abort." << endl;
815 filesystem::recursive_rmdir( newMaster.path().dirname() );
819 if ( line.compare( 0, 2, "D:" ) )
821 errmsg += line + '\n';
822 // report.notify( line );
827 int rpm_status = systemStatus();
829 if ( rpm_status != 0 )
831 //TranslatorExplanation after semicolon is error message
832 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
836 report->progress( 100, root() + dbPath() ); // 100%
840 ///////////////////////////////////////////////////////////////////
843 /** \ref RpmDb::syncTrustedKeys helper
844 * Compute which keys need to be exprted to / imported from the zypp keyring.
845 * Return result via argument list.
847 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
849 ///////////////////////////////////////////////////////////////////
850 // Remember latest release and where it ocurred
854 : _inRpmKeys( nullptr )
855 , _inZyppKeys( nullptr )
858 void updateIf( const Edition & rpmKey_r )
860 std::string keyRelease( rpmKey_r.release() );
861 int comp = _release.compare( keyRelease );
864 // update to newer release
865 _release.swap( keyRelease );
866 _inRpmKeys = &rpmKey_r;
867 _inZyppKeys = nullptr;
868 if ( !keyRelease.empty() )
869 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
871 else if ( comp == 0 )
873 // stay with this release
875 _inRpmKeys = &rpmKey_r;
877 // else: this is an old release
879 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
882 void updateIf( const PublicKeyData & zyppKey_r )
884 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
885 int comp = _release.compare( keyRelease );
888 // update to newer release
889 _release.swap( keyRelease );
890 _inRpmKeys = nullptr;
891 _inZyppKeys = &zyppKey_r;
892 if ( !keyRelease.empty() )
893 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
895 else if ( comp == 0 )
897 // stay with this release
899 _inZyppKeys = &zyppKey_r;
901 // else: this is an old release
903 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
906 std::string _release;
907 const Edition * _inRpmKeys;
908 const PublicKeyData * _inZyppKeys;
910 ///////////////////////////////////////////////////////////////////
912 // collect keys by ID(version) and latest creation(release)
913 std::map<std::string,Key> _keymap;
915 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
917 _keymap[(*it).version()].updateIf( *it );
920 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
922 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
925 // compute missing keys
926 std::set<Edition> rpmKeys;
927 std::list<PublicKeyData> zyppKeys;
928 for_( it, _keymap.begin(), _keymap.end() )
930 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
931 << ( (*it).second._inRpmKeys ? "R" : "_" )
932 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
933 if ( ! (*it).second._inRpmKeys )
935 zyppKeys.push_back( *(*it).second._inZyppKeys );
937 if ( ! (*it).second._inZyppKeys )
939 rpmKeys.insert( *(*it).second._inRpmKeys );
942 rpmKeys_r.swap( rpmKeys );
943 zyppKeys_r.swap( zyppKeys );
946 ///////////////////////////////////////////////////////////////////
948 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
950 MIL << "Going to sync trusted keys..." << endl;
951 std::set<Edition> rpmKeys( pubkeyEditions() );
952 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
954 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
956 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
957 // when re-acquiring the zyppp lock. For now we remove all excess keys.
958 // TODO: Once we can safely assume that all PK versions are updated we
959 // can think about re-importing newer key versions found in the zypp keyring and
960 // removing only excess ones (but case is not very likely). Unfixed PK versions
961 // however will remove the newer version found in the zypp keyring and by doing
962 // this, the key here will be removed via callback as well (keys are deleted
963 // via gpg id, regardless of the edition).
964 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
965 // Temporarily disconnect to prevent the attempt to pass back the delete request.
966 callback::TempConnect<KeyRingSignals> tempDisconnect;
968 for ( const PublicKeyData & keyData : zyppKeys )
970 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
972 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
973 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
974 if ( !dirty ) dirty = true;
978 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
981 computeKeyRingSync( rpmKeys, zyppKeys );
982 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
983 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
985 ///////////////////////////////////////////////////////////////////
986 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
988 // export to zypp keyring
989 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
990 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
991 callback::TempConnect<KeyRingSignals> tempDisconnect;
992 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
994 TmpFile tmpfile( getZYpp()->tmpPath() );
996 std::ofstream tmpos( tmpfile.path().c_str() );
997 for_( it, rpmKeys.begin(), rpmKeys.end() )
999 // we export the rpm key into a file
1000 RpmHeader::constPtr result;
1001 getData( "gpg-pubkey", *it, result );
1002 tmpos << result->tag_description() << endl;
1007 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1009 catch (Exception &e)
1011 ERR << "Could not import keys into in zypp keyring" << endl;
1015 ///////////////////////////////////////////////////////////////////
1016 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1018 // import from zypp keyring
1019 MIL << "Importing zypp trusted keyring" << std::endl;
1020 for_( it, zyppKeys.begin(), zyppKeys.end() )
1024 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1026 catch ( const RpmException & exp )
1032 MIL << "Trusted keys synced." << endl;
1035 void RpmDb::importZyppKeyRingTrustedKeys()
1036 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1038 void RpmDb::exportTrustedKeysInZyppKeyRing()
1039 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1041 ///////////////////////////////////////////////////////////////////
1044 // METHOD NAME : RpmDb::importPubkey
1045 // METHOD TYPE : PMError
1047 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1049 FAILIFNOTINITIALIZED;
1051 // bnc#828672: On the fly key import in READONLY
1052 if ( zypp_readonly_hack::IGotIt() )
1054 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1058 // check if the key is already in the rpm database
1059 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1060 std::set<Edition> rpmKeys = pubkeyEditions();
1061 bool hasOldkeys = false;
1063 for_( it, rpmKeys.begin(), rpmKeys.end() )
1065 // bsc#1008325: Keys using subkeys for signing don't get a higher release
1066 // if new subkeys are added, because the primary key remains unchanged.
1067 // For now always re-import keys with subkeys. Here we don't want to export the
1068 // keys in the rpm database to check whether the subkeys are the same. The calling
1069 // code should take care, we don't re-import the same kesy over and over again.
1070 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
1072 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1076 if ( keyEd.version() != (*it).version() )
1077 continue; // different key ID (version)
1079 if ( keyEd.release() < (*it).release() )
1081 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1089 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1093 // We must explicitly delete old key IDs first (all releases,
1094 // that's why we don't call removePubkey here).
1095 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1097 opts.push_back ( "-e" );
1098 opts.push_back ( "--allmatches" );
1099 opts.push_back ( "--" );
1100 opts.push_back ( keyName.c_str() );
1101 // don't call modifyDatabase because it would remove the old
1102 // rpm3 database, if the current database is a temporary one.
1103 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1106 while ( systemReadLine( line ) )
1108 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1111 if ( systemStatus() != 0 )
1113 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1117 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1121 // import the new key
1123 opts.push_back ( "--import" );
1124 opts.push_back ( "--" );
1125 std::string pubkeypath( pubkey_r.path().asString() );
1126 opts.push_back ( pubkeypath.c_str() );
1128 // don't call modifyDatabase because it would remove the old
1129 // rpm3 database, if the current database is a temporary one.
1130 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1133 std::vector<std::string> excplines;
1134 while ( systemReadLine( line ) )
1136 if ( str::startsWith( line, "error:" ) )
1138 WAR << line << endl;
1139 excplines.push_back( std::move(line) );
1142 DBG << line << endl;
1145 if ( systemStatus() != 0 )
1147 // Translator: %1% is a gpg public key
1148 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
1149 excp.moveToHistory( excplines );
1150 excp.addHistory( std::move(error_message) );
1151 ZYPP_THROW( std::move(excp) );
1155 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1159 ///////////////////////////////////////////////////////////////////
1162 // METHOD NAME : RpmDb::removePubkey
1163 // METHOD TYPE : PMError
1165 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1167 FAILIFNOTINITIALIZED;
1169 // check if the key is in the rpm database and just
1170 // return if it does not.
1171 std::set<Edition> rpm_keys = pubkeyEditions();
1172 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
1173 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1175 for_( it, rpm_keys.begin(), rpm_keys.end() )
1177 if ( (*it).version() == pubkeyVersion )
1184 // the key does not exist, cannot be removed
1185 if (found_edition == rpm_keys.end())
1187 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1191 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
1194 opts.push_back ( "-e" );
1195 opts.push_back ( "--" );
1196 opts.push_back ( rpm_name.c_str() );
1198 // don't call modifyDatabase because it would remove the old
1199 // rpm3 database, if the current database is a temporary one.
1200 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1203 std::vector<std::string> excplines;
1204 while ( systemReadLine( line ) )
1206 if ( str::startsWith( line, "error:" ) )
1208 WAR << line << endl;
1209 excplines.push_back( std::move(line) );
1212 DBG << line << endl;
1215 if ( systemStatus() != 0 )
1217 // Translator: %1% is a gpg public key
1218 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
1219 excp.moveToHistory( excplines );
1220 excp.addHistory( std::move(error_message) );
1221 ZYPP_THROW( std::move(excp) );
1225 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1229 ///////////////////////////////////////////////////////////////////
1232 // METHOD NAME : RpmDb::pubkeys
1233 // METHOD TYPE : std::set<Edition>
1235 std::list<PublicKey> RpmDb::pubkeys() const
1237 std::list<PublicKey> ret;
1239 librpmDb::db_const_iterator it;
1240 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1242 Edition edition = it->tag_edition();
1243 if (edition != Edition::noedition)
1245 // we export the rpm key into a file
1246 RpmHeader::constPtr result;
1247 getData( "gpg-pubkey", edition, result );
1248 TmpFile file(getZYpp()->tmpPath());
1252 os.open(file.path().asString().c_str());
1253 // dump rpm key into the tmp file
1254 os << result->tag_description();
1255 //MIL << "-----------------------------------------------" << endl;
1256 //MIL << result->tag_description() <<endl;
1257 //MIL << "-----------------------------------------------" << endl;
1259 // read the public key from the dumped file
1260 PublicKey key(file);
1263 catch ( std::exception & e )
1265 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1266 // just ignore the key
1273 std::set<Edition> RpmDb::pubkeyEditions() const
1275 std::set<Edition> ret;
1277 librpmDb::db_const_iterator it;
1278 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1280 Edition edition = it->tag_edition();
1281 if (edition != Edition::noedition)
1282 ret.insert( edition );
1288 ///////////////////////////////////////////////////////////////////
1291 // METHOD NAME : RpmDb::fileList
1292 // METHOD TYPE : bool
1297 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
1299 std::list<FileInfo> result;
1301 librpmDb::db_const_iterator it;
1303 if (edition_r == Edition::noedition)
1305 found = it.findPackage( name_r );
1309 found = it.findPackage( name_r, edition_r );
1318 ///////////////////////////////////////////////////////////////////
1321 // METHOD NAME : RpmDb::hasFile
1322 // METHOD TYPE : bool
1326 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
1328 librpmDb::db_const_iterator it;
1332 res = it.findByFile( file_r );
1334 if (!name_r.empty())
1336 res = (it->tag_name() == name_r);
1344 ///////////////////////////////////////////////////////////////////
1347 // METHOD NAME : RpmDb::whoOwnsFile
1348 // METHOD TYPE : std::string
1352 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
1354 librpmDb::db_const_iterator it;
1355 if (it.findByFile( file_r ))
1357 return it->tag_name();
1362 ///////////////////////////////////////////////////////////////////
1365 // METHOD NAME : RpmDb::hasProvides
1366 // METHOD TYPE : bool
1370 bool RpmDb::hasProvides( const std::string & tag_r ) const
1372 librpmDb::db_const_iterator it;
1373 return it.findByProvides( tag_r );
1376 ///////////////////////////////////////////////////////////////////
1379 // METHOD NAME : RpmDb::hasRequiredBy
1380 // METHOD TYPE : bool
1384 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1386 librpmDb::db_const_iterator it;
1387 return it.findByRequiredBy( tag_r );
1390 ///////////////////////////////////////////////////////////////////
1393 // METHOD NAME : RpmDb::hasConflicts
1394 // METHOD TYPE : bool
1398 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1400 librpmDb::db_const_iterator it;
1401 return it.findByConflicts( tag_r );
1404 ///////////////////////////////////////////////////////////////////
1407 // METHOD NAME : RpmDb::hasPackage
1408 // METHOD TYPE : bool
1412 bool RpmDb::hasPackage( const std::string & name_r ) const
1414 librpmDb::db_const_iterator it;
1415 return it.findPackage( name_r );
1418 ///////////////////////////////////////////////////////////////////
1421 // METHOD NAME : RpmDb::hasPackage
1422 // METHOD TYPE : bool
1426 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1428 librpmDb::db_const_iterator it;
1429 return it.findPackage( name_r, ed_r );
1432 ///////////////////////////////////////////////////////////////////
1435 // METHOD NAME : RpmDb::getData
1436 // METHOD TYPE : PMError
1440 void RpmDb::getData( const std::string & name_r,
1441 RpmHeader::constPtr & result_r ) const
1443 librpmDb::db_const_iterator it;
1444 it.findPackage( name_r );
1447 ZYPP_THROW(*(it.dbError()));
1450 ///////////////////////////////////////////////////////////////////
1453 // METHOD NAME : RpmDb::getData
1454 // METHOD TYPE : void
1458 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1459 RpmHeader::constPtr & result_r ) const
1461 librpmDb::db_const_iterator it;
1462 it.findPackage( name_r, ed_r );
1465 ZYPP_THROW(*(it.dbError()));
1468 ///////////////////////////////////////////////////////////////////
1471 struct RpmlogCapture : public std::string
1474 { rpmlog()._cap = this; }
1477 { rpmlog()._cap = nullptr; }
1485 rpmlogSetCallback( rpmLogCB, this );
1486 rpmSetVerbosity( RPMLOG_INFO );
1487 _f = ::fopen( "/dev/null","w");
1488 rpmlogSetFile( _f );
1492 { if ( _f ) ::fclose( _f ); }
1494 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1495 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1497 int rpmLog( rpmlogRec rec_r )
1499 if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1500 return RPMLOG_DEFAULT;
1507 static Rpmlog & rpmlog()
1508 { static Rpmlog _rpmlog; return _rpmlog; }
1511 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1512 const Pathname & root_r, // target root
1513 bool requireGPGSig_r, // whether no gpg signature is to be reported
1514 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1516 PathInfo file( path_r );
1517 if ( ! file.isFile() )
1519 ERR << "Not a file: " << file << endl;
1520 return RpmDb::CHK_ERROR;
1523 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1524 if ( fd == 0 || ::Ferror(fd) )
1526 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1529 return RpmDb::CHK_ERROR;
1531 rpmts ts = ::rpmtsCreate();
1532 ::rpmtsSetRootDir( ts, root_r.c_str() );
1533 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1535 rpmQVKArguments_s qva;
1536 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1537 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1539 RpmlogCapture vresult;
1540 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1541 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1547 // results per line...
1548 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1549 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1550 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1551 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1553 // TODO: try to get SIG info from the header rather than parsing the output
1554 std::vector<std::string> lines;
1555 str::split( vresult, std::back_inserter(lines), "\n" );
1556 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1558 for ( unsigned i = 1; i < lines.size(); ++i )
1560 std::string & line( lines[i] );
1561 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1562 if ( line.find( ": OK" ) != std::string::npos )
1564 lineres = RpmDb::CHK_OK;
1565 if ( line.find( "Signature, key ID" ) == std::string::npos )
1566 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1568 else if ( line.find( ": NOKEY" ) != std::string::npos )
1569 { lineres = RpmDb::CHK_NOKEY; }
1570 else if ( line.find( ": BAD" ) != std::string::npos )
1571 { lineres = RpmDb::CHK_FAIL; }
1572 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1573 { lineres = RpmDb::CHK_NOTFOUND; }
1574 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1575 { lineres = RpmDb::CHK_NOTTRUSTED; }
1578 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1581 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1583 if ( count[RpmDb::CHK_FAIL] )
1584 ret = RpmDb::CHK_FAIL;
1586 else if ( count[RpmDb::CHK_NOTFOUND] )
1587 ret = RpmDb::CHK_NOTFOUND;
1589 else if ( count[RpmDb::CHK_NOKEY] )
1590 ret = RpmDb::CHK_NOKEY;
1592 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1593 ret = RpmDb::CHK_NOTTRUSTED;
1595 else if ( ret == RpmDb::CHK_OK )
1597 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1599 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1600 if ( requireGPGSig_r )
1601 ret = RpmDb::CHK_NOSIG;
1605 if ( ret != RpmDb::CHK_OK )
1607 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1614 ///////////////////////////////////////////////////////////////////
1616 // METHOD NAME : RpmDb::checkPackage
1617 // METHOD TYPE : RpmDb::CheckPackageResult
1619 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1620 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1622 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1623 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1625 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1626 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1629 // determine changed files of installed package
1631 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1637 if ( ! initialized() ) return false;
1641 opts.push_back ("-V");
1642 opts.push_back ("--nodeps");
1643 opts.push_back ("--noscripts");
1644 opts.push_back ("--nomd5");
1645 opts.push_back ("--");
1646 opts.push_back (packageName.c_str());
1648 run_rpm (opts, ExternalProgram::Discard_Stderr);
1650 if ( process == NULL )
1661 M Mode (includes permissions and file type)
1665 while (systemReadLine(line))
1667 if (line.length() > 12 &&
1668 (line[0] == 'S' || line[0] == 's' ||
1669 (line[0] == '.' && line[7] == 'T')))
1671 // file has been changed
1672 std::string filename;
1674 filename.assign(line, 11, line.length() - 11);
1675 fileList.insert(filename);
1680 // exit code ignored, rpm returns 1 no matter if package is installed or
1688 /****************************************************************/
1689 /* private member-functions */
1690 /****************************************************************/
1692 /*--------------------------------------------------------------*/
1693 /* Run rpm with the specified arguments, handling stderr */
1694 /* as specified by disp */
1695 /*--------------------------------------------------------------*/
1697 RpmDb::run_rpm (const RpmArgVec& opts,
1698 ExternalProgram::Stderr_Disposition disp)
1707 if ( ! initialized() )
1709 ZYPP_THROW(RpmDbNotOpenException());
1714 // always set root and dbpath
1715 #if defined(WORKAROUNDRPMPWDBUG)
1716 args.push_back("#/"); // chdir to / to workaround bnc#819354
1718 args.push_back("rpm");
1719 args.push_back("--root");
1720 args.push_back(_root.asString().c_str());
1721 args.push_back("--dbpath");
1722 args.push_back(_dbPath.asString().c_str());
1724 const char* argv[args.size() + opts.size() + 1];
1726 const char** p = argv;
1727 p = copy (args.begin (), args.end (), p);
1728 p = copy (opts.begin (), opts.end (), p);
1731 // Invalidate all outstanding database handles in case
1732 // the database gets modified.
1733 librpmDb::dbRelease( true );
1735 // Launch the program with default locale
1736 process = new ExternalProgram(argv, disp, false, -1, true);
1740 /*--------------------------------------------------------------*/
1741 /* Read a line from the rpm process */
1742 /*--------------------------------------------------------------*/
1743 bool RpmDb::systemReadLine( std::string & line )
1747 if ( process == NULL )
1750 if ( process->inputFile() )
1752 process->setBlocking( false );
1753 FILE * inputfile = process->inputFile();
1754 int inputfileFd = ::fileno( inputfile );
1757 /* Watch inputFile to see when it has input. */
1760 FD_SET( inputfileFd, &rfds );
1762 /* Wait up to 5 seconds. */
1767 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1771 ERR << "select error: " << strerror(errno) << endl;
1772 if ( errno != EINTR )
1777 // Data is available now.
1778 static size_t linebuffer_size = 0; // static because getline allocs
1779 static char * linebuffer = 0; // and reallocs if buffer is too small
1780 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1783 if ( ::feof( inputfile ) )
1784 return line.size(); // in case of pending output
1790 if ( linebuffer[nread-1] == '\n' )
1792 line += std::string( linebuffer, nread );
1795 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1796 return true; // complete line
1798 clearerr( inputfile );
1802 // No data within time.
1803 if ( ! process->running() )
1812 /*--------------------------------------------------------------*/
1813 /* Return the exit status of the rpm process, closing the */
1814 /* connection if not already done */
1815 /*--------------------------------------------------------------*/
1817 RpmDb::systemStatus()
1819 if ( process == NULL )
1822 exit_code = process->close();
1826 error_message = process->execError();
1831 // DBG << "exit code " << exit_code << endl;
1836 /*--------------------------------------------------------------*/
1837 /* Forcably kill the rpm process */
1838 /*--------------------------------------------------------------*/
1842 if (process) process->kill();
1846 // generate diff mails for config files
1847 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1849 std::string msg = line.substr(9);
1850 std::string::size_type pos1 = std::string::npos;
1851 std::string::size_type pos2 = std::string::npos;
1852 std::string file1s, file2s;
1856 pos1 = msg.find (typemsg);
1859 if ( pos1 == std::string::npos )
1862 pos2 = pos1 + strlen (typemsg);
1864 if (pos2 >= msg.length() )
1867 file1 = msg.substr (0, pos1);
1868 file2 = msg.substr (pos2);
1870 file1s = file1.asString();
1871 file2s = file2.asString();
1873 if (!_root.empty() && _root != "/")
1875 file1 = _root + file1;
1876 file2 = _root + file2;
1880 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1883 Pathname file = _root + WARNINGMAILPATH;
1884 if (filesystem::assert_dir(file) != 0)
1886 ERR << "Could not create " << file.asString() << endl;
1889 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1890 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1893 ERR << "Could not open " << file << endl;
1897 // Translator: %s = name of an rpm package. A list of diffs follows
1899 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1902 ERR << "diff failed" << endl;
1903 notify << str::form(difffailmsg,
1904 file1s.c_str(), file2s.c_str()) << endl;
1908 notify << str::form(diffgenmsg,
1909 file1s.c_str(), file2s.c_str()) << endl;
1911 // remove root for the viewer's pleasure (#38240)
1912 if (!_root.empty() && _root != "/")
1914 if (out.substr(0,4) == "--- ")
1916 out.replace(4, file1.asString().length(), file1s);
1918 std::string::size_type pos = out.find("\n+++ ");
1919 if (pos != std::string::npos)
1921 out.replace(pos+5, file2.asString().length(), file2s);
1924 notify << out << endl;
1927 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1932 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1938 ///////////////////////////////////////////////////////////////////
1941 // METHOD NAME : RpmDb::installPackage
1942 // METHOD TYPE : PMError
1944 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1946 callback::SendReport<RpmInstallReport> report;
1948 report->start(filename);
1953 doInstallPackage(filename, flags, report);
1957 catch (RpmException & excpt_r)
1959 RpmInstallReport::Action user = report->problem( excpt_r );
1961 if ( user == RpmInstallReport::ABORT )
1963 report->finish( excpt_r );
1964 ZYPP_RETHROW(excpt_r);
1966 else if ( user == RpmInstallReport::IGNORE )
1974 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1976 FAILIFNOTINITIALIZED;
1977 HistoryLog historylog;
1979 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1983 if ( _packagebackups )
1985 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1986 if ( ! backupPackage( filename ) )
1988 ERR << "backup of " << filename.asString() << " failed" << endl;
1990 // FIXME status handling
1991 report->progress( 0 ); // allow 1% for backup creation.
1996 if (flags & RPMINST_NOUPGRADE)
1997 opts.push_back("-i");
1999 opts.push_back("-U");
2001 opts.push_back("--percent");
2002 opts.push_back("--noglob");
2004 // ZConfig defines cross-arch installation
2005 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
2006 opts.push_back("--ignorearch");
2008 if (flags & RPMINST_NODIGEST)
2009 opts.push_back("--nodigest");
2010 if (flags & RPMINST_NOSIGNATURE)
2011 opts.push_back("--nosignature");
2012 if (flags & RPMINST_EXCLUDEDOCS)
2013 opts.push_back ("--excludedocs");
2014 if (flags & RPMINST_NOSCRIPTS)
2015 opts.push_back ("--noscripts");
2016 if (flags & RPMINST_FORCE)
2017 opts.push_back ("--force");
2018 if (flags & RPMINST_NODEPS)
2019 opts.push_back ("--nodeps");
2020 if (flags & RPMINST_IGNORESIZE)
2021 opts.push_back ("--ignoresize");
2022 if (flags & RPMINST_JUSTDB)
2023 opts.push_back ("--justdb");
2024 if (flags & RPMINST_TEST)
2025 opts.push_back ("--test");
2026 if (flags & RPMINST_NOPOSTTRANS)
2027 opts.push_back ("--noposttrans");
2029 opts.push_back("--");
2031 // rpm requires additional quoting of special chars:
2032 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
2033 opts.push_back ( quotedFilename.c_str() );
2035 modifyDatabase(); // BEFORE run_rpm
2036 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
2039 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2040 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
2042 unsigned linecnt = 0;
2043 while ( systemReadLine( line ) )
2045 if ( str::startsWith( line, "%%" ) )
2048 sscanf( line.c_str() + 2, "%d", &percent );
2049 report->progress( percent );
2053 if ( linecnt < MAXRPMMESSAGELINES )
2055 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2058 rpmmsg += line+'\n';
2060 if ( str::startsWith( line, "warning:" ) )
2061 configwarnings.push_back(line);
2063 if ( linecnt >= MAXRPMMESSAGELINES )
2064 rpmmsg += "[truncated]\n";
2066 int rpm_status = systemStatus();
2069 for (std::vector<std::string>::iterator it = configwarnings.begin();
2070 it != configwarnings.end(); ++it)
2072 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2074 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2076 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2077 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2079 _("rpm created %s as %s, but it was impossible to determine the difference"),
2081 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2084 if ( rpm_status != 0 )
2087 str::form("%s install failed", Pathname::basename(filename).c_str()),
2088 true /*timestamp*/);
2089 std::ostringstream sstr;
2090 sstr << "rpm output:" << endl << rpmmsg << endl;
2091 historylog.comment(sstr.str());
2092 // TranslatorExplanation the colon is followed by an error message
2093 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
2095 else if ( ! rpmmsg.empty() )
2098 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2099 true /*timestamp*/);
2100 std::ostringstream sstr;
2101 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2102 historylog.comment(sstr.str());
2104 // report additional rpm output in finish
2105 // TranslatorExplanation Text is followed by a ':' and the actual output.
2106 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2110 ///////////////////////////////////////////////////////////////////
2113 // METHOD NAME : RpmDb::removePackage
2114 // METHOD TYPE : PMError
2116 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2118 // 'rpm -e' does not like epochs
2119 return removePackage( package->name()
2120 + "-" + package->edition().version()
2121 + "-" + package->edition().release()
2122 + "." + package->arch().asString(), flags );
2125 ///////////////////////////////////////////////////////////////////
2128 // METHOD NAME : RpmDb::removePackage
2129 // METHOD TYPE : PMError
2131 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
2133 callback::SendReport<RpmRemoveReport> report;
2135 report->start( name_r );
2140 doRemovePackage(name_r, flags, report);
2144 catch (RpmException & excpt_r)
2146 RpmRemoveReport::Action user = report->problem( excpt_r );
2148 if ( user == RpmRemoveReport::ABORT )
2150 report->finish( excpt_r );
2151 ZYPP_RETHROW(excpt_r);
2153 else if ( user == RpmRemoveReport::IGNORE )
2162 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2164 FAILIFNOTINITIALIZED;
2165 HistoryLog historylog;
2167 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2170 if ( _packagebackups )
2172 // FIXME solve this status report somehow
2173 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2174 if ( ! backupPackage( name_r ) )
2176 ERR << "backup of " << name_r << " failed" << endl;
2178 report->progress( 0 );
2182 report->progress( 100 );
2187 opts.push_back("-e");
2188 opts.push_back("--allmatches");
2190 if (flags & RPMINST_NOSCRIPTS)
2191 opts.push_back("--noscripts");
2192 if (flags & RPMINST_NODEPS)
2193 opts.push_back("--nodeps");
2194 if (flags & RPMINST_JUSTDB)
2195 opts.push_back("--justdb");
2196 if (flags & RPMINST_TEST)
2197 opts.push_back ("--test");
2198 if (flags & RPMINST_FORCE)
2200 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2203 opts.push_back("--");
2204 opts.push_back(name_r.c_str());
2206 modifyDatabase(); // BEFORE run_rpm
2207 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2210 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2212 // got no progress from command, so we fake it:
2213 // 5 - command started
2214 // 50 - command completed
2216 report->progress( 5 );
2217 unsigned linecnt = 0;
2218 while (systemReadLine(line))
2220 if ( linecnt < MAXRPMMESSAGELINES )
2222 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2224 rpmmsg += line+'\n';
2226 if ( linecnt >= MAXRPMMESSAGELINES )
2227 rpmmsg += "[truncated]\n";
2228 report->progress( 50 );
2229 int rpm_status = systemStatus();
2231 if ( rpm_status != 0 )
2234 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2235 std::ostringstream sstr;
2236 sstr << "rpm output:" << endl << rpmmsg << endl;
2237 historylog.comment(sstr.str());
2238 // TranslatorExplanation the colon is followed by an error message
2239 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
2241 else if ( ! rpmmsg.empty() )
2244 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2246 std::ostringstream sstr;
2247 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2248 historylog.comment(sstr.str());
2250 // report additional rpm output in finish
2251 // TranslatorExplanation Text is followed by a ':' and the actual output.
2252 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2256 ///////////////////////////////////////////////////////////////////
2259 // METHOD NAME : RpmDb::backupPackage
2260 // METHOD TYPE : bool
2262 bool RpmDb::backupPackage( const Pathname & filename )
2264 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2268 return backupPackage( h->tag_name() );
2271 ///////////////////////////////////////////////////////////////////
2274 // METHOD NAME : RpmDb::backupPackage
2275 // METHOD TYPE : bool
2277 bool RpmDb::backupPackage(const std::string& packageName)
2279 HistoryLog progresslog;
2281 Pathname backupFilename;
2282 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2284 if (_backuppath.empty())
2286 INT << "_backuppath empty" << endl;
2292 if (!queryChangedFiles(fileList, packageName))
2294 ERR << "Error while getting changed files for package " <<
2295 packageName << endl;
2299 if (fileList.size() <= 0)
2301 DBG << "package " << packageName << " not changed -> no backup" << endl;
2305 if (filesystem::assert_dir(_root + _backuppath) != 0)
2311 // build up archive name
2312 time_t currentTime = time(0);
2313 struct tm *currentLocalTime = localtime(¤tTime);
2315 int date = (currentLocalTime->tm_year + 1900) * 10000
2316 + (currentLocalTime->tm_mon + 1) * 100
2317 + currentLocalTime->tm_mday;
2322 backupFilename = _root + _backuppath
2323 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2326 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2328 PathInfo pi(filestobackupfile);
2329 if (pi.isExist() && !pi.isFile())
2331 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2335 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2339 ERR << "could not open " << filestobackupfile.asString() << endl;
2343 for (FileList::const_iterator cit = fileList.begin();
2344 cit != fileList.end(); ++cit)
2346 std::string name = *cit;
2347 if ( name[0] == '/' )
2349 // remove slash, file must be relative to -C parameter of tar
2350 name = name.substr( 1 );
2352 DBG << "saving file "<< name << endl;
2357 const char* const argv[] =
2362 _root.asString().c_str(),
2363 "--ignore-failed-read",
2365 backupFilename.asString().c_str(),
2367 filestobackupfile.asString().c_str(),
2371 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2372 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2376 // TODO: its probably possible to start tar with -v and watch it adding
2377 // files to report progress
2378 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2383 int ret = tar.close();
2387 ERR << "tar failed: " << tarmsg << endl;
2392 MIL << "tar backup ok" << endl;
2393 progresslog.comment(
2394 str::form(_("created backup %s"), backupFilename.asString().c_str())
2395 , /*timestamp*/true);
2398 filesystem::unlink(filestobackupfile);
2404 void RpmDb::setBackupPath(const Pathname& path)
2409 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2413 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2414 // translators: possible rpm package signature check result [brief]
2415 OUTS( CHK_OK, _("Signature is OK") );
2416 // translators: possible rpm package signature check result [brief]
2417 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2418 // translators: possible rpm package signature check result [brief]
2419 OUTS( CHK_FAIL, _("Signature does not verify") );
2420 // translators: possible rpm package signature check result [brief]
2421 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2422 // translators: possible rpm package signature check result [brief]
2423 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2424 // translators: possible rpm package signature check result [brief]
2425 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2426 // translators: possible rpm package signature check result [brief]
2427 OUTS( CHK_NOSIG, _("File is unsigned") );
2430 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2433 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2435 for ( const auto & el : obj )
2436 str << el.second << endl;
2441 } // namespace target