1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
32 #include "zypp/base/Logger.h"
33 #include "zypp/base/String.h"
34 #include "zypp/base/Gettext.h"
36 #include "zypp/Date.h"
37 #include "zypp/Pathname.h"
38 #include "zypp/PathInfo.h"
39 #include "zypp/PublicKey.h"
41 #include "zypp/target/rpm/RpmDb.h"
42 #include "zypp/target/rpm/RpmCallbacks.h"
44 #include "zypp/HistoryLog.h"
45 #include "zypp/target/rpm/librpmDb.h"
46 #include "zypp/target/rpm/RpmException.h"
47 #include "zypp/TmpPath.h"
48 #include "zypp/KeyRing.h"
49 #include "zypp/ZYppFactory.h"
50 #include "zypp/ZConfig.h"
53 using namespace zypp::filesystem;
55 #define WARNINGMAILPATH "/var/log/YaST2/"
56 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
57 #define MAXRPMMESSAGELINES 10000
59 #define WORKAROUNDRPMPWDBUG
63 namespace zypp_readonly_hack
65 bool IGotIt(); // in readonly-mode
73 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
74 const char* quoteInFilename_m = "\'\"";
76 const char* quoteInFilename_m = " \t\'\"";
78 inline std::string rpmQuoteFilename( const Pathname & path_r )
80 std::string path( path_r.asString() );
81 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
82 pos != std::string::npos;
83 pos = path.find_first_of( quoteInFilename_m, pos ) )
85 path.insert( pos, "\\" );
86 pos += 2; // skip '\\' and the quoted char.
92 /** Workaround bnc#827609 - rpm needs a readable pwd so we
93 * chdir to /. Turn realtive pathnames into absolute ones
94 * by prepending cwd so rpm still finds them
96 inline Pathname workaroundRpmPwdBug( Pathname path_r )
98 #if defined(WORKAROUNDRPMPWDBUG)
99 if ( path_r.relative() )
101 // try to prepend cwd
102 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
104 return Pathname( cwd ) / path_r;
105 WAR << "Can't get cwd!" << endl;
108 return path_r; // no problem with absolute pathnames
112 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
114 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
119 ~KeyRingSignalReceiver()
124 virtual void trustedKeyAdded( const PublicKey &key )
126 MIL << "trusted key added to zypp Keyring. Importing" << endl;
127 // now import the key in rpm
130 _rpmdb.importPubkey( key );
132 catch (RpmException &e)
134 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
138 virtual void trustedKeyRemoved( const PublicKey &key )
140 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
142 // remove the key from rpm
145 _rpmdb.removePubkey( key );
147 catch (RpmException &e)
149 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
156 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
158 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
168 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
175 for (line = prog.receiveLine(), count=0;
177 line = prog.receiveLine(), count++ )
179 if (maxlines<0?true:count<maxlines)
188 /******************************************************************
191 ** FUNCTION NAME : stringPath
192 ** FUNCTION TYPE : inline std::string
194 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
196 return librpmDb::stringPath( root_r, sub_r );
199 /******************************************************************
202 ** FUNCTION NAME : operator<<
203 ** FUNCTION TYPE : std::ostream &
205 std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
207 if ( obj == RpmDb::DbSI_NO_INIT )
213 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
215 ENUM_OUT( DbSI_HAVE_V4, 'X' );
216 ENUM_OUT( DbSI_MADE_V4, 'c' );
217 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
219 ENUM_OUT( DbSI_HAVE_V3, 'X' );
220 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
221 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
230 ///////////////////////////////////////////////////////////////////
232 // CLASS NAME : RpmDb
234 ///////////////////////////////////////////////////////////////////
236 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
238 ///////////////////////////////////////////////////////////////////
240 ///////////////////////////////////////////////////////////////////
243 // METHOD NAME : RpmDb::RpmDb
244 // METHOD TYPE : Constructor
247 : _dbStateInfo( DbSI_NO_INIT )
248 #warning Check for obsolete memebers
249 , _backuppath ("/var/adm/backup")
250 , _packagebackups(false)
251 , _warndirexists(false)
255 librpmDb::globalInit();
256 // Some rpm versions are patched not to abort installation if
257 // symlink creation failed.
258 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
259 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
262 ///////////////////////////////////////////////////////////////////
265 // METHOD NAME : RpmDb::~RpmDb
266 // METHOD TYPE : Destructor
270 MIL << "~RpmDb()" << endl;
273 MIL << "~RpmDb() end" << endl;
274 sKeyRingReceiver.reset();
277 Date RpmDb::timestamp() const
282 if ( dbPath().empty() )
283 db_path = "/var/lib/rpm";
287 PathInfo rpmdb_info(root() + db_path + "/Packages");
289 if ( rpmdb_info.isExist() )
290 return rpmdb_info.mtime();
294 ///////////////////////////////////////////////////////////////////
297 // METHOD NAME : RpmDb::dumpOn
298 // METHOD TYPE : std::ostream &
300 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
304 if ( _dbStateInfo == DbSI_NO_INIT )
310 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
312 ENUM_OUT( DbSI_HAVE_V4, 'X' );
313 ENUM_OUT( DbSI_MADE_V4, 'c' );
314 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
316 ENUM_OUT( DbSI_HAVE_V3, 'X' );
317 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
318 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
319 str << "): " << stringPath( _root, _dbPath );
325 ///////////////////////////////////////////////////////////////////
328 // METHOD NAME : RpmDb::initDatabase
329 // METHOD TYPE : PMError
331 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
333 ///////////////////////////////////////////////////////////////////
335 ///////////////////////////////////////////////////////////////////
336 bool quickinit( root_r.empty() );
338 if ( root_r.empty() )
341 if ( dbPath_r.empty() )
342 dbPath_r = "/var/lib/rpm";
344 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
346 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
347 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
350 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
351 << ( doRebuild_r ? " (rebuilddb)" : "" )
352 << ( quickinit ? " (quickinit)" : "" ) << endl;
354 ///////////////////////////////////////////////////////////////////
355 // Check whether already initialized
356 ///////////////////////////////////////////////////////////////////
359 if ( root_r == _root && dbPath_r == _dbPath )
365 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
369 ///////////////////////////////////////////////////////////////////
371 ///////////////////////////////////////////////////////////////////
372 librpmDb::unblockAccess();
376 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
380 DbStateInfoBits info = DbSI_NO_INIT;
383 internal_initDatabase( root_r, dbPath_r, info );
385 catch (const RpmException & excpt_r)
387 ZYPP_CAUGHT(excpt_r);
388 librpmDb::blockAccess();
389 ERR << "Cleanup on error: state " << info << endl;
391 if ( dbsi_has( info, DbSI_MADE_V4 ) )
393 // remove the newly created rpm4 database and
394 // any backup created on conversion.
395 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
397 ZYPP_RETHROW(excpt_r);
399 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
401 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
403 // Move obsolete rpm3 database beside.
404 MIL << "Cleanup: state " << info << endl;
405 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
406 dbsi_clr( info, DbSI_HAVE_V3 );
410 // Performing an update: Keep the original rpm3 database
411 // and wait if the rpm4 database gets modified by installing
412 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
413 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
416 #warning CHECK: notify root about conversion backup.
424 if ( dbsi_has( info, DbSI_HAVE_V4 )
425 && ! dbsi_has( info, DbSI_MADE_V4 ) )
431 MIL << "Synchronizing keys with zypp keyring" << endl;
434 // Close the database in case any write acces (create/convert)
435 // happened during init. This should drop any lock acquired
436 // by librpm. On demand it will be reopened readonly and should
437 // not hold any lock.
438 librpmDb::dbRelease( true );
440 MIL << "InitDatabase: " << *this << endl;
443 ///////////////////////////////////////////////////////////////////
446 // METHOD NAME : RpmDb::internal_initDatabase
447 // METHOD TYPE : PMError
449 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
450 DbStateInfoBits & info_r )
452 info_r = DbSI_NO_INIT;
454 ///////////////////////////////////////////////////////////////////
455 // Get info about the desired database dir
456 ///////////////////////////////////////////////////////////////////
457 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
459 if ( dbInfo.illegalArgs() )
461 // should not happen (checked in initDatabase)
462 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
464 if ( ! dbInfo.usableArgs() )
466 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
467 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
470 if ( dbInfo.hasDbV4() )
472 dbsi_set( info_r, DbSI_HAVE_V4 );
473 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
477 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
480 if ( dbInfo.hasDbV3() )
482 dbsi_set( info_r, DbSI_HAVE_V3 );
484 if ( dbInfo.hasDbV3ToV4() )
486 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
489 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
490 librpmDb::dumpState( DBG ) << endl;
492 ///////////////////////////////////////////////////////////////////
493 // Access database, create if needed
494 ///////////////////////////////////////////////////////////////////
496 // creates dbdir and empty rpm4 database if not present
497 librpmDb::dbAccess( root_r, dbPath_r );
499 if ( ! dbInfo.hasDbV4() )
502 if ( dbInfo.hasDbV4() )
504 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
508 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
509 librpmDb::dumpState( DBG ) << endl;
511 ///////////////////////////////////////////////////////////////////
512 // Check whether to convert something. Create backup but do
513 // not remove anything here
514 ///////////////////////////////////////////////////////////////////
515 librpmDb::constPtr dbptr;
516 librpmDb::dbAccess( dbptr );
517 bool dbEmpty = dbptr->empty();
520 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
523 if ( dbInfo.hasDbV3() )
525 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
529 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
530 convertV3toV4( dbInfo.dbV3().path(), dbptr );
532 // create a backup copy
533 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
536 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
541 if ( dbInfo.hasDbV3ToV4() )
543 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
544 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
552 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
553 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
554 dbsi_set( info_r, DbSI_MODIFIED_V4 );
558 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
559 librpmDb::dumpState( DBG ) << endl;
562 if ( dbInfo.hasDbV3ToV4() )
564 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
568 ///////////////////////////////////////////////////////////////////
571 // METHOD NAME : RpmDb::removeV4
572 // METHOD TYPE : void
574 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
576 const char * v3backup = "packages.rpm3";
577 const char * master = "Packages";
578 const char * index[] =
600 PathInfo pi( dbdir_r );
603 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
607 for ( const char ** f = index; *f; ++f )
612 filesystem::unlink( pi.path() );
616 pi( dbdir_r + master );
619 MIL << "Removing rpm4 database " << pi << endl;
620 filesystem::unlink( pi.path() );
625 pi( dbdir_r + v3backup );
628 MIL << "Removing converted rpm3 database backup " << pi << endl;
629 filesystem::unlink( pi.path() );
634 ///////////////////////////////////////////////////////////////////
637 // METHOD NAME : RpmDb::removeV3
638 // METHOD TYPE : void
640 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
642 const char * master = "packages.rpm";
643 const char * index[] =
645 "conflictsindex.rpm",
656 PathInfo pi( dbdir_r );
659 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
663 for ( const char ** f = index; *f; ++f )
668 filesystem::unlink( pi.path() );
672 #warning CHECK: compare vs existing v3 backup. notify root
673 pi( dbdir_r + master );
676 Pathname m( pi.path() );
679 // backup was already created
680 filesystem::unlink( m );
681 Pathname b( m.extend( "3" ) );
682 pi( b ); // stat backup
686 Pathname b( m.extend( ".deleted" ) );
690 // rempve existing backup
691 filesystem::unlink( b );
693 filesystem::rename( m, b );
694 pi( b ); // stat backup
696 MIL << "(Re)moved rpm3 database to " << pi << endl;
700 ///////////////////////////////////////////////////////////////////
703 // METHOD NAME : RpmDb::modifyDatabase
704 // METHOD TYPE : void
706 void RpmDb::modifyDatabase()
708 if ( ! initialized() )
711 // tag database as modified
712 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
714 // Move outdated rpm3 database beside.
715 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
717 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
718 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
719 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
723 ///////////////////////////////////////////////////////////////////
726 // METHOD NAME : RpmDb::closeDatabase
727 // METHOD TYPE : PMError
729 void RpmDb::closeDatabase()
731 if ( ! initialized() )
736 MIL << "Calling closeDatabase: " << *this << endl;
738 ///////////////////////////////////////////////////////////////////
739 // Block further database access
740 ///////////////////////////////////////////////////////////////////
741 librpmDb::blockAccess();
743 ///////////////////////////////////////////////////////////////////
744 // Check fate if old version database still present
745 ///////////////////////////////////////////////////////////////////
746 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
748 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
749 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
751 // Move outdated rpm3 database beside.
752 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
756 // Remove unmodified rpm4 database
757 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
761 ///////////////////////////////////////////////////////////////////
763 ///////////////////////////////////////////////////////////////////
764 _root = _dbPath = Pathname();
765 _dbStateInfo = DbSI_NO_INIT;
767 MIL << "closeDatabase: " << *this << endl;
770 ///////////////////////////////////////////////////////////////////
773 // METHOD NAME : RpmDb::rebuildDatabase
774 // METHOD TYPE : PMError
776 void RpmDb::rebuildDatabase()
778 callback::SendReport<RebuildDBReport> report;
780 report->start( root() + dbPath() );
784 doRebuildDatabase(report);
786 catch (RpmException & excpt_r)
788 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
789 ZYPP_RETHROW(excpt_r);
791 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
794 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
796 FAILIFNOTINITIALIZED;
798 MIL << "RpmDb::rebuildDatabase" << *this << endl;
799 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
801 PathInfo dbMaster( root() + dbPath() + "Packages" );
802 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
806 opts.push_back("--rebuilddb");
807 opts.push_back("-vv");
809 // don't call modifyDatabase because it would remove the old
810 // rpm3 database, if the current database is a temporary one.
811 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
813 // progress report: watch this file growing
814 PathInfo newMaster( root()
815 + dbPath().extend( str::form( "rebuilddb.%d",
816 process?process->getpid():0) )
822 while ( systemReadLine( line ) )
825 { // file is removed at the end of rebuild.
826 // current size should be upper limit for new db
827 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
829 WAR << "User requested abort." << endl;
831 filesystem::recursive_rmdir( newMaster.path().dirname() );
835 if ( line.compare( 0, 2, "D:" ) )
837 errmsg += line + '\n';
838 // report.notify( line );
843 int rpm_status = systemStatus();
845 if ( rpm_status != 0 )
847 //TranslatorExplanation after semicolon is error message
848 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
852 report->progress( 100, root() + dbPath() ); // 100%
856 ///////////////////////////////////////////////////////////////////
859 /** \ref RpmDb::syncTrustedKeys helper
860 * Compute which keys need to be exprted to / imported from the zypp keyring.
861 * Return result via argument list.
863 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
865 ///////////////////////////////////////////////////////////////////
866 // Remember latest release and where it ocurred
870 : _inRpmKeys( nullptr )
871 , _inZyppKeys( nullptr )
874 void updateIf( const Edition & rpmKey_r )
876 std::string keyRelease( rpmKey_r.release() );
877 int comp = _release.compare( keyRelease );
880 // update to newer release
881 _release.swap( keyRelease );
882 _inRpmKeys = &rpmKey_r;
883 _inZyppKeys = nullptr;
884 if ( !keyRelease.empty() )
885 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
887 else if ( comp == 0 )
889 // stay with this release
891 _inRpmKeys = &rpmKey_r;
893 // else: this is an old release
895 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
898 void updateIf( const PublicKeyData & zyppKey_r )
900 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
901 int comp = _release.compare( keyRelease );
904 // update to newer release
905 _release.swap( keyRelease );
906 _inRpmKeys = nullptr;
907 _inZyppKeys = &zyppKey_r;
908 if ( !keyRelease.empty() )
909 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
911 else if ( comp == 0 )
913 // stay with this release
915 _inZyppKeys = &zyppKey_r;
917 // else: this is an old release
919 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
922 std::string _release;
923 const Edition * _inRpmKeys;
924 const PublicKeyData * _inZyppKeys;
926 ///////////////////////////////////////////////////////////////////
928 // collect keys by ID(version) and latest creation(release)
929 std::map<std::string,Key> _keymap;
931 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
933 _keymap[(*it).version()].updateIf( *it );
936 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
938 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
941 // compute missing keys
942 std::set<Edition> rpmKeys;
943 std::list<PublicKeyData> zyppKeys;
944 for_( it, _keymap.begin(), _keymap.end() )
946 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
947 << ( (*it).second._inRpmKeys ? "R" : "_" )
948 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
949 if ( ! (*it).second._inRpmKeys )
951 zyppKeys.push_back( *(*it).second._inZyppKeys );
953 if ( ! (*it).second._inZyppKeys )
955 rpmKeys.insert( *(*it).second._inRpmKeys );
958 rpmKeys_r.swap( rpmKeys );
959 zyppKeys_r.swap( zyppKeys );
962 ///////////////////////////////////////////////////////////////////
964 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
966 MIL << "Going to sync trusted keys..." << endl;
967 std::set<Edition> rpmKeys( pubkeyEditions() );
968 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
969 computeKeyRingSync( rpmKeys, zyppKeys );
970 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
971 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
973 ///////////////////////////////////////////////////////////////////
974 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
976 // export to zypp keyring
977 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
978 // Temporarily disconnect to prevent the attemt to re-import the exported keys.
979 callback::TempConnect<KeyRingSignals> tempDisconnect;
980 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
982 TmpFile tmpfile( getZYpp()->tmpPath() );
984 std::ofstream tmpos( tmpfile.path().c_str() );
985 for_( it, rpmKeys.begin(), rpmKeys.end() )
987 // we export the rpm key into a file
988 RpmHeader::constPtr result;
989 getData( "gpg-pubkey", *it, result );
990 tmpos << result->tag_description() << endl;
995 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
999 ERR << "Could not import keys into in zypp keyring" << endl;
1003 ///////////////////////////////////////////////////////////////////
1004 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1006 // import from zypp keyring
1007 MIL << "Importing zypp trusted keyring" << std::endl;
1008 for_( it, zyppKeys.begin(), zyppKeys.end() )
1012 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1014 catch ( const RpmException & exp )
1020 MIL << "Trusted keys synced." << endl;
1023 void RpmDb::importZyppKeyRingTrustedKeys()
1024 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1026 void RpmDb::exportTrustedKeysInZyppKeyRing()
1027 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1029 ///////////////////////////////////////////////////////////////////
1032 // METHOD NAME : RpmDb::importPubkey
1033 // METHOD TYPE : PMError
1035 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1037 FAILIFNOTINITIALIZED;
1039 // bnc#828672: On the fly key import in READONLY
1040 if ( zypp_readonly_hack::IGotIt() )
1042 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1046 // check if the key is already in the rpm database
1047 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1048 std::set<Edition> rpmKeys = pubkeyEditions();
1049 bool hasOldkeys = false;
1051 for_( it, rpmKeys.begin(), rpmKeys.end() )
1053 if ( keyEd == *it ) // quick test (Edition is IdStringType!)
1055 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1059 if ( keyEd.version() != (*it).version() )
1060 continue; // different key ID (version)
1062 if ( keyEd.release() < (*it).release() )
1064 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1072 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1076 // We must explicitly delete old key IDs first (all releases,
1077 // that's why we don't call removePubkey here).
1078 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1080 opts.push_back ( "-e" );
1081 opts.push_back ( "--allmatches" );
1082 opts.push_back ( "--" );
1083 opts.push_back ( keyName.c_str() );
1084 // don't call modifyDatabase because it would remove the old
1085 // rpm3 database, if the current database is a temporary one.
1086 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1089 while ( systemReadLine( line ) )
1091 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1094 if ( systemStatus() != 0 )
1096 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1100 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1104 // import the new key
1106 opts.push_back ( "--import" );
1107 opts.push_back ( "--" );
1108 std::string pubkeypath( pubkey_r.path().asString() );
1109 opts.push_back ( pubkeypath.c_str() );
1111 // don't call modifyDatabase because it would remove the old
1112 // rpm3 database, if the current database is a temporary one.
1113 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1116 while ( systemReadLine( line ) )
1118 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1121 if ( systemStatus() != 0 )
1123 //TranslatorExplanation first %s is file name, second is error message
1124 ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to import public key from file %s: %s"))
1125 % pubkey_r.asString()
1130 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1134 ///////////////////////////////////////////////////////////////////
1137 // METHOD NAME : RpmDb::removePubkey
1138 // METHOD TYPE : PMError
1140 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1142 FAILIFNOTINITIALIZED;
1144 // check if the key is in the rpm database and just
1145 // return if it does not.
1146 std::set<Edition> rpm_keys = pubkeyEditions();
1147 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
1148 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1150 for_( it, rpm_keys.begin(), rpm_keys.end() )
1152 if ( (*it).version() == pubkeyVersion )
1159 // the key does not exist, cannot be removed
1160 if (found_edition == rpm_keys.end())
1162 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1166 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
1169 opts.push_back ( "-e" );
1170 opts.push_back ( "--" );
1171 opts.push_back ( rpm_name.c_str() );
1173 // don't call modifyDatabase because it would remove the old
1174 // rpm3 database, if the current database is a temporary one.
1175 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1178 while ( systemReadLine( line ) )
1180 if ( line.substr( 0, 6 ) == "error:" )
1182 WAR << line << endl;
1186 DBG << line << endl;
1190 int rpm_status = systemStatus();
1192 if ( rpm_status != 0 )
1194 //TranslatorExplanation first %s is key name, second is error message
1195 ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to remove public key %s: %s"))
1196 % pubkey_r.asString()
1201 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1205 ///////////////////////////////////////////////////////////////////
1208 // METHOD NAME : RpmDb::pubkeys
1209 // METHOD TYPE : std::set<Edition>
1211 std::list<PublicKey> RpmDb::pubkeys() const
1213 std::list<PublicKey> ret;
1215 librpmDb::db_const_iterator it;
1216 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1218 Edition edition = it->tag_edition();
1219 if (edition != Edition::noedition)
1221 // we export the rpm key into a file
1222 RpmHeader::constPtr result;
1223 getData( "gpg-pubkey", edition, result );
1224 TmpFile file(getZYpp()->tmpPath());
1228 os.open(file.path().asString().c_str());
1229 // dump rpm key into the tmp file
1230 os << result->tag_description();
1231 //MIL << "-----------------------------------------------" << endl;
1232 //MIL << result->tag_description() <<endl;
1233 //MIL << "-----------------------------------------------" << endl;
1235 // read the public key from the dumped file
1236 PublicKey key(file);
1239 catch ( std::exception & e )
1241 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1242 // just ignore the key
1249 std::set<Edition> RpmDb::pubkeyEditions() const
1251 std::set<Edition> ret;
1253 librpmDb::db_const_iterator it;
1254 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1256 Edition edition = it->tag_edition();
1257 if (edition != Edition::noedition)
1258 ret.insert( edition );
1264 ///////////////////////////////////////////////////////////////////
1267 // METHOD NAME : RpmDb::fileList
1268 // METHOD TYPE : bool
1273 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
1275 std::list<FileInfo> result;
1277 librpmDb::db_const_iterator it;
1279 if (edition_r == Edition::noedition)
1281 found = it.findPackage( name_r );
1285 found = it.findPackage( name_r, edition_r );
1294 ///////////////////////////////////////////////////////////////////
1297 // METHOD NAME : RpmDb::hasFile
1298 // METHOD TYPE : bool
1302 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
1304 librpmDb::db_const_iterator it;
1308 res = it.findByFile( file_r );
1310 if (!name_r.empty())
1312 res = (it->tag_name() == name_r);
1320 ///////////////////////////////////////////////////////////////////
1323 // METHOD NAME : RpmDb::whoOwnsFile
1324 // METHOD TYPE : std::string
1328 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
1330 librpmDb::db_const_iterator it;
1331 if (it.findByFile( file_r ))
1333 return it->tag_name();
1338 ///////////////////////////////////////////////////////////////////
1341 // METHOD NAME : RpmDb::hasProvides
1342 // METHOD TYPE : bool
1346 bool RpmDb::hasProvides( const std::string & tag_r ) const
1348 librpmDb::db_const_iterator it;
1349 return it.findByProvides( tag_r );
1352 ///////////////////////////////////////////////////////////////////
1355 // METHOD NAME : RpmDb::hasRequiredBy
1356 // METHOD TYPE : bool
1360 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1362 librpmDb::db_const_iterator it;
1363 return it.findByRequiredBy( tag_r );
1366 ///////////////////////////////////////////////////////////////////
1369 // METHOD NAME : RpmDb::hasConflicts
1370 // METHOD TYPE : bool
1374 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1376 librpmDb::db_const_iterator it;
1377 return it.findByConflicts( tag_r );
1380 ///////////////////////////////////////////////////////////////////
1383 // METHOD NAME : RpmDb::hasPackage
1384 // METHOD TYPE : bool
1388 bool RpmDb::hasPackage( const std::string & name_r ) const
1390 librpmDb::db_const_iterator it;
1391 return it.findPackage( name_r );
1394 ///////////////////////////////////////////////////////////////////
1397 // METHOD NAME : RpmDb::hasPackage
1398 // METHOD TYPE : bool
1402 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1404 librpmDb::db_const_iterator it;
1405 return it.findPackage( name_r, ed_r );
1408 ///////////////////////////////////////////////////////////////////
1411 // METHOD NAME : RpmDb::getData
1412 // METHOD TYPE : PMError
1416 void RpmDb::getData( const std::string & name_r,
1417 RpmHeader::constPtr & result_r ) const
1419 librpmDb::db_const_iterator it;
1420 it.findPackage( name_r );
1423 ZYPP_THROW(*(it.dbError()));
1426 ///////////////////////////////////////////////////////////////////
1429 // METHOD NAME : RpmDb::getData
1430 // METHOD TYPE : void
1434 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1435 RpmHeader::constPtr & result_r ) const
1437 librpmDb::db_const_iterator it;
1438 it.findPackage( name_r, ed_r );
1441 ZYPP_THROW(*(it.dbError()));
1444 ///////////////////////////////////////////////////////////////////
1447 struct RpmlogCapture : public std::string
1450 { rpmlog()._cap = this; }
1453 { rpmlog()._cap = nullptr; }
1461 rpmlogSetCallback( rpmLogCB, this );
1462 rpmSetVerbosity( RPMLOG_INFO );
1463 _f = ::fopen( "/dev/null","w");
1464 rpmlogSetFile( _f );
1468 { if ( _f ) ::fclose( _f ); }
1470 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1471 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1473 int rpmLog( rpmlogRec rec_r )
1475 if ( _cap ) (*_cap) = rpmlogRecMessage( rec_r );
1476 return RPMLOG_DEFAULT;
1483 static Rpmlog & rpmlog()
1484 { static Rpmlog _rpmlog; return _rpmlog; }
1489 ///////////////////////////////////////////////////////////////////
1491 // METHOD NAME : RpmDb::checkPackage
1492 // METHOD TYPE : RpmDb::CheckPackageResult
1494 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1496 PathInfo file( path_r );
1497 if ( ! file.isFile() )
1499 ERR << "Not a file: " << file << endl;
1503 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1504 if ( fd == 0 || ::Ferror(fd) )
1506 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1511 rpmts ts = ::rpmtsCreate();
1512 ::rpmtsSetRootDir( ts, root().asString().c_str() );
1513 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1515 rpmQVKArguments_s qva;
1516 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1517 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1519 RpmlogCapture vresult;
1520 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1528 // remove trailing NL!
1529 detail_r.push_back( CheckPackageDetail::value_type( CHK_OK, str::rtrim( std::move(vresult) ) ) );
1533 // results per line...
1535 std::vector<std::string> lines;
1536 str::split( vresult, std::back_inserter(lines), "\n" );
1537 unsigned count[6] = { 0, 0, 0, 0, 0, 0 };
1539 for ( unsigned i = 1; i < lines.size(); ++i )
1541 std::string & line( lines[i] );
1542 CheckPackageResult lineres = CHK_ERROR;
1543 if ( line.find( ": OK" ) != std::string::npos )
1544 { lineres = CHK_OK; }
1545 else if ( line.find( ": NOKEY" ) != std::string::npos )
1546 { lineres = CHK_NOKEY; }
1547 else if ( line.find( ": BAD" ) != std::string::npos )
1548 { lineres = CHK_FAIL; }
1549 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1550 { lineres = CHK_NOTFOUND; }
1551 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1552 { lineres = CHK_NOTTRUSTED; }
1555 detail_r.push_back( CheckPackageDetail::value_type( lineres, std::move(line) ) );
1558 CheckPackageResult ret = CHK_ERROR;
1559 if ( count[CHK_FAIL] )
1562 else if ( count[CHK_NOTFOUND] )
1565 else if ( count[CHK_NOKEY] )
1568 else if ( count[CHK_NOTTRUSTED] )
1569 ret = CHK_NOTTRUSTED;
1574 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1575 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1578 // determine changed files of installed package
1580 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1586 if ( ! initialized() ) return false;
1590 opts.push_back ("-V");
1591 opts.push_back ("--nodeps");
1592 opts.push_back ("--noscripts");
1593 opts.push_back ("--nomd5");
1594 opts.push_back ("--");
1595 opts.push_back (packageName.c_str());
1597 run_rpm (opts, ExternalProgram::Discard_Stderr);
1599 if ( process == NULL )
1610 M Mode (includes permissions and file type)
1614 while (systemReadLine(line))
1616 if (line.length() > 12 &&
1617 (line[0] == 'S' || line[0] == 's' ||
1618 (line[0] == '.' && line[7] == 'T')))
1620 // file has been changed
1621 std::string filename;
1623 filename.assign(line, 11, line.length() - 11);
1624 fileList.insert(filename);
1629 // exit code ignored, rpm returns 1 no matter if package is installed or
1637 /****************************************************************/
1638 /* private member-functions */
1639 /****************************************************************/
1641 /*--------------------------------------------------------------*/
1642 /* Run rpm with the specified arguments, handling stderr */
1643 /* as specified by disp */
1644 /*--------------------------------------------------------------*/
1646 RpmDb::run_rpm (const RpmArgVec& opts,
1647 ExternalProgram::Stderr_Disposition disp)
1656 if ( ! initialized() )
1658 ZYPP_THROW(RpmDbNotOpenException());
1663 // always set root and dbpath
1664 #if defined(WORKAROUNDRPMPWDBUG)
1665 args.push_back("#/"); // chdir to / to workaround bnc#819354
1667 args.push_back("rpm");
1668 args.push_back("--root");
1669 args.push_back(_root.asString().c_str());
1670 args.push_back("--dbpath");
1671 args.push_back(_dbPath.asString().c_str());
1673 const char* argv[args.size() + opts.size() + 1];
1675 const char** p = argv;
1676 p = copy (args.begin (), args.end (), p);
1677 p = copy (opts.begin (), opts.end (), p);
1680 // Invalidate all outstanding database handles in case
1681 // the database gets modified.
1682 librpmDb::dbRelease( true );
1684 // Launch the program with default locale
1685 process = new ExternalProgram(argv, disp, false, -1, true);
1689 /*--------------------------------------------------------------*/
1690 /* Read a line from the rpm process */
1691 /*--------------------------------------------------------------*/
1692 bool RpmDb::systemReadLine( std::string & line )
1696 if ( process == NULL )
1699 if ( process->inputFile() )
1701 process->setBlocking( false );
1702 FILE * inputfile = process->inputFile();
1703 int inputfileFd = ::fileno( inputfile );
1706 /* Watch inputFile to see when it has input. */
1709 FD_SET( inputfileFd, &rfds );
1711 /* Wait up to 5 seconds. */
1716 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1720 ERR << "select error: " << strerror(errno) << endl;
1721 if ( errno != EINTR )
1726 // Data is available now.
1727 static size_t linebuffer_size = 0; // static because getline allocs
1728 static char * linebuffer = 0; // and reallocs if buffer is too small
1729 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1732 if ( ::feof( inputfile ) )
1733 return line.size(); // in case of pending output
1739 if ( linebuffer[nread-1] == '\n' )
1741 line += std::string( linebuffer, nread );
1744 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1745 return true; // complete line
1747 clearerr( inputfile );
1751 // No data within time.
1752 if ( ! process->running() )
1761 /*--------------------------------------------------------------*/
1762 /* Return the exit status of the rpm process, closing the */
1763 /* connection if not already done */
1764 /*--------------------------------------------------------------*/
1766 RpmDb::systemStatus()
1768 if ( process == NULL )
1771 exit_code = process->close();
1775 error_message = process->execError();
1780 // DBG << "exit code " << exit_code << endl;
1785 /*--------------------------------------------------------------*/
1786 /* Forcably kill the rpm process */
1787 /*--------------------------------------------------------------*/
1791 if (process) process->kill();
1795 // generate diff mails for config files
1796 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1798 std::string msg = line.substr(9);
1799 std::string::size_type pos1 = std::string::npos;
1800 std::string::size_type pos2 = std::string::npos;
1801 std::string file1s, file2s;
1805 pos1 = msg.find (typemsg);
1808 if ( pos1 == std::string::npos )
1811 pos2 = pos1 + strlen (typemsg);
1813 if (pos2 >= msg.length() )
1816 file1 = msg.substr (0, pos1);
1817 file2 = msg.substr (pos2);
1819 file1s = file1.asString();
1820 file2s = file2.asString();
1822 if (!_root.empty() && _root != "/")
1824 file1 = _root + file1;
1825 file2 = _root + file2;
1829 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1832 Pathname file = _root + WARNINGMAILPATH;
1833 if (filesystem::assert_dir(file) != 0)
1835 ERR << "Could not create " << file.asString() << endl;
1838 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1839 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1842 ERR << "Could not open " << file << endl;
1846 // Translator: %s = name of an rpm package. A list of diffs follows
1848 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1851 ERR << "diff failed" << endl;
1852 notify << str::form(difffailmsg,
1853 file1s.c_str(), file2s.c_str()) << endl;
1857 notify << str::form(diffgenmsg,
1858 file1s.c_str(), file2s.c_str()) << endl;
1860 // remove root for the viewer's pleasure (#38240)
1861 if (!_root.empty() && _root != "/")
1863 if (out.substr(0,4) == "--- ")
1865 out.replace(4, file1.asString().length(), file1s);
1867 std::string::size_type pos = out.find("\n+++ ");
1868 if (pos != std::string::npos)
1870 out.replace(pos+5, file2.asString().length(), file2s);
1873 notify << out << endl;
1876 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1881 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1887 ///////////////////////////////////////////////////////////////////
1890 // METHOD NAME : RpmDb::installPackage
1891 // METHOD TYPE : PMError
1893 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1895 callback::SendReport<RpmInstallReport> report;
1897 report->start(filename);
1902 doInstallPackage(filename, flags, report);
1906 catch (RpmException & excpt_r)
1908 RpmInstallReport::Action user = report->problem( excpt_r );
1910 if ( user == RpmInstallReport::ABORT )
1912 report->finish( excpt_r );
1913 ZYPP_RETHROW(excpt_r);
1915 else if ( user == RpmInstallReport::IGNORE )
1923 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1925 FAILIFNOTINITIALIZED;
1926 HistoryLog historylog;
1928 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1932 if ( _packagebackups )
1934 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1935 if ( ! backupPackage( filename ) )
1937 ERR << "backup of " << filename.asString() << " failed" << endl;
1939 // FIXME status handling
1940 report->progress( 0 ); // allow 1% for backup creation.
1945 if (flags & RPMINST_NOUPGRADE)
1946 opts.push_back("-i");
1948 opts.push_back("-U");
1950 opts.push_back("--percent");
1951 opts.push_back("--noglob");
1953 // ZConfig defines cross-arch installation
1954 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1955 opts.push_back("--ignorearch");
1957 if (flags & RPMINST_NODIGEST)
1958 opts.push_back("--nodigest");
1959 if (flags & RPMINST_NOSIGNATURE)
1960 opts.push_back("--nosignature");
1961 if (flags & RPMINST_EXCLUDEDOCS)
1962 opts.push_back ("--excludedocs");
1963 if (flags & RPMINST_NOSCRIPTS)
1964 opts.push_back ("--noscripts");
1965 if (flags & RPMINST_FORCE)
1966 opts.push_back ("--force");
1967 if (flags & RPMINST_NODEPS)
1968 opts.push_back ("--nodeps");
1969 if (flags & RPMINST_IGNORESIZE)
1970 opts.push_back ("--ignoresize");
1971 if (flags & RPMINST_JUSTDB)
1972 opts.push_back ("--justdb");
1973 if (flags & RPMINST_TEST)
1974 opts.push_back ("--test");
1975 if (flags & RPMINST_NOPOSTTRANS)
1976 opts.push_back ("--noposttrans");
1978 opts.push_back("--");
1980 // rpm requires additional quoting of special chars:
1981 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1982 opts.push_back ( quotedFilename.c_str() );
1984 modifyDatabase(); // BEFORE run_rpm
1985 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1989 std::vector<std::string> configwarnings;
1991 unsigned linecnt = 0;
1992 while (systemReadLine(line))
1994 if ( linecnt < MAXRPMMESSAGELINES )
1999 if (line.substr(0,2)=="%%")
2002 sscanf (line.c_str () + 2, "%d", &percent);
2003 report->progress( percent );
2006 rpmmsg += line+'\n';
2008 if ( line.substr(0,8) == "warning:" )
2010 configwarnings.push_back(line);
2013 if ( linecnt > MAXRPMMESSAGELINES )
2014 rpmmsg += "[truncated]\n";
2016 int rpm_status = systemStatus();
2019 for (std::vector<std::string>::iterator it = configwarnings.begin();
2020 it != configwarnings.end(); ++it)
2022 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2024 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2026 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2027 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2029 _("rpm created %s as %s, but it was impossible to determine the difference"),
2031 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2034 if ( rpm_status != 0 )
2037 str::form("%s install failed", Pathname::basename(filename).c_str()),
2038 true /*timestamp*/);
2039 std::ostringstream sstr;
2040 sstr << "rpm output:" << endl << rpmmsg << endl;
2041 historylog.comment(sstr.str());
2042 // TranslatorExplanation the colon is followed by an error message
2043 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
2045 else if ( ! rpmmsg.empty() )
2048 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2049 true /*timestamp*/);
2050 std::ostringstream sstr;
2051 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2052 historylog.comment(sstr.str());
2054 // report additional rpm output in finish
2055 // TranslatorExplanation Text is followed by a ':' and the actual output.
2056 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2060 ///////////////////////////////////////////////////////////////////
2063 // METHOD NAME : RpmDb::removePackage
2064 // METHOD TYPE : PMError
2066 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2068 // 'rpm -e' does not like epochs
2069 return removePackage( package->name()
2070 + "-" + package->edition().version()
2071 + "-" + package->edition().release()
2072 + "." + package->arch().asString(), flags );
2075 ///////////////////////////////////////////////////////////////////
2078 // METHOD NAME : RpmDb::removePackage
2079 // METHOD TYPE : PMError
2081 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
2083 callback::SendReport<RpmRemoveReport> report;
2085 report->start( name_r );
2090 doRemovePackage(name_r, flags, report);
2094 catch (RpmException & excpt_r)
2096 RpmRemoveReport::Action user = report->problem( excpt_r );
2098 if ( user == RpmRemoveReport::ABORT )
2100 report->finish( excpt_r );
2101 ZYPP_RETHROW(excpt_r);
2103 else if ( user == RpmRemoveReport::IGNORE )
2112 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2114 FAILIFNOTINITIALIZED;
2115 HistoryLog historylog;
2117 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2120 if ( _packagebackups )
2122 // FIXME solve this status report somehow
2123 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2124 if ( ! backupPackage( name_r ) )
2126 ERR << "backup of " << name_r << " failed" << endl;
2128 report->progress( 0 );
2132 report->progress( 100 );
2137 opts.push_back("-e");
2138 opts.push_back("--allmatches");
2140 if (flags & RPMINST_NOSCRIPTS)
2141 opts.push_back("--noscripts");
2142 if (flags & RPMINST_NODEPS)
2143 opts.push_back("--nodeps");
2144 if (flags & RPMINST_JUSTDB)
2145 opts.push_back("--justdb");
2146 if (flags & RPMINST_TEST)
2147 opts.push_back ("--test");
2148 if (flags & RPMINST_FORCE)
2150 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2153 opts.push_back("--");
2154 opts.push_back(name_r.c_str());
2156 modifyDatabase(); // BEFORE run_rpm
2157 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2162 // got no progress from command, so we fake it:
2163 // 5 - command started
2164 // 50 - command completed
2166 report->progress( 5 );
2167 unsigned linecnt = 0;
2168 while (systemReadLine(line))
2170 if ( linecnt < MAXRPMMESSAGELINES )
2174 rpmmsg += line+'\n';
2176 if ( linecnt > MAXRPMMESSAGELINES )
2177 rpmmsg += "[truncated]\n";
2178 report->progress( 50 );
2179 int rpm_status = systemStatus();
2181 if ( rpm_status != 0 )
2184 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2185 std::ostringstream sstr;
2186 sstr << "rpm output:" << endl << rpmmsg << endl;
2187 historylog.comment(sstr.str());
2188 // TranslatorExplanation the colon is followed by an error message
2189 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
2191 else if ( ! rpmmsg.empty() )
2194 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2196 std::ostringstream sstr;
2197 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2198 historylog.comment(sstr.str());
2200 // report additional rpm output in finish
2201 // TranslatorExplanation Text is followed by a ':' and the actual output.
2202 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2206 ///////////////////////////////////////////////////////////////////
2209 // METHOD NAME : RpmDb::backupPackage
2210 // METHOD TYPE : bool
2212 bool RpmDb::backupPackage( const Pathname & filename )
2214 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2218 return backupPackage( h->tag_name() );
2221 ///////////////////////////////////////////////////////////////////
2224 // METHOD NAME : RpmDb::backupPackage
2225 // METHOD TYPE : bool
2227 bool RpmDb::backupPackage(const std::string& packageName)
2229 HistoryLog progresslog;
2231 Pathname backupFilename;
2232 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2234 if (_backuppath.empty())
2236 INT << "_backuppath empty" << endl;
2242 if (!queryChangedFiles(fileList, packageName))
2244 ERR << "Error while getting changed files for package " <<
2245 packageName << endl;
2249 if (fileList.size() <= 0)
2251 DBG << "package " << packageName << " not changed -> no backup" << endl;
2255 if (filesystem::assert_dir(_root + _backuppath) != 0)
2261 // build up archive name
2262 time_t currentTime = time(0);
2263 struct tm *currentLocalTime = localtime(¤tTime);
2265 int date = (currentLocalTime->tm_year + 1900) * 10000
2266 + (currentLocalTime->tm_mon + 1) * 100
2267 + currentLocalTime->tm_mday;
2272 backupFilename = _root + _backuppath
2273 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2276 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2278 PathInfo pi(filestobackupfile);
2279 if (pi.isExist() && !pi.isFile())
2281 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2285 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2289 ERR << "could not open " << filestobackupfile.asString() << endl;
2293 for (FileList::const_iterator cit = fileList.begin();
2294 cit != fileList.end(); ++cit)
2296 std::string name = *cit;
2297 if ( name[0] == '/' )
2299 // remove slash, file must be relative to -C parameter of tar
2300 name = name.substr( 1 );
2302 DBG << "saving file "<< name << endl;
2307 const char* const argv[] =
2312 _root.asString().c_str(),
2313 "--ignore-failed-read",
2315 backupFilename.asString().c_str(),
2317 filestobackupfile.asString().c_str(),
2321 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2322 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2326 // TODO: its probably possible to start tar with -v and watch it adding
2327 // files to report progress
2328 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2333 int ret = tar.close();
2337 ERR << "tar failed: " << tarmsg << endl;
2342 MIL << "tar backup ok" << endl;
2343 progresslog.comment(
2344 str::form(_("created backup %s"), backupFilename.asString().c_str())
2345 , /*timestamp*/true);
2348 filesystem::unlink(filestobackupfile);
2354 void RpmDb::setBackupPath(const Pathname& path)
2359 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2363 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2364 // translators: possible rpm package signature check result [brief]
2365 OUTS( CHK_OK, _("Signature is OK") );
2366 // translators: possible rpm package signature check result [brief]
2367 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2368 // translators: possible rpm package signature check result [brief]
2369 OUTS( CHK_FAIL, _("Signature does not verify") );
2370 // translators: possible rpm package signature check result [brief]
2371 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2372 // translators: possible rpm package signature check result [brief]
2373 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2374 // translators: possible rpm package signature check result [brief]
2375 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2378 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2381 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2383 for ( const auto & el : obj )
2384 str << el.second << endl;
2389 } // namespace target