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 string rpmQuoteFilename( const Pathname & path_r )
81 string path( path_r.asString() );
82 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
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 // now import the key in rpm
131 _rpmdb.importPubkey( key );
133 catch (RpmException &e)
135 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
139 virtual void trustedKeyRemoved( const PublicKey &key )
141 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
143 // remove the key from rpm
146 _rpmdb.removePubkey( key );
148 catch (RpmException &e)
150 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
157 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
159 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
169 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
176 for (line = prog.receiveLine(), count=0;
178 line = prog.receiveLine(), count++ )
180 if (maxlines<0?true:count<maxlines)
189 /******************************************************************
192 ** FUNCTION NAME : stringPath
193 ** FUNCTION TYPE : inline string
195 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
197 return librpmDb::stringPath( root_r, sub_r );
200 /******************************************************************
203 ** FUNCTION NAME : operator<<
204 ** FUNCTION TYPE : ostream &
206 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
208 if ( obj == RpmDb::DbSI_NO_INIT )
214 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
216 ENUM_OUT( DbSI_HAVE_V4, 'X' );
217 ENUM_OUT( DbSI_MADE_V4, 'c' );
218 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
220 ENUM_OUT( DbSI_HAVE_V3, 'X' );
221 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
222 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
231 ///////////////////////////////////////////////////////////////////
233 // CLASS NAME : RpmDb
235 ///////////////////////////////////////////////////////////////////
237 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
239 ///////////////////////////////////////////////////////////////////
241 ///////////////////////////////////////////////////////////////////
244 // METHOD NAME : RpmDb::RpmDb
245 // METHOD TYPE : Constructor
248 : _dbStateInfo( DbSI_NO_INIT )
249 #warning Check for obsolete memebers
250 , _backuppath ("/var/adm/backup")
251 , _packagebackups(false)
252 , _warndirexists(false)
256 librpmDb::globalInit();
257 // Some rpm versions are patched not to abort installation if
258 // symlink creation failed.
259 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
260 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
263 ///////////////////////////////////////////////////////////////////
266 // METHOD NAME : RpmDb::~RpmDb
267 // METHOD TYPE : Destructor
271 MIL << "~RpmDb()" << endl;
274 MIL << "~RpmDb() end" << endl;
275 sKeyRingReceiver.reset();
278 Date RpmDb::timestamp() const
283 if ( dbPath().empty() )
284 db_path = "/var/lib/rpm";
288 PathInfo rpmdb_info(root() + db_path + "/Packages");
290 if ( rpmdb_info.isExist() )
291 return rpmdb_info.mtime();
295 ///////////////////////////////////////////////////////////////////
298 // METHOD NAME : RpmDb::dumpOn
299 // METHOD TYPE : ostream &
301 ostream & RpmDb::dumpOn( ostream & str ) const
305 if ( _dbStateInfo == DbSI_NO_INIT )
311 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
313 ENUM_OUT( DbSI_HAVE_V4, 'X' );
314 ENUM_OUT( DbSI_MADE_V4, 'c' );
315 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
317 ENUM_OUT( DbSI_HAVE_V3, 'X' );
318 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
319 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
320 str << "): " << stringPath( _root, _dbPath );
326 ///////////////////////////////////////////////////////////////////
329 // METHOD NAME : RpmDb::initDatabase
330 // METHOD TYPE : PMError
332 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
334 ///////////////////////////////////////////////////////////////////
336 ///////////////////////////////////////////////////////////////////
337 bool quickinit( root_r.empty() );
339 if ( root_r.empty() )
342 if ( dbPath_r.empty() )
343 dbPath_r = "/var/lib/rpm";
345 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
347 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
348 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
351 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
352 << ( doRebuild_r ? " (rebuilddb)" : "" )
353 << ( quickinit ? " (quickinit)" : "" ) << endl;
355 ///////////////////////////////////////////////////////////////////
356 // Check whether already initialized
357 ///////////////////////////////////////////////////////////////////
360 if ( root_r == _root && dbPath_r == _dbPath )
366 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
370 ///////////////////////////////////////////////////////////////////
372 ///////////////////////////////////////////////////////////////////
373 librpmDb::unblockAccess();
377 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
381 DbStateInfoBits info = DbSI_NO_INIT;
384 internal_initDatabase( root_r, dbPath_r, info );
386 catch (const RpmException & excpt_r)
388 ZYPP_CAUGHT(excpt_r);
389 librpmDb::blockAccess();
390 ERR << "Cleanup on error: state " << info << endl;
392 if ( dbsi_has( info, DbSI_MADE_V4 ) )
394 // remove the newly created rpm4 database and
395 // any backup created on conversion.
396 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
398 ZYPP_RETHROW(excpt_r);
400 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
402 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
404 // Move obsolete rpm3 database beside.
405 MIL << "Cleanup: state " << info << endl;
406 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
407 dbsi_clr( info, DbSI_HAVE_V3 );
411 // Performing an update: Keep the original rpm3 database
412 // and wait if the rpm4 database gets modified by installing
413 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
414 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
417 #warning CHECK: notify root about conversion backup.
425 if ( dbsi_has( info, DbSI_HAVE_V4 )
426 && ! dbsi_has( info, DbSI_MADE_V4 ) )
432 MIL << "Synchronizing keys with zypp keyring" << endl;
435 // Close the database in case any write acces (create/convert)
436 // happened during init. This should drop any lock acquired
437 // by librpm. On demand it will be reopened readonly and should
438 // not hold any lock.
439 librpmDb::dbRelease( true );
441 MIL << "InitDatabase: " << *this << endl;
444 ///////////////////////////////////////////////////////////////////
447 // METHOD NAME : RpmDb::internal_initDatabase
448 // METHOD TYPE : PMError
450 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
451 DbStateInfoBits & info_r )
453 info_r = DbSI_NO_INIT;
455 ///////////////////////////////////////////////////////////////////
456 // Get info about the desired database dir
457 ///////////////////////////////////////////////////////////////////
458 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
460 if ( dbInfo.illegalArgs() )
462 // should not happen (checked in initDatabase)
463 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
465 if ( ! dbInfo.usableArgs() )
467 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
468 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
471 if ( dbInfo.hasDbV4() )
473 dbsi_set( info_r, DbSI_HAVE_V4 );
474 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
478 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
481 if ( dbInfo.hasDbV3() )
483 dbsi_set( info_r, DbSI_HAVE_V3 );
485 if ( dbInfo.hasDbV3ToV4() )
487 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
490 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
491 librpmDb::dumpState( DBG ) << endl;
493 ///////////////////////////////////////////////////////////////////
494 // Access database, create if needed
495 ///////////////////////////////////////////////////////////////////
497 // creates dbdir and empty rpm4 database if not present
498 librpmDb::dbAccess( root_r, dbPath_r );
500 if ( ! dbInfo.hasDbV4() )
503 if ( dbInfo.hasDbV4() )
505 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
509 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
510 librpmDb::dumpState( DBG ) << endl;
512 ///////////////////////////////////////////////////////////////////
513 // Check whether to convert something. Create backup but do
514 // not remove anything here
515 ///////////////////////////////////////////////////////////////////
516 librpmDb::constPtr dbptr;
517 librpmDb::dbAccess( dbptr );
518 bool dbEmpty = dbptr->empty();
521 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
524 if ( dbInfo.hasDbV3() )
526 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
530 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
531 convertV3toV4( dbInfo.dbV3().path(), dbptr );
533 // create a backup copy
534 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
537 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
542 if ( dbInfo.hasDbV3ToV4() )
544 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
545 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
553 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
554 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
555 dbsi_set( info_r, DbSI_MODIFIED_V4 );
559 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
560 librpmDb::dumpState( DBG ) << endl;
563 if ( dbInfo.hasDbV3ToV4() )
565 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
569 ///////////////////////////////////////////////////////////////////
572 // METHOD NAME : RpmDb::removeV4
573 // METHOD TYPE : void
575 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
577 const char * v3backup = "packages.rpm3";
578 const char * master = "Packages";
579 const char * index[] =
601 PathInfo pi( dbdir_r );
604 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
608 for ( const char ** f = index; *f; ++f )
613 filesystem::unlink( pi.path() );
617 pi( dbdir_r + master );
620 MIL << "Removing rpm4 database " << pi << endl;
621 filesystem::unlink( pi.path() );
626 pi( dbdir_r + v3backup );
629 MIL << "Removing converted rpm3 database backup " << pi << endl;
630 filesystem::unlink( pi.path() );
635 ///////////////////////////////////////////////////////////////////
638 // METHOD NAME : RpmDb::removeV3
639 // METHOD TYPE : void
641 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
643 const char * master = "packages.rpm";
644 const char * index[] =
646 "conflictsindex.rpm",
657 PathInfo pi( dbdir_r );
660 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
664 for ( const char ** f = index; *f; ++f )
669 filesystem::unlink( pi.path() );
673 #warning CHECK: compare vs existing v3 backup. notify root
674 pi( dbdir_r + master );
677 Pathname m( pi.path() );
680 // backup was already created
681 filesystem::unlink( m );
682 Pathname b( m.extend( "3" ) );
683 pi( b ); // stat backup
687 Pathname b( m.extend( ".deleted" ) );
691 // rempve existing backup
692 filesystem::unlink( b );
694 filesystem::rename( m, b );
695 pi( b ); // stat backup
697 MIL << "(Re)moved rpm3 database to " << pi << endl;
701 ///////////////////////////////////////////////////////////////////
704 // METHOD NAME : RpmDb::modifyDatabase
705 // METHOD TYPE : void
707 void RpmDb::modifyDatabase()
709 if ( ! initialized() )
712 // tag database as modified
713 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
715 // Move outdated rpm3 database beside.
716 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
718 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
719 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
720 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
724 ///////////////////////////////////////////////////////////////////
727 // METHOD NAME : RpmDb::closeDatabase
728 // METHOD TYPE : PMError
730 void RpmDb::closeDatabase()
732 if ( ! initialized() )
737 MIL << "Calling closeDatabase: " << *this << endl;
739 ///////////////////////////////////////////////////////////////////
740 // Block further database access
741 ///////////////////////////////////////////////////////////////////
742 librpmDb::blockAccess();
744 ///////////////////////////////////////////////////////////////////
745 // Check fate if old version database still present
746 ///////////////////////////////////////////////////////////////////
747 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
749 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
750 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
752 // Move outdated rpm3 database beside.
753 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
757 // Remove unmodified rpm4 database
758 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
762 ///////////////////////////////////////////////////////////////////
764 ///////////////////////////////////////////////////////////////////
765 _root = _dbPath = Pathname();
766 _dbStateInfo = DbSI_NO_INIT;
768 MIL << "closeDatabase: " << *this << endl;
771 ///////////////////////////////////////////////////////////////////
774 // METHOD NAME : RpmDb::rebuildDatabase
775 // METHOD TYPE : PMError
777 void RpmDb::rebuildDatabase()
779 callback::SendReport<RebuildDBReport> report;
781 report->start( root() + dbPath() );
785 doRebuildDatabase(report);
787 catch (RpmException & excpt_r)
789 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
790 ZYPP_RETHROW(excpt_r);
792 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
795 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
797 FAILIFNOTINITIALIZED;
799 MIL << "RpmDb::rebuildDatabase" << *this << endl;
800 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
802 PathInfo dbMaster( root() + dbPath() + "Packages" );
803 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
807 opts.push_back("--rebuilddb");
808 opts.push_back("-vv");
810 // don't call modifyDatabase because it would remove the old
811 // rpm3 database, if the current database is a temporary one.
812 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
814 // progress report: watch this file growing
815 PathInfo newMaster( root()
816 + dbPath().extend( str::form( "rebuilddb.%d",
817 process?process->getpid():0) )
823 while ( systemReadLine( line ) )
826 { // file is removed at the end of rebuild.
827 // current size should be upper limit for new db
828 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
830 WAR << "User requested abort." << endl;
832 filesystem::recursive_rmdir( newMaster.path().dirname() );
836 if ( line.compare( 0, 2, "D:" ) )
838 errmsg += line + '\n';
839 // report.notify( line );
844 int rpm_status = systemStatus();
846 if ( rpm_status != 0 )
848 //TranslatorExplanation after semicolon is error message
849 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
850 (errmsg.empty() ? error_message: errmsg))));
854 report->progress( 100, root() + dbPath() ); // 100%
858 ///////////////////////////////////////////////////////////////////
861 /** \ref RpmDb::syncTrustedKeys helper
862 * Compute which keys need to be exprted to / imported from the zypp keyring.
863 * Return result via argument list.
865 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
867 ///////////////////////////////////////////////////////////////////
868 // Remember latest release and where it ocurred
872 : _inRpmKeys( nullptr )
873 , _inZyppKeys( nullptr )
876 void updateIf( const Edition & rpmKey_r )
878 std::string keyRelease( rpmKey_r.release() );
879 int comp = _release.compare( keyRelease );
882 // update to newer release
883 _release.swap( keyRelease );
884 _inRpmKeys = &rpmKey_r;
885 _inZyppKeys = nullptr;
886 if ( !keyRelease.empty() )
887 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
889 else if ( comp == 0 )
891 // stay with this release
893 _inRpmKeys = &rpmKey_r;
895 // else: this is an old release
897 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
900 void updateIf( const PublicKeyData & zyppKey_r )
902 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
903 int comp = _release.compare( keyRelease );
906 // update to newer release
907 _release.swap( keyRelease );
908 _inRpmKeys = nullptr;
909 _inZyppKeys = &zyppKey_r;
910 if ( !keyRelease.empty() )
911 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
913 else if ( comp == 0 )
915 // stay with this release
917 _inZyppKeys = &zyppKey_r;
919 // else: this is an old release
921 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
924 std::string _release;
925 const Edition * _inRpmKeys;
926 const PublicKeyData * _inZyppKeys;
928 ///////////////////////////////////////////////////////////////////
930 // collect keys by ID(version) and latest creation(release)
931 std::map<std::string,Key> _keymap;
933 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
935 _keymap[(*it).version()].updateIf( *it );
938 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
940 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
943 // compute missing keys
944 std::set<Edition> rpmKeys;
945 std::list<PublicKeyData> zyppKeys;
946 for_( it, _keymap.begin(), _keymap.end() )
948 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
949 << ( (*it).second._inRpmKeys ? "R" : "_" )
950 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
951 if ( ! (*it).second._inRpmKeys )
953 zyppKeys.push_back( *(*it).second._inZyppKeys );
955 if ( ! (*it).second._inZyppKeys )
957 rpmKeys.insert( *(*it).second._inRpmKeys );
960 rpmKeys_r.swap( rpmKeys );
961 zyppKeys_r.swap( zyppKeys );
964 ///////////////////////////////////////////////////////////////////
966 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
968 MIL << "Going to sync trusted keys..." << endl;
969 std::set<Edition> rpmKeys( pubkeyEditions() );
970 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
971 computeKeyRingSync( rpmKeys, zyppKeys );
972 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
973 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
975 ///////////////////////////////////////////////////////////////////
976 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
978 // export to zypp keyring
979 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
980 // Temporarily disconnect to prevent the attemt to re-import the exported keys.
981 callback::TempConnect<KeyRingSignals> tempDisconnect;
982 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
984 TmpFile tmpfile( getZYpp()->tmpPath() );
986 ofstream tmpos( tmpfile.path().c_str() );
987 for_( it, rpmKeys.begin(), rpmKeys.end() )
989 // we export the rpm key into a file
990 RpmHeader::constPtr result;
991 getData( string("gpg-pubkey"), *it, result );
992 tmpos << result->tag_description() << endl;
997 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1001 ERR << "Could not import keys into in zypp keyring" << endl;
1005 ///////////////////////////////////////////////////////////////////
1006 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1008 // import from zypp keyring
1009 MIL << "Importing zypp trusted keyring" << std::endl;
1010 for_( it, zyppKeys.begin(), zyppKeys.end() )
1014 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1016 catch ( const RpmException & exp )
1022 MIL << "Trusted keys synced." << endl;
1025 void RpmDb::importZyppKeyRingTrustedKeys()
1026 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1028 void RpmDb::exportTrustedKeysInZyppKeyRing()
1029 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1031 ///////////////////////////////////////////////////////////////////
1034 // METHOD NAME : RpmDb::importPubkey
1035 // METHOD TYPE : PMError
1037 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1039 FAILIFNOTINITIALIZED;
1041 // bnc#828672: On the fly key import in READONLY
1042 if ( zypp_readonly_hack::IGotIt() )
1044 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1048 // check if the key is already in the rpm database
1049 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1050 set<Edition> rpmKeys = pubkeyEditions();
1051 bool hasOldkeys = false;
1053 for_( it, rpmKeys.begin(), rpmKeys.end() )
1055 // bsc#1008325: Keys using subkeys for signing don't get a higher release
1056 // if new subkeys are added, because the primary key remains unchanged.
1057 // For now always re-import keys with subkeys. Here we don't want to export the
1058 // keys in the rpm database to check whether the subkeys are the same. The calling
1059 // code should take care, we don't re-import the same kesy over and over again.
1060 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
1062 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1066 if ( keyEd.version() != (*it).version() )
1067 continue; // different key ID (version)
1069 if ( keyEd.release() < (*it).release() )
1071 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1079 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1083 // We must explicitly delete old key IDs first (all releases,
1084 // that's why we don't call removePubkey here).
1085 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1087 opts.push_back ( "-e" );
1088 opts.push_back ( "--allmatches" );
1089 opts.push_back ( "--" );
1090 opts.push_back ( keyName.c_str() );
1091 // don't call modifyDatabase because it would remove the old
1092 // rpm3 database, if the current database is a temporary one.
1093 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1096 while ( systemReadLine( line ) )
1098 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1101 if ( systemStatus() != 0 )
1103 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1107 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1111 // import the new key
1113 opts.push_back ( "--import" );
1114 opts.push_back ( "--" );
1115 opts.push_back ( pubkey_r.path().asString().c_str() );
1117 // don't call modifyDatabase because it would remove the old
1118 // rpm3 database, if the current database is a temporary one.
1119 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1122 while ( systemReadLine( line ) )
1124 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1127 if ( systemStatus() != 0 )
1129 //TranslatorExplanation first %s is file name, second is error message
1130 ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to import public key from file %s: %s"))
1131 % pubkey_r.asString()
1136 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1140 ///////////////////////////////////////////////////////////////////
1143 // METHOD NAME : RpmDb::removePubkey
1144 // METHOD TYPE : PMError
1146 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1148 FAILIFNOTINITIALIZED;
1150 // check if the key is in the rpm database and just
1151 // return if it does not.
1152 set<Edition> rpm_keys = pubkeyEditions();
1153 set<Edition>::const_iterator found_edition = rpm_keys.end();
1154 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1156 for_( it, rpm_keys.begin(), rpm_keys.end() )
1158 if ( (*it).version() == pubkeyVersion )
1165 // the key does not exist, cannot be removed
1166 if (found_edition == rpm_keys.end())
1168 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1172 string rpm_name("gpg-pubkey-" + found_edition->asString());
1175 opts.push_back ( "-e" );
1176 opts.push_back ( "--" );
1177 opts.push_back ( rpm_name.c_str() );
1179 // don't call modifyDatabase because it would remove the old
1180 // rpm3 database, if the current database is a temporary one.
1181 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1184 while ( systemReadLine( line ) )
1186 if ( line.substr( 0, 6 ) == "error:" )
1188 WAR << line << endl;
1192 DBG << line << endl;
1196 int rpm_status = systemStatus();
1198 if ( rpm_status != 0 )
1200 //TranslatorExplanation first %s is key name, second is error message
1201 ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to remove public key %s: %s"))
1202 % pubkey_r.asString()
1207 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1211 ///////////////////////////////////////////////////////////////////
1214 // METHOD NAME : RpmDb::pubkeys
1215 // METHOD TYPE : set<Edition>
1217 list<PublicKey> RpmDb::pubkeys() const
1219 list<PublicKey> ret;
1221 librpmDb::db_const_iterator it;
1222 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1224 Edition edition = it->tag_edition();
1225 if (edition != Edition::noedition)
1227 // we export the rpm key into a file
1228 RpmHeader::constPtr result;
1229 getData( string("gpg-pubkey"), edition, result );
1230 TmpFile file(getZYpp()->tmpPath());
1234 os.open(file.path().asString().c_str());
1235 // dump rpm key into the tmp file
1236 os << result->tag_description();
1237 //MIL << "-----------------------------------------------" << endl;
1238 //MIL << result->tag_description() <<endl;
1239 //MIL << "-----------------------------------------------" << endl;
1241 // read the public key from the dumped file
1242 PublicKey key(file);
1245 catch (exception &e)
1247 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1248 // just ignore the key
1255 set<Edition> RpmDb::pubkeyEditions() const
1259 librpmDb::db_const_iterator it;
1260 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1262 Edition edition = it->tag_edition();
1263 if (edition != Edition::noedition)
1264 ret.insert( edition );
1270 ///////////////////////////////////////////////////////////////////
1273 // METHOD NAME : RpmDb::fileList
1274 // METHOD TYPE : bool
1279 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1281 list<FileInfo> result;
1283 librpmDb::db_const_iterator it;
1285 if (edition_r == Edition::noedition)
1287 found = it.findPackage( name_r );
1291 found = it.findPackage( name_r, edition_r );
1300 ///////////////////////////////////////////////////////////////////
1303 // METHOD NAME : RpmDb::hasFile
1304 // METHOD TYPE : bool
1308 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1310 librpmDb::db_const_iterator it;
1314 res = it.findByFile( file_r );
1316 if (!name_r.empty())
1318 res = (it->tag_name() == name_r);
1326 ///////////////////////////////////////////////////////////////////
1329 // METHOD NAME : RpmDb::whoOwnsFile
1330 // METHOD TYPE : string
1334 string RpmDb::whoOwnsFile( const string & file_r) const
1336 librpmDb::db_const_iterator it;
1337 if (it.findByFile( file_r ))
1339 return it->tag_name();
1344 ///////////////////////////////////////////////////////////////////
1347 // METHOD NAME : RpmDb::hasProvides
1348 // METHOD TYPE : bool
1352 bool RpmDb::hasProvides( const string & tag_r ) const
1354 librpmDb::db_const_iterator it;
1355 return it.findByProvides( tag_r );
1358 ///////////////////////////////////////////////////////////////////
1361 // METHOD NAME : RpmDb::hasRequiredBy
1362 // METHOD TYPE : bool
1366 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1368 librpmDb::db_const_iterator it;
1369 return it.findByRequiredBy( tag_r );
1372 ///////////////////////////////////////////////////////////////////
1375 // METHOD NAME : RpmDb::hasConflicts
1376 // METHOD TYPE : bool
1380 bool RpmDb::hasConflicts( const string & tag_r ) const
1382 librpmDb::db_const_iterator it;
1383 return it.findByConflicts( tag_r );
1386 ///////////////////////////////////////////////////////////////////
1389 // METHOD NAME : RpmDb::hasPackage
1390 // METHOD TYPE : bool
1394 bool RpmDb::hasPackage( const string & name_r ) const
1396 librpmDb::db_const_iterator it;
1397 return it.findPackage( name_r );
1400 ///////////////////////////////////////////////////////////////////
1403 // METHOD NAME : RpmDb::hasPackage
1404 // METHOD TYPE : bool
1408 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1410 librpmDb::db_const_iterator it;
1411 return it.findPackage( name_r, ed_r );
1414 ///////////////////////////////////////////////////////////////////
1417 // METHOD NAME : RpmDb::getData
1418 // METHOD TYPE : PMError
1422 void RpmDb::getData( const string & name_r,
1423 RpmHeader::constPtr & result_r ) const
1425 librpmDb::db_const_iterator it;
1426 it.findPackage( name_r );
1429 ZYPP_THROW(*(it.dbError()));
1432 ///////////////////////////////////////////////////////////////////
1435 // METHOD NAME : RpmDb::getData
1436 // METHOD TYPE : void
1440 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1441 RpmHeader::constPtr & result_r ) const
1443 librpmDb::db_const_iterator it;
1444 it.findPackage( name_r, ed_r );
1447 ZYPP_THROW(*(it.dbError()));
1450 ///////////////////////////////////////////////////////////////////
1453 struct RpmlogCapture : public std::string
1456 { rpmlog()._cap = this; }
1459 { rpmlog()._cap = nullptr; }
1467 rpmlogSetCallback( rpmLogCB, this );
1468 rpmSetVerbosity( RPMLOG_INFO );
1469 _f = ::fopen( "/dev/null","w");
1470 rpmlogSetFile( _f );
1474 { if ( _f ) ::fclose( _f ); }
1476 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1477 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1479 int rpmLog( rpmlogRec rec_r )
1481 if ( _cap ) (*_cap) = rpmlogRecMessage( rec_r );
1482 return RPMLOG_DEFAULT;
1489 static Rpmlog & rpmlog()
1490 { static Rpmlog _rpmlog; return _rpmlog; }
1493 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1494 const Pathname & root_r, // target root
1495 bool requireGPGSig_r, // whether no gpg signature is to be reported
1496 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1498 PathInfo file( path_r );
1499 if ( ! file.isFile() )
1501 ERR << "Not a file: " << file << endl;
1502 return RpmDb::CHK_ERROR;
1505 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1506 if ( fd == 0 || ::Ferror(fd) )
1508 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1511 return RpmDb::CHK_ERROR;
1513 rpmts ts = ::rpmtsCreate();
1514 ::rpmtsSetRootDir( ts, root_r.c_str() );
1515 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1517 rpmQVKArguments_s qva;
1518 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1519 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1521 RpmlogCapture vresult;
1522 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1523 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1529 // results per line...
1530 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1531 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1532 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1533 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1535 // TODO: try to get SIG info from the header rather than parsing the output
1536 std::vector<std::string> lines;
1537 str::split( vresult, std::back_inserter(lines), "\n" );
1538 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1540 for ( unsigned i = 1; i < lines.size(); ++i )
1542 std::string & line( lines[i] );
1543 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1544 if ( line.find( ": OK" ) != std::string::npos )
1546 lineres = RpmDb::CHK_OK;
1547 if ( line.find( "Signature, key ID" ) == std::string::npos )
1548 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1550 else if ( line.find( ": NOKEY" ) != std::string::npos )
1551 { lineres = RpmDb::CHK_NOKEY; }
1552 else if ( line.find( ": BAD" ) != std::string::npos )
1553 { lineres = RpmDb::CHK_FAIL; }
1554 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1555 { lineres = RpmDb::CHK_NOTFOUND; }
1556 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1557 { lineres = RpmDb::CHK_NOTTRUSTED; }
1560 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1563 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1565 if ( count[RpmDb::CHK_FAIL] )
1566 ret = RpmDb::CHK_FAIL;
1568 else if ( count[RpmDb::CHK_NOTFOUND] )
1569 ret = RpmDb::CHK_NOTFOUND;
1571 else if ( count[RpmDb::CHK_NOKEY] )
1572 ret = RpmDb::CHK_NOKEY;
1574 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1575 ret = RpmDb::CHK_NOTTRUSTED;
1577 else if ( ret == RpmDb::CHK_OK )
1579 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1581 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1582 if ( requireGPGSig_r )
1583 ret = RpmDb::CHK_NOSIG;
1587 if ( ret != RpmDb::CHK_OK )
1589 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1596 ///////////////////////////////////////////////////////////////////
1598 // METHOD NAME : RpmDb::checkPackage
1599 // METHOD TYPE : RpmDb::CheckPackageResult
1601 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1602 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1604 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1605 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1607 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1608 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1611 // determine changed files of installed package
1613 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1619 if ( ! initialized() ) return false;
1623 opts.push_back ("-V");
1624 opts.push_back ("--nodeps");
1625 opts.push_back ("--noscripts");
1626 opts.push_back ("--nomd5");
1627 opts.push_back ("--");
1628 opts.push_back (packageName.c_str());
1630 run_rpm (opts, ExternalProgram::Discard_Stderr);
1632 if ( process == NULL )
1643 M Mode (includes permissions and file type)
1647 while (systemReadLine(line))
1649 if (line.length() > 12 &&
1650 (line[0] == 'S' || line[0] == 's' ||
1651 (line[0] == '.' && line[7] == 'T')))
1653 // file has been changed
1656 filename.assign(line, 11, line.length() - 11);
1657 fileList.insert(filename);
1662 // exit code ignored, rpm returns 1 no matter if package is installed or
1670 /****************************************************************/
1671 /* private member-functions */
1672 /****************************************************************/
1674 /*--------------------------------------------------------------*/
1675 /* Run rpm with the specified arguments, handling stderr */
1676 /* as specified by disp */
1677 /*--------------------------------------------------------------*/
1679 RpmDb::run_rpm (const RpmArgVec& opts,
1680 ExternalProgram::Stderr_Disposition disp)
1689 if ( ! initialized() )
1691 ZYPP_THROW(RpmDbNotOpenException());
1696 // always set root and dbpath
1697 #if defined(WORKAROUNDRPMPWDBUG)
1698 args.push_back("#/"); // chdir to / to workaround bnc#819354
1700 args.push_back("rpm");
1701 args.push_back("--root");
1702 args.push_back(_root.asString().c_str());
1703 args.push_back("--dbpath");
1704 args.push_back(_dbPath.asString().c_str());
1706 const char* argv[args.size() + opts.size() + 1];
1708 const char** p = argv;
1709 p = copy (args.begin (), args.end (), p);
1710 p = copy (opts.begin (), opts.end (), p);
1713 // Invalidate all outstanding database handles in case
1714 // the database gets modified.
1715 librpmDb::dbRelease( true );
1717 // Launch the program with default locale
1718 process = new ExternalProgram(argv, disp, false, -1, true);
1722 /*--------------------------------------------------------------*/
1723 /* Read a line from the rpm process */
1724 /*--------------------------------------------------------------*/
1725 bool RpmDb::systemReadLine( string & line )
1729 if ( process == NULL )
1732 if ( process->inputFile() )
1734 process->setBlocking( false );
1735 FILE * inputfile = process->inputFile();
1736 int inputfileFd = ::fileno( inputfile );
1739 /* Watch inputFile to see when it has input. */
1742 FD_SET( inputfileFd, &rfds );
1744 /* Wait up to 5 seconds. */
1749 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1753 ERR << "select error: " << strerror(errno) << endl;
1754 if ( errno != EINTR )
1759 // Data is available now.
1760 static size_t linebuffer_size = 0; // static because getline allocs
1761 static char * linebuffer = 0; // and reallocs if buffer is too small
1762 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1765 if ( ::feof( inputfile ) )
1766 return line.size(); // in case of pending output
1772 if ( linebuffer[nread-1] == '\n' )
1774 line += string( linebuffer, nread );
1777 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1778 return true; // complete line
1780 clearerr( inputfile );
1784 // No data within time.
1785 if ( ! process->running() )
1794 /*--------------------------------------------------------------*/
1795 /* Return the exit status of the rpm process, closing the */
1796 /* connection if not already done */
1797 /*--------------------------------------------------------------*/
1799 RpmDb::systemStatus()
1801 if ( process == NULL )
1804 exit_code = process->close();
1808 error_message = process->execError();
1813 // DBG << "exit code " << exit_code << endl;
1818 /*--------------------------------------------------------------*/
1819 /* Forcably kill the rpm process */
1820 /*--------------------------------------------------------------*/
1824 if (process) process->kill();
1828 // generate diff mails for config files
1829 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1831 string msg = line.substr(9);
1832 string::size_type pos1 = string::npos;
1833 string::size_type pos2 = string::npos;
1834 string file1s, file2s;
1838 pos1 = msg.find (typemsg);
1841 if ( pos1 == string::npos )
1844 pos2 = pos1 + strlen (typemsg);
1846 if (pos2 >= msg.length() )
1849 file1 = msg.substr (0, pos1);
1850 file2 = msg.substr (pos2);
1852 file1s = file1.asString();
1853 file2s = file2.asString();
1855 if (!_root.empty() && _root != "/")
1857 file1 = _root + file1;
1858 file2 = _root + file2;
1862 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1865 Pathname file = _root + WARNINGMAILPATH;
1866 if (filesystem::assert_dir(file) != 0)
1868 ERR << "Could not create " << file.asString() << endl;
1871 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1872 ofstream notify(file.asString().c_str(), ios::out|ios::app);
1875 ERR << "Could not open " << file << endl;
1879 // Translator: %s = name of an rpm package. A list of diffs follows
1881 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1884 ERR << "diff failed" << endl;
1885 notify << str::form(difffailmsg,
1886 file1s.c_str(), file2s.c_str()) << endl;
1890 notify << str::form(diffgenmsg,
1891 file1s.c_str(), file2s.c_str()) << endl;
1893 // remove root for the viewer's pleasure (#38240)
1894 if (!_root.empty() && _root != "/")
1896 if (out.substr(0,4) == "--- ")
1898 out.replace(4, file1.asString().length(), file1s);
1900 string::size_type pos = out.find("\n+++ ");
1901 if (pos != string::npos)
1903 out.replace(pos+5, file2.asString().length(), file2s);
1906 notify << out << endl;
1909 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1914 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1920 ///////////////////////////////////////////////////////////////////
1923 // METHOD NAME : RpmDb::installPackage
1924 // METHOD TYPE : PMError
1926 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1928 callback::SendReport<RpmInstallReport> report;
1930 report->start(filename);
1935 doInstallPackage(filename, flags, report);
1939 catch (RpmException & excpt_r)
1941 RpmInstallReport::Action user = report->problem( excpt_r );
1943 if ( user == RpmInstallReport::ABORT )
1945 report->finish( excpt_r );
1946 ZYPP_RETHROW(excpt_r);
1948 else if ( user == RpmInstallReport::IGNORE )
1956 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1958 FAILIFNOTINITIALIZED;
1959 HistoryLog historylog;
1961 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1965 if ( _packagebackups )
1967 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1968 if ( ! backupPackage( filename ) )
1970 ERR << "backup of " << filename.asString() << " failed" << endl;
1972 // FIXME status handling
1973 report->progress( 0 ); // allow 1% for backup creation.
1978 if (flags & RPMINST_NOUPGRADE)
1979 opts.push_back("-i");
1981 opts.push_back("-U");
1983 opts.push_back("--percent");
1984 opts.push_back("--noglob");
1986 // ZConfig defines cross-arch installation
1987 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1988 opts.push_back("--ignorearch");
1990 if (flags & RPMINST_NODIGEST)
1991 opts.push_back("--nodigest");
1992 if (flags & RPMINST_NOSIGNATURE)
1993 opts.push_back("--nosignature");
1994 if (flags & RPMINST_EXCLUDEDOCS)
1995 opts.push_back ("--excludedocs");
1996 if (flags & RPMINST_NOSCRIPTS)
1997 opts.push_back ("--noscripts");
1998 if (flags & RPMINST_FORCE)
1999 opts.push_back ("--force");
2000 if (flags & RPMINST_NODEPS)
2001 opts.push_back ("--nodeps");
2002 if (flags & RPMINST_IGNORESIZE)
2003 opts.push_back ("--ignoresize");
2004 if (flags & RPMINST_JUSTDB)
2005 opts.push_back ("--justdb");
2006 if (flags & RPMINST_TEST)
2007 opts.push_back ("--test");
2008 if (flags & RPMINST_NOPOSTTRANS)
2009 opts.push_back ("--noposttrans");
2011 opts.push_back("--");
2013 // rpm requires additional quoting of special chars:
2014 string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
2015 opts.push_back ( quotedFilename.c_str() );
2017 modifyDatabase(); // BEFORE run_rpm
2018 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
2022 vector<string> configwarnings;
2024 unsigned linecnt = 0;
2025 while (systemReadLine(line))
2027 if ( linecnt < MAXRPMMESSAGELINES )
2032 if (line.substr(0,2)=="%%")
2035 sscanf (line.c_str () + 2, "%d", &percent);
2036 report->progress( percent );
2039 rpmmsg += line+'\n';
2041 if ( line.substr(0,8) == "warning:" )
2043 configwarnings.push_back(line);
2046 if ( linecnt > MAXRPMMESSAGELINES )
2047 rpmmsg += "[truncated]\n";
2049 int rpm_status = systemStatus();
2052 for (vector<string>::iterator it = configwarnings.begin();
2053 it != configwarnings.end(); ++it)
2055 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2057 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2059 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2060 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2062 _("rpm created %s as %s, but it was impossible to determine the difference"),
2064 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2067 if ( rpm_status != 0 )
2070 str::form("%s install failed", Pathname::basename(filename).c_str()),
2071 true /*timestamp*/);
2073 sstr << "rpm output:" << endl << rpmmsg << endl;
2074 historylog.comment(sstr.str());
2075 // TranslatorExplanation the colon is followed by an error message
2076 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2077 (rpmmsg.empty() ? error_message : rpmmsg)));
2079 else if ( ! rpmmsg.empty() )
2082 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2083 true /*timestamp*/);
2085 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2086 historylog.comment(sstr.str());
2088 // report additional rpm output in finish
2089 // TranslatorExplanation Text is followed by a ':' and the actual output.
2090 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2094 ///////////////////////////////////////////////////////////////////
2097 // METHOD NAME : RpmDb::removePackage
2098 // METHOD TYPE : PMError
2100 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2102 // 'rpm -e' does not like epochs
2103 return removePackage( package->name()
2104 + "-" + package->edition().version()
2105 + "-" + package->edition().release()
2106 + "." + package->arch().asString(), flags );
2109 ///////////////////////////////////////////////////////////////////
2112 // METHOD NAME : RpmDb::removePackage
2113 // METHOD TYPE : PMError
2115 void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
2117 callback::SendReport<RpmRemoveReport> report;
2119 report->start( name_r );
2124 doRemovePackage(name_r, flags, report);
2128 catch (RpmException & excpt_r)
2130 RpmRemoveReport::Action user = report->problem( excpt_r );
2132 if ( user == RpmRemoveReport::ABORT )
2134 report->finish( excpt_r );
2135 ZYPP_RETHROW(excpt_r);
2137 else if ( user == RpmRemoveReport::IGNORE )
2146 void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2148 FAILIFNOTINITIALIZED;
2149 HistoryLog historylog;
2151 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2154 if ( _packagebackups )
2156 // FIXME solve this status report somehow
2157 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2158 if ( ! backupPackage( name_r ) )
2160 ERR << "backup of " << name_r << " failed" << endl;
2162 report->progress( 0 );
2166 report->progress( 100 );
2171 opts.push_back("-e");
2172 opts.push_back("--allmatches");
2174 if (flags & RPMINST_NOSCRIPTS)
2175 opts.push_back("--noscripts");
2176 if (flags & RPMINST_NODEPS)
2177 opts.push_back("--nodeps");
2178 if (flags & RPMINST_JUSTDB)
2179 opts.push_back("--justdb");
2180 if (flags & RPMINST_TEST)
2181 opts.push_back ("--test");
2182 if (flags & RPMINST_FORCE)
2184 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2187 opts.push_back("--");
2188 opts.push_back(name_r.c_str());
2190 modifyDatabase(); // BEFORE run_rpm
2191 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2196 // got no progress from command, so we fake it:
2197 // 5 - command started
2198 // 50 - command completed
2200 report->progress( 5 );
2201 unsigned linecnt = 0;
2202 while (systemReadLine(line))
2204 if ( linecnt < MAXRPMMESSAGELINES )
2208 rpmmsg += line+'\n';
2210 if ( linecnt > MAXRPMMESSAGELINES )
2211 rpmmsg += "[truncated]\n";
2212 report->progress( 50 );
2213 int rpm_status = systemStatus();
2215 if ( rpm_status != 0 )
2218 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2220 sstr << "rpm output:" << endl << rpmmsg << endl;
2221 historylog.comment(sstr.str());
2222 // TranslatorExplanation the colon is followed by an error message
2223 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2224 (rpmmsg.empty() ? error_message: rpmmsg)));
2226 else if ( ! rpmmsg.empty() )
2229 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2232 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2233 historylog.comment(sstr.str());
2235 // report additional rpm output in finish
2236 // TranslatorExplanation Text is followed by a ':' and the actual output.
2237 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2241 ///////////////////////////////////////////////////////////////////
2244 // METHOD NAME : RpmDb::backupPackage
2245 // METHOD TYPE : bool
2247 bool RpmDb::backupPackage( const Pathname & filename )
2249 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2253 return backupPackage( h->tag_name() );
2256 ///////////////////////////////////////////////////////////////////
2259 // METHOD NAME : RpmDb::backupPackage
2260 // METHOD TYPE : bool
2262 bool RpmDb::backupPackage(const string& packageName)
2264 HistoryLog progresslog;
2266 Pathname backupFilename;
2267 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2269 if (_backuppath.empty())
2271 INT << "_backuppath empty" << endl;
2277 if (!queryChangedFiles(fileList, packageName))
2279 ERR << "Error while getting changed files for package " <<
2280 packageName << endl;
2284 if (fileList.size() <= 0)
2286 DBG << "package " << packageName << " not changed -> no backup" << endl;
2290 if (filesystem::assert_dir(_root + _backuppath) != 0)
2296 // build up archive name
2297 time_t currentTime = time(0);
2298 struct tm *currentLocalTime = localtime(¤tTime);
2300 int date = (currentLocalTime->tm_year + 1900) * 10000
2301 + (currentLocalTime->tm_mon + 1) * 100
2302 + currentLocalTime->tm_mday;
2307 backupFilename = _root + _backuppath
2308 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2311 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2313 PathInfo pi(filestobackupfile);
2314 if (pi.isExist() && !pi.isFile())
2316 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2320 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2324 ERR << "could not open " << filestobackupfile.asString() << endl;
2328 for (FileList::const_iterator cit = fileList.begin();
2329 cit != fileList.end(); ++cit)
2332 if ( name[0] == '/' )
2334 // remove slash, file must be relative to -C parameter of tar
2335 name = name.substr( 1 );
2337 DBG << "saving file "<< name << endl;
2342 const char* const argv[] =
2347 _root.asString().c_str(),
2348 "--ignore-failed-read",
2350 backupFilename.asString().c_str(),
2352 filestobackupfile.asString().c_str(),
2356 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2357 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2361 // TODO: its probably possible to start tar with -v and watch it adding
2362 // files to report progress
2363 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2368 int ret = tar.close();
2372 ERR << "tar failed: " << tarmsg << endl;
2377 MIL << "tar backup ok" << endl;
2378 progresslog.comment(
2379 str::form(_("created backup %s"), backupFilename.asString().c_str())
2380 , /*timestamp*/true);
2383 filesystem::unlink(filestobackupfile);
2389 void RpmDb::setBackupPath(const Pathname& path)
2394 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2398 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2399 // translators: possible rpm package signature check result [brief]
2400 OUTS( CHK_OK, _("Signature is OK") );
2401 // translators: possible rpm package signature check result [brief]
2402 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2403 // translators: possible rpm package signature check result [brief]
2404 OUTS( CHK_FAIL, _("Signature does not verify") );
2405 // translators: possible rpm package signature check result [brief]
2406 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2407 // translators: possible rpm package signature check result [brief]
2408 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2409 // translators: possible rpm package signature check result [brief]
2410 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2411 // translators: possible rpm package signature check result [brief]
2412 OUTS( CHK_NOSIG, _("File is unsigned") );
2415 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2418 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2420 for ( const auto & el : obj )
2421 str << el.second << endl;
2426 } // namespace target