1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
28 #include <boost/format.hpp>
30 #include "zypp/base/Logger.h"
31 #include "zypp/base/String.h"
32 #include "zypp/base/Gettext.h"
34 #include "zypp/Date.h"
35 #include "zypp/Pathname.h"
36 #include "zypp/PathInfo.h"
37 #include "zypp/PublicKey.h"
39 #include "zypp/target/rpm/RpmDb.h"
40 #include "zypp/target/rpm/RpmCallbacks.h"
42 #include "zypp/HistoryLog.h"
43 #include "zypp/target/rpm/librpmDb.h"
44 #include "zypp/target/rpm/RpmException.h"
45 #include "zypp/TmpPath.h"
46 #include "zypp/KeyRing.h"
47 #include "zypp/ZYppFactory.h"
48 #include "zypp/ZConfig.h"
51 using namespace zypp::filesystem;
53 #define WARNINGMAILPATH "/var/log/YaST2/"
54 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
55 #define MAXRPMMESSAGELINES 10000
57 #define WORKAROUNDRPMPWDBUG
61 namespace zypp_readonly_hack
63 bool IGotIt(); // in readonly-mode
71 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
72 const char* quoteInFilename_m = "\'\"";
74 const char* quoteInFilename_m = " \t\'\"";
76 inline string rpmQuoteFilename( const Pathname & path_r )
78 string path( path_r.asString() );
79 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
81 pos = path.find_first_of( quoteInFilename_m, pos ) )
83 path.insert( pos, "\\" );
84 pos += 2; // skip '\\' and the quoted char.
90 /** Workaround bnc#827609 - rpm needs a readable pwd so we
91 * chdir to /. Turn realtive pathnames into absolute ones
92 * by prepending cwd so rpm still finds them
94 inline Pathname workaroundRpmPwdBug( Pathname path_r )
96 #if defined(WORKAROUNDRPMPWDBUG)
97 if ( path_r.relative() )
100 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
102 return Pathname( cwd ) / path_r;
103 WAR << "Can't get cwd!" << endl;
106 return path_r; // no problem with absolute pathnames
110 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
112 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
117 ~KeyRingSignalReceiver()
122 virtual void trustedKeyAdded( const PublicKey &key )
124 MIL << "trusted key added to zypp Keyring. Importing" << endl;
125 // now import the key in rpm
128 _rpmdb.importPubkey( key );
130 catch (RpmException &e)
132 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
136 virtual void trustedKeyRemoved( const PublicKey &key )
138 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
140 // remove the key from rpm
143 _rpmdb.removePubkey( key );
145 catch (RpmException &e)
147 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
154 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
156 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
166 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
173 for (line = prog.receiveLine(), count=0;
175 line = prog.receiveLine(), count++ )
177 if (maxlines<0?true:count<maxlines)
186 /******************************************************************
189 ** FUNCTION NAME : stringPath
190 ** FUNCTION TYPE : inline string
192 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
194 return librpmDb::stringPath( root_r, sub_r );
197 /******************************************************************
200 ** FUNCTION NAME : operator<<
201 ** FUNCTION TYPE : ostream &
203 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
205 if ( obj == RpmDb::DbSI_NO_INIT )
211 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
213 ENUM_OUT( DbSI_HAVE_V4, 'X' );
214 ENUM_OUT( DbSI_MADE_V4, 'c' );
215 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
217 ENUM_OUT( DbSI_HAVE_V3, 'X' );
218 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
219 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
228 ///////////////////////////////////////////////////////////////////
230 // CLASS NAME : RpmDb
232 ///////////////////////////////////////////////////////////////////
234 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
236 ///////////////////////////////////////////////////////////////////
238 ///////////////////////////////////////////////////////////////////
241 // METHOD NAME : RpmDb::RpmDb
242 // METHOD TYPE : Constructor
245 : _dbStateInfo( DbSI_NO_INIT )
246 #warning Check for obsolete memebers
247 , _backuppath ("/var/adm/backup")
248 , _packagebackups(false)
249 , _warndirexists(false)
253 librpmDb::globalInit();
254 // Some rpm versions are patched not to abort installation if
255 // symlink creation failed.
256 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
257 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
260 ///////////////////////////////////////////////////////////////////
263 // METHOD NAME : RpmDb::~RpmDb
264 // METHOD TYPE : Destructor
268 MIL << "~RpmDb()" << endl;
271 MIL << "~RpmDb() end" << endl;
272 sKeyRingReceiver.reset();
275 Date RpmDb::timestamp() const
280 if ( dbPath().empty() )
281 db_path = "/var/lib/rpm";
285 PathInfo rpmdb_info(root() + db_path + "/Packages");
287 if ( rpmdb_info.isExist() )
288 return rpmdb_info.mtime();
292 ///////////////////////////////////////////////////////////////////
295 // METHOD NAME : RpmDb::dumpOn
296 // METHOD TYPE : ostream &
298 ostream & RpmDb::dumpOn( ostream & str ) const
302 if ( _dbStateInfo == DbSI_NO_INIT )
308 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
310 ENUM_OUT( DbSI_HAVE_V4, 'X' );
311 ENUM_OUT( DbSI_MADE_V4, 'c' );
312 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
314 ENUM_OUT( DbSI_HAVE_V3, 'X' );
315 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
316 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
317 str << "): " << stringPath( _root, _dbPath );
323 ///////////////////////////////////////////////////////////////////
326 // METHOD NAME : RpmDb::initDatabase
327 // METHOD TYPE : PMError
329 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
331 ///////////////////////////////////////////////////////////////////
333 ///////////////////////////////////////////////////////////////////
334 bool quickinit( root_r.empty() );
336 if ( root_r.empty() )
339 if ( dbPath_r.empty() )
340 dbPath_r = "/var/lib/rpm";
342 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
344 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
345 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
348 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
349 << ( doRebuild_r ? " (rebuilddb)" : "" )
350 << ( quickinit ? " (quickinit)" : "" ) << endl;
352 ///////////////////////////////////////////////////////////////////
353 // Check whether already initialized
354 ///////////////////////////////////////////////////////////////////
357 if ( root_r == _root && dbPath_r == _dbPath )
363 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
367 ///////////////////////////////////////////////////////////////////
369 ///////////////////////////////////////////////////////////////////
370 librpmDb::unblockAccess();
374 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
378 DbStateInfoBits info = DbSI_NO_INIT;
381 internal_initDatabase( root_r, dbPath_r, info );
383 catch (const RpmException & excpt_r)
385 ZYPP_CAUGHT(excpt_r);
386 librpmDb::blockAccess();
387 ERR << "Cleanup on error: state " << info << endl;
389 if ( dbsi_has( info, DbSI_MADE_V4 ) )
391 // remove the newly created rpm4 database and
392 // any backup created on conversion.
393 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
395 ZYPP_RETHROW(excpt_r);
397 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
399 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
401 // Move obsolete rpm3 database beside.
402 MIL << "Cleanup: state " << info << endl;
403 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
404 dbsi_clr( info, DbSI_HAVE_V3 );
408 // Performing an update: Keep the original rpm3 database
409 // and wait if the rpm4 database gets modified by installing
410 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
411 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
414 #warning CHECK: notify root about conversion backup.
422 if ( dbsi_has( info, DbSI_HAVE_V4 )
423 && ! dbsi_has( info, DbSI_MADE_V4 ) )
429 MIL << "Synchronizing keys with zypp keyring" << endl;
432 // Close the database in case any write acces (create/convert)
433 // happened during init. This should drop any lock acquired
434 // by librpm. On demand it will be reopened readonly and should
435 // not hold any lock.
436 librpmDb::dbRelease( true );
438 MIL << "InitDatabase: " << *this << endl;
441 ///////////////////////////////////////////////////////////////////
444 // METHOD NAME : RpmDb::internal_initDatabase
445 // METHOD TYPE : PMError
447 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
448 DbStateInfoBits & info_r )
450 info_r = DbSI_NO_INIT;
452 ///////////////////////////////////////////////////////////////////
453 // Get info about the desired database dir
454 ///////////////////////////////////////////////////////////////////
455 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
457 if ( dbInfo.illegalArgs() )
459 // should not happen (checked in initDatabase)
460 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
462 if ( ! dbInfo.usableArgs() )
464 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
465 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
468 if ( dbInfo.hasDbV4() )
470 dbsi_set( info_r, DbSI_HAVE_V4 );
471 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
475 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
478 if ( dbInfo.hasDbV3() )
480 dbsi_set( info_r, DbSI_HAVE_V3 );
482 if ( dbInfo.hasDbV3ToV4() )
484 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
487 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
488 librpmDb::dumpState( DBG ) << endl;
490 ///////////////////////////////////////////////////////////////////
491 // Access database, create if needed
492 ///////////////////////////////////////////////////////////////////
494 // creates dbdir and empty rpm4 database if not present
495 librpmDb::dbAccess( root_r, dbPath_r );
497 if ( ! dbInfo.hasDbV4() )
500 if ( dbInfo.hasDbV4() )
502 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
506 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
507 librpmDb::dumpState( DBG ) << endl;
509 ///////////////////////////////////////////////////////////////////
510 // Check whether to convert something. Create backup but do
511 // not remove anything here
512 ///////////////////////////////////////////////////////////////////
513 librpmDb::constPtr dbptr;
514 librpmDb::dbAccess( dbptr );
515 bool dbEmpty = dbptr->empty();
518 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
521 if ( dbInfo.hasDbV3() )
523 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
527 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
528 convertV3toV4( dbInfo.dbV3().path(), dbptr );
530 // create a backup copy
531 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
534 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
539 if ( dbInfo.hasDbV3ToV4() )
541 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
542 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
550 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
551 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
552 dbsi_set( info_r, DbSI_MODIFIED_V4 );
556 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
557 librpmDb::dumpState( DBG ) << endl;
560 if ( dbInfo.hasDbV3ToV4() )
562 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
566 ///////////////////////////////////////////////////////////////////
569 // METHOD NAME : RpmDb::removeV4
570 // METHOD TYPE : void
572 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
574 const char * v3backup = "packages.rpm3";
575 const char * master = "Packages";
576 const char * index[] =
598 PathInfo pi( dbdir_r );
601 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
605 for ( const char ** f = index; *f; ++f )
610 filesystem::unlink( pi.path() );
614 pi( dbdir_r + master );
617 MIL << "Removing rpm4 database " << pi << endl;
618 filesystem::unlink( pi.path() );
623 pi( dbdir_r + v3backup );
626 MIL << "Removing converted rpm3 database backup " << pi << endl;
627 filesystem::unlink( pi.path() );
632 ///////////////////////////////////////////////////////////////////
635 // METHOD NAME : RpmDb::removeV3
636 // METHOD TYPE : void
638 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
640 const char * master = "packages.rpm";
641 const char * index[] =
643 "conflictsindex.rpm",
654 PathInfo pi( dbdir_r );
657 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
661 for ( const char ** f = index; *f; ++f )
666 filesystem::unlink( pi.path() );
670 #warning CHECK: compare vs existing v3 backup. notify root
671 pi( dbdir_r + master );
674 Pathname m( pi.path() );
677 // backup was already created
678 filesystem::unlink( m );
679 Pathname b( m.extend( "3" ) );
680 pi( b ); // stat backup
684 Pathname b( m.extend( ".deleted" ) );
688 // rempve existing backup
689 filesystem::unlink( b );
691 filesystem::rename( m, b );
692 pi( b ); // stat backup
694 MIL << "(Re)moved rpm3 database to " << pi << endl;
698 ///////////////////////////////////////////////////////////////////
701 // METHOD NAME : RpmDb::modifyDatabase
702 // METHOD TYPE : void
704 void RpmDb::modifyDatabase()
706 if ( ! initialized() )
709 // tag database as modified
710 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
712 // Move outdated rpm3 database beside.
713 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
715 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
716 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
717 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
721 ///////////////////////////////////////////////////////////////////
724 // METHOD NAME : RpmDb::closeDatabase
725 // METHOD TYPE : PMError
727 void RpmDb::closeDatabase()
729 if ( ! initialized() )
734 MIL << "Calling closeDatabase: " << *this << endl;
736 ///////////////////////////////////////////////////////////////////
737 // Block further database access
738 ///////////////////////////////////////////////////////////////////
739 librpmDb::blockAccess();
741 ///////////////////////////////////////////////////////////////////
742 // Check fate if old version database still present
743 ///////////////////////////////////////////////////////////////////
744 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
746 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
747 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
749 // Move outdated rpm3 database beside.
750 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
754 // Remove unmodified rpm4 database
755 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
759 ///////////////////////////////////////////////////////////////////
761 ///////////////////////////////////////////////////////////////////
762 _root = _dbPath = Pathname();
763 _dbStateInfo = DbSI_NO_INIT;
765 MIL << "closeDatabase: " << *this << endl;
768 ///////////////////////////////////////////////////////////////////
771 // METHOD NAME : RpmDb::rebuildDatabase
772 // METHOD TYPE : PMError
774 void RpmDb::rebuildDatabase()
776 callback::SendReport<RebuildDBReport> report;
778 report->start( root() + dbPath() );
782 doRebuildDatabase(report);
784 catch (RpmException & excpt_r)
786 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
787 ZYPP_RETHROW(excpt_r);
789 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
792 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
794 FAILIFNOTINITIALIZED;
796 MIL << "RpmDb::rebuildDatabase" << *this << endl;
797 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
799 PathInfo dbMaster( root() + dbPath() + "Packages" );
800 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
804 opts.push_back("--rebuilddb");
805 opts.push_back("-vv");
807 // don't call modifyDatabase because it would remove the old
808 // rpm3 database, if the current database is a temporary one.
809 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
811 // progress report: watch this file growing
812 PathInfo newMaster( root()
813 + dbPath().extend( str::form( "rebuilddb.%d",
814 process?process->getpid():0) )
820 while ( systemReadLine( line ) )
823 { // file is removed at the end of rebuild.
824 // current size should be upper limit for new db
825 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
827 WAR << "User requested abort." << endl;
829 filesystem::recursive_rmdir( newMaster.path().dirname() );
833 if ( line.compare( 0, 2, "D:" ) )
835 errmsg += line + '\n';
836 // report.notify( line );
841 int rpm_status = systemStatus();
843 if ( rpm_status != 0 )
845 //TranslatorExplanation after semicolon is error message
846 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
847 (errmsg.empty() ? error_message: errmsg))));
851 report->progress( 100, root() + dbPath() ); // 100%
855 ///////////////////////////////////////////////////////////////////
858 /** \ref RpmDb::syncTrustedKeys helper
859 * Compute which keys need to be exprted to / imported from the zypp keyring.
860 * Return result via argument list.
862 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
864 ///////////////////////////////////////////////////////////////////
865 // Remember latest release and where it ocurred
869 : _inRpmKeys( nullptr )
870 , _inZyppKeys( nullptr )
873 void updateIf( const Edition & rpmKey_r )
875 std::string keyRelease( rpmKey_r.release() );
876 int comp = _release.compare( keyRelease );
879 // update to newer release
880 _release.swap( keyRelease );
881 _inRpmKeys = &rpmKey_r;
882 _inZyppKeys = nullptr;
883 if ( !keyRelease.empty() )
884 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
886 else if ( comp == 0 )
888 // stay with this release
890 _inRpmKeys = &rpmKey_r;
892 // else: this is an old release
894 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
897 void updateIf( const PublicKeyData & zyppKey_r )
899 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
900 int comp = _release.compare( keyRelease );
903 // update to newer release
904 _release.swap( keyRelease );
905 _inRpmKeys = nullptr;
906 _inZyppKeys = &zyppKey_r;
907 if ( !keyRelease.empty() )
908 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
910 else if ( comp == 0 )
912 // stay with this release
914 _inZyppKeys = &zyppKey_r;
916 // else: this is an old release
918 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
921 std::string _release;
922 const Edition * _inRpmKeys;
923 const PublicKeyData * _inZyppKeys;
925 ///////////////////////////////////////////////////////////////////
927 // collect keys by ID(version) and latest creation(release)
928 std::map<std::string,Key> _keymap;
930 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
932 _keymap[(*it).version()].updateIf( *it );
935 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
937 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
940 // compute missing keys
941 std::set<Edition> rpmKeys;
942 std::list<PublicKeyData> zyppKeys;
943 for_( it, _keymap.begin(), _keymap.end() )
945 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
946 << ( (*it).second._inRpmKeys ? "R" : "_" )
947 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
948 if ( ! (*it).second._inRpmKeys )
950 zyppKeys.push_back( *(*it).second._inZyppKeys );
952 if ( ! (*it).second._inZyppKeys )
954 rpmKeys.insert( *(*it).second._inRpmKeys );
957 rpmKeys_r.swap( rpmKeys );
958 zyppKeys_r.swap( zyppKeys );
961 ///////////////////////////////////////////////////////////////////
963 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
965 MIL << "Going to sync trusted keys..." << endl;
966 std::set<Edition> rpmKeys( pubkeyEditions() );
967 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
968 computeKeyRingSync( rpmKeys, zyppKeys );
969 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
970 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
972 ///////////////////////////////////////////////////////////////////
973 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
975 // export to zypp keyring
976 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
977 // Temporarily disconnect to prevent the attemt to re-import the exported keys.
978 callback::TempConnect<KeyRingSignals> tempDisconnect;
979 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
981 TmpFile tmpfile( getZYpp()->tmpPath() );
983 ofstream tmpos( tmpfile.path().c_str() );
984 for_( it, rpmKeys.begin(), rpmKeys.end() )
986 // we export the rpm key into a file
987 RpmHeader::constPtr result;
988 getData( string("gpg-pubkey"), *it, result );
989 tmpos << result->tag_description() << endl;
994 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
998 ERR << "Could not import keys into in zypp keyring" << endl;
1002 ///////////////////////////////////////////////////////////////////
1003 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1005 // import from zypp keyring
1006 MIL << "Importing zypp trusted keyring" << std::endl;
1007 for_( it, zyppKeys.begin(), zyppKeys.end() )
1011 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1013 catch ( const RpmException & exp )
1019 MIL << "Trusted keys synced." << endl;
1022 void RpmDb::importZyppKeyRingTrustedKeys()
1023 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1025 void RpmDb::exportTrustedKeysInZyppKeyRing()
1026 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1028 ///////////////////////////////////////////////////////////////////
1031 // METHOD NAME : RpmDb::importPubkey
1032 // METHOD TYPE : PMError
1034 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1036 FAILIFNOTINITIALIZED;
1038 // bnc#828672: On the fly key import in READONLY
1039 if ( zypp_readonly_hack::IGotIt() )
1041 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1045 // check if the key is already in the rpm database
1046 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1047 set<Edition> rpmKeys = pubkeyEditions();
1048 bool hasOldkeys = false;
1050 for_( it, rpmKeys.begin(), rpmKeys.end() )
1052 if ( keyEd == *it ) // quick test (Edition is IdStringType!)
1054 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1058 if ( keyEd.version() != (*it).version() )
1059 continue; // different key ID (version)
1061 if ( keyEd.release() < (*it).release() )
1063 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1071 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1075 // We must explicitly delete old key IDs first (all releases,
1076 // that's why we don't call removePubkey here).
1077 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1079 opts.push_back ( "-e" );
1080 opts.push_back ( "--allmatches" );
1081 opts.push_back ( "--" );
1082 opts.push_back ( keyName.c_str() );
1083 // don't call modifyDatabase because it would remove the old
1084 // rpm3 database, if the current database is a temporary one.
1085 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1088 while ( systemReadLine( line ) )
1090 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1093 if ( systemStatus() != 0 )
1095 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1099 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1103 // import the new key
1105 opts.push_back ( "--import" );
1106 opts.push_back ( "--" );
1107 opts.push_back ( pubkey_r.path().asString().c_str() );
1109 // don't call modifyDatabase because it would remove the old
1110 // rpm3 database, if the current database is a temporary one.
1111 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1114 while ( systemReadLine( line ) )
1116 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1119 if ( systemStatus() != 0 )
1121 //TranslatorExplanation first %s is file name, second is error message
1122 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1123 _("Failed to import public key from file %s: %s"))
1124 % pubkey_r.asString() % error_message)));
1128 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1132 ///////////////////////////////////////////////////////////////////
1135 // METHOD NAME : RpmDb::removePubkey
1136 // METHOD TYPE : PMError
1138 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1140 FAILIFNOTINITIALIZED;
1142 // check if the key is in the rpm database and just
1143 // return if it does not.
1144 set<Edition> rpm_keys = pubkeyEditions();
1145 set<Edition>::const_iterator found_edition = rpm_keys.end();
1146 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1148 for_( it, rpm_keys.begin(), rpm_keys.end() )
1150 if ( (*it).version() == pubkeyVersion )
1157 // the key does not exist, cannot be removed
1158 if (found_edition == rpm_keys.end())
1160 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1164 string rpm_name("gpg-pubkey-" + found_edition->asString());
1167 opts.push_back ( "-e" );
1168 opts.push_back ( "--" );
1169 opts.push_back ( rpm_name.c_str() );
1171 // don't call modifyDatabase because it would remove the old
1172 // rpm3 database, if the current database is a temporary one.
1173 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1176 while ( systemReadLine( line ) )
1178 if ( line.substr( 0, 6 ) == "error:" )
1180 WAR << line << endl;
1184 DBG << line << endl;
1188 int rpm_status = systemStatus();
1190 if ( rpm_status != 0 )
1192 //TranslatorExplanation first %s is key name, second is error message
1193 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1194 _("Failed to remove public key %s: %s")) % pubkey_r.asString()
1199 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1203 ///////////////////////////////////////////////////////////////////
1206 // METHOD NAME : RpmDb::pubkeys
1207 // METHOD TYPE : set<Edition>
1209 list<PublicKey> RpmDb::pubkeys() const
1211 list<PublicKey> ret;
1213 librpmDb::db_const_iterator it;
1214 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1216 Edition edition = it->tag_edition();
1217 if (edition != Edition::noedition)
1219 // we export the rpm key into a file
1220 RpmHeader::constPtr result;
1221 getData( string("gpg-pubkey"), edition, result );
1222 TmpFile file(getZYpp()->tmpPath());
1226 os.open(file.path().asString().c_str());
1227 // dump rpm key into the tmp file
1228 os << result->tag_description();
1229 //MIL << "-----------------------------------------------" << endl;
1230 //MIL << result->tag_description() <<endl;
1231 //MIL << "-----------------------------------------------" << endl;
1233 // read the public key from the dumped file
1234 PublicKey key(file);
1237 catch (exception &e)
1239 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1240 // just ignore the key
1247 set<Edition> RpmDb::pubkeyEditions() const
1251 librpmDb::db_const_iterator it;
1252 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1254 Edition edition = it->tag_edition();
1255 if (edition != Edition::noedition)
1256 ret.insert( edition );
1262 ///////////////////////////////////////////////////////////////////
1265 // METHOD NAME : RpmDb::fileList
1266 // METHOD TYPE : bool
1271 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1273 list<FileInfo> result;
1275 librpmDb::db_const_iterator it;
1277 if (edition_r == Edition::noedition)
1279 found = it.findPackage( name_r );
1283 found = it.findPackage( name_r, edition_r );
1292 ///////////////////////////////////////////////////////////////////
1295 // METHOD NAME : RpmDb::hasFile
1296 // METHOD TYPE : bool
1300 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1302 librpmDb::db_const_iterator it;
1306 res = it.findByFile( file_r );
1308 if (!name_r.empty())
1310 res = (it->tag_name() == name_r);
1318 ///////////////////////////////////////////////////////////////////
1321 // METHOD NAME : RpmDb::whoOwnsFile
1322 // METHOD TYPE : string
1326 string RpmDb::whoOwnsFile( const string & file_r) const
1328 librpmDb::db_const_iterator it;
1329 if (it.findByFile( file_r ))
1331 return it->tag_name();
1336 ///////////////////////////////////////////////////////////////////
1339 // METHOD NAME : RpmDb::hasProvides
1340 // METHOD TYPE : bool
1344 bool RpmDb::hasProvides( const string & tag_r ) const
1346 librpmDb::db_const_iterator it;
1347 return it.findByProvides( tag_r );
1350 ///////////////////////////////////////////////////////////////////
1353 // METHOD NAME : RpmDb::hasRequiredBy
1354 // METHOD TYPE : bool
1358 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1360 librpmDb::db_const_iterator it;
1361 return it.findByRequiredBy( tag_r );
1364 ///////////////////////////////////////////////////////////////////
1367 // METHOD NAME : RpmDb::hasConflicts
1368 // METHOD TYPE : bool
1372 bool RpmDb::hasConflicts( const string & tag_r ) const
1374 librpmDb::db_const_iterator it;
1375 return it.findByConflicts( tag_r );
1378 ///////////////////////////////////////////////////////////////////
1381 // METHOD NAME : RpmDb::hasPackage
1382 // METHOD TYPE : bool
1386 bool RpmDb::hasPackage( const string & name_r ) const
1388 librpmDb::db_const_iterator it;
1389 return it.findPackage( name_r );
1392 ///////////////////////////////////////////////////////////////////
1395 // METHOD NAME : RpmDb::hasPackage
1396 // METHOD TYPE : bool
1400 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1402 librpmDb::db_const_iterator it;
1403 return it.findPackage( name_r, ed_r );
1406 ///////////////////////////////////////////////////////////////////
1409 // METHOD NAME : RpmDb::getData
1410 // METHOD TYPE : PMError
1414 void RpmDb::getData( const string & name_r,
1415 RpmHeader::constPtr & result_r ) const
1417 librpmDb::db_const_iterator it;
1418 it.findPackage( name_r );
1421 ZYPP_THROW(*(it.dbError()));
1424 ///////////////////////////////////////////////////////////////////
1427 // METHOD NAME : RpmDb::getData
1428 // METHOD TYPE : void
1432 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1433 RpmHeader::constPtr & result_r ) const
1435 librpmDb::db_const_iterator it;
1436 it.findPackage( name_r, ed_r );
1439 ZYPP_THROW(*(it.dbError()));
1442 ///////////////////////////////////////////////////////////////////
1444 // METHOD NAME : RpmDb::checkPackage
1445 // METHOD TYPE : RpmDb::checkPackageResult
1447 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
1449 PathInfo file( path_r );
1450 if ( ! file.isFile() )
1452 ERR << "Not a file: " << file << endl;
1456 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1457 if ( fd == 0 || ::Ferror(fd) )
1459 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1465 rpmts ts = ::rpmtsCreate();
1466 ::rpmtsSetRootDir( ts, root().asString().c_str() );
1467 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1468 int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
1478 case RPMRC_NOTFOUND:
1479 WAR << "Signature is unknown type. " << file << endl;
1480 return CHK_NOTFOUND;
1483 WAR << "Signature does not verify. " << file << endl;
1486 case RPMRC_NOTTRUSTED:
1487 WAR << "Signature is OK, but key is not trusted. " << file << endl;
1488 return CHK_NOTTRUSTED;
1491 WAR << "Public key is unavailable. " << file << endl;
1495 ERR << "Error reading header." << file << endl;
1499 // determine changed files of installed package
1501 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1507 if ( ! initialized() ) return false;
1511 opts.push_back ("-V");
1512 opts.push_back ("--nodeps");
1513 opts.push_back ("--noscripts");
1514 opts.push_back ("--nomd5");
1515 opts.push_back ("--");
1516 opts.push_back (packageName.c_str());
1518 run_rpm (opts, ExternalProgram::Discard_Stderr);
1520 if ( process == NULL )
1531 M Mode (includes permissions and file type)
1535 while (systemReadLine(line))
1537 if (line.length() > 12 &&
1538 (line[0] == 'S' || line[0] == 's' ||
1539 (line[0] == '.' && line[7] == 'T')))
1541 // file has been changed
1544 filename.assign(line, 11, line.length() - 11);
1545 fileList.insert(filename);
1550 // exit code ignored, rpm returns 1 no matter if package is installed or
1558 /****************************************************************/
1559 /* private member-functions */
1560 /****************************************************************/
1562 /*--------------------------------------------------------------*/
1563 /* Run rpm with the specified arguments, handling stderr */
1564 /* as specified by disp */
1565 /*--------------------------------------------------------------*/
1567 RpmDb::run_rpm (const RpmArgVec& opts,
1568 ExternalProgram::Stderr_Disposition disp)
1577 if ( ! initialized() )
1579 ZYPP_THROW(RpmDbNotOpenException());
1584 // always set root and dbpath
1585 #if defined(WORKAROUNDRPMPWDBUG)
1586 args.push_back("#/"); // chdir to / to workaround bnc#819354
1588 args.push_back("rpm");
1589 args.push_back("--root");
1590 args.push_back(_root.asString().c_str());
1591 args.push_back("--dbpath");
1592 args.push_back(_dbPath.asString().c_str());
1594 const char* argv[args.size() + opts.size() + 1];
1596 const char** p = argv;
1597 p = copy (args.begin (), args.end (), p);
1598 p = copy (opts.begin (), opts.end (), p);
1601 // Invalidate all outstanding database handles in case
1602 // the database gets modified.
1603 librpmDb::dbRelease( true );
1605 // Launch the program with default locale
1606 process = new ExternalProgram(argv, disp, false, -1, true);
1610 /*--------------------------------------------------------------*/
1611 /* Read a line from the rpm process */
1612 /*--------------------------------------------------------------*/
1613 bool RpmDb::systemReadLine( string & line )
1617 if ( process == NULL )
1620 if ( process->inputFile() )
1622 process->setBlocking( false );
1623 FILE * inputfile = process->inputFile();
1624 int inputfileFd = ::fileno( inputfile );
1627 /* Watch inputFile to see when it has input. */
1630 FD_SET( inputfileFd, &rfds );
1632 /* Wait up to 5 seconds. */
1637 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1641 ERR << "select error: " << strerror(errno) << endl;
1642 if ( errno != EINTR )
1647 // Data is available now.
1648 static size_t linebuffer_size = 0; // static because getline allocs
1649 static char * linebuffer = 0; // and reallocs if buffer is too small
1650 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1653 if ( ::feof( inputfile ) )
1654 return line.size(); // in case of pending output
1660 if ( linebuffer[nread-1] == '\n' )
1662 line += string( linebuffer, nread );
1665 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1666 return true; // complete line
1668 clearerr( inputfile );
1672 // No data within time.
1673 if ( ! process->running() )
1682 /*--------------------------------------------------------------*/
1683 /* Return the exit status of the rpm process, closing the */
1684 /* connection if not already done */
1685 /*--------------------------------------------------------------*/
1687 RpmDb::systemStatus()
1689 if ( process == NULL )
1692 exit_code = process->close();
1696 error_message = process->execError();
1701 // DBG << "exit code " << exit_code << endl;
1706 /*--------------------------------------------------------------*/
1707 /* Forcably kill the rpm process */
1708 /*--------------------------------------------------------------*/
1712 if (process) process->kill();
1716 // generate diff mails for config files
1717 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1719 string msg = line.substr(9);
1720 string::size_type pos1 = string::npos;
1721 string::size_type pos2 = string::npos;
1722 string file1s, file2s;
1726 pos1 = msg.find (typemsg);
1729 if ( pos1 == string::npos )
1732 pos2 = pos1 + strlen (typemsg);
1734 if (pos2 >= msg.length() )
1737 file1 = msg.substr (0, pos1);
1738 file2 = msg.substr (pos2);
1740 file1s = file1.asString();
1741 file2s = file2.asString();
1743 if (!_root.empty() && _root != "/")
1745 file1 = _root + file1;
1746 file2 = _root + file2;
1750 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1753 Pathname file = _root + WARNINGMAILPATH;
1754 if (filesystem::assert_dir(file) != 0)
1756 ERR << "Could not create " << file.asString() << endl;
1759 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1760 ofstream notify(file.asString().c_str(), ios::out|ios::app);
1763 ERR << "Could not open " << file << endl;
1767 // Translator: %s = name of an rpm package. A list of diffs follows
1769 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1772 ERR << "diff failed" << endl;
1773 notify << str::form(difffailmsg,
1774 file1s.c_str(), file2s.c_str()) << endl;
1778 notify << str::form(diffgenmsg,
1779 file1s.c_str(), file2s.c_str()) << endl;
1781 // remove root for the viewer's pleasure (#38240)
1782 if (!_root.empty() && _root != "/")
1784 if (out.substr(0,4) == "--- ")
1786 out.replace(4, file1.asString().length(), file1s);
1788 string::size_type pos = out.find("\n+++ ");
1789 if (pos != string::npos)
1791 out.replace(pos+5, file2.asString().length(), file2s);
1794 notify << out << endl;
1797 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1802 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1808 ///////////////////////////////////////////////////////////////////
1811 // METHOD NAME : RpmDb::installPackage
1812 // METHOD TYPE : PMError
1814 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1816 callback::SendReport<RpmInstallReport> report;
1818 report->start(filename);
1823 doInstallPackage(filename, flags, report);
1827 catch (RpmException & excpt_r)
1829 RpmInstallReport::Action user = report->problem( excpt_r );
1831 if ( user == RpmInstallReport::ABORT )
1833 report->finish( excpt_r );
1834 ZYPP_RETHROW(excpt_r);
1836 else if ( user == RpmInstallReport::IGNORE )
1844 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1846 FAILIFNOTINITIALIZED;
1847 HistoryLog historylog;
1849 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1853 if ( _packagebackups )
1855 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1856 if ( ! backupPackage( filename ) )
1858 ERR << "backup of " << filename.asString() << " failed" << endl;
1860 // FIXME status handling
1861 report->progress( 0 ); // allow 1% for backup creation.
1866 if (flags & RPMINST_NOUPGRADE)
1867 opts.push_back("-i");
1869 opts.push_back("-U");
1871 opts.push_back("--percent");
1872 opts.push_back("--noglob");
1874 // ZConfig defines cross-arch installation
1875 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1876 opts.push_back("--ignorearch");
1878 if (flags & RPMINST_NODIGEST)
1879 opts.push_back("--nodigest");
1880 if (flags & RPMINST_NOSIGNATURE)
1881 opts.push_back("--nosignature");
1882 if (flags & RPMINST_EXCLUDEDOCS)
1883 opts.push_back ("--excludedocs");
1884 if (flags & RPMINST_NOSCRIPTS)
1885 opts.push_back ("--noscripts");
1886 if (flags & RPMINST_FORCE)
1887 opts.push_back ("--force");
1888 if (flags & RPMINST_NODEPS)
1889 opts.push_back ("--nodeps");
1890 if (flags & RPMINST_IGNORESIZE)
1891 opts.push_back ("--ignoresize");
1892 if (flags & RPMINST_JUSTDB)
1893 opts.push_back ("--justdb");
1894 if (flags & RPMINST_TEST)
1895 opts.push_back ("--test");
1896 if (flags & RPMINST_NOPOSTTRANS)
1897 opts.push_back ("--noposttrans");
1899 opts.push_back("--");
1901 // rpm requires additional quoting of special chars:
1902 string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1903 opts.push_back ( quotedFilename.c_str() );
1905 modifyDatabase(); // BEFORE run_rpm
1906 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1910 vector<string> configwarnings;
1912 unsigned linecnt = 0;
1913 while (systemReadLine(line))
1915 if ( linecnt < MAXRPMMESSAGELINES )
1920 if (line.substr(0,2)=="%%")
1923 sscanf (line.c_str () + 2, "%d", &percent);
1924 report->progress( percent );
1927 rpmmsg += line+'\n';
1929 if ( line.substr(0,8) == "warning:" )
1931 configwarnings.push_back(line);
1934 if ( linecnt > MAXRPMMESSAGELINES )
1935 rpmmsg += "[truncated]\n";
1937 int rpm_status = systemStatus();
1940 for (vector<string>::iterator it = configwarnings.begin();
1941 it != configwarnings.end(); ++it)
1943 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1945 _("rpm saved %s as %s, but it was impossible to determine the difference"),
1947 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1948 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1950 _("rpm created %s as %s, but it was impossible to determine the difference"),
1952 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1955 if ( rpm_status != 0 )
1958 str::form("%s install failed", Pathname::basename(filename).c_str()),
1959 true /*timestamp*/);
1961 sstr << "rpm output:" << endl << rpmmsg << endl;
1962 historylog.comment(sstr.str());
1963 // TranslatorExplanation the colon is followed by an error message
1964 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
1965 (rpmmsg.empty() ? error_message : rpmmsg)));
1967 else if ( ! rpmmsg.empty() )
1970 str::form("%s installed ok", Pathname::basename(filename).c_str()),
1971 true /*timestamp*/);
1973 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1974 historylog.comment(sstr.str());
1976 // report additional rpm output in finish
1977 // TranslatorExplanation Text is followed by a ':' and the actual output.
1978 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1982 ///////////////////////////////////////////////////////////////////
1985 // METHOD NAME : RpmDb::removePackage
1986 // METHOD TYPE : PMError
1988 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
1990 // 'rpm -e' does not like epochs
1991 return removePackage( package->name()
1992 + "-" + package->edition().version()
1993 + "-" + package->edition().release()
1994 + "." + package->arch().asString(), flags );
1997 ///////////////////////////////////////////////////////////////////
2000 // METHOD NAME : RpmDb::removePackage
2001 // METHOD TYPE : PMError
2003 void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
2005 callback::SendReport<RpmRemoveReport> report;
2007 report->start( name_r );
2012 doRemovePackage(name_r, flags, report);
2016 catch (RpmException & excpt_r)
2018 RpmRemoveReport::Action user = report->problem( excpt_r );
2020 if ( user == RpmRemoveReport::ABORT )
2022 report->finish( excpt_r );
2023 ZYPP_RETHROW(excpt_r);
2025 else if ( user == RpmRemoveReport::IGNORE )
2034 void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2036 FAILIFNOTINITIALIZED;
2037 HistoryLog historylog;
2039 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2042 if ( _packagebackups )
2044 // FIXME solve this status report somehow
2045 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2046 if ( ! backupPackage( name_r ) )
2048 ERR << "backup of " << name_r << " failed" << endl;
2050 report->progress( 0 );
2054 report->progress( 100 );
2059 opts.push_back("-e");
2060 opts.push_back("--allmatches");
2062 if (flags & RPMINST_NOSCRIPTS)
2063 opts.push_back("--noscripts");
2064 if (flags & RPMINST_NODEPS)
2065 opts.push_back("--nodeps");
2066 if (flags & RPMINST_JUSTDB)
2067 opts.push_back("--justdb");
2068 if (flags & RPMINST_TEST)
2069 opts.push_back ("--test");
2070 if (flags & RPMINST_FORCE)
2072 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2075 opts.push_back("--");
2076 opts.push_back(name_r.c_str());
2078 modifyDatabase(); // BEFORE run_rpm
2079 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2084 // got no progress from command, so we fake it:
2085 // 5 - command started
2086 // 50 - command completed
2088 report->progress( 5 );
2089 unsigned linecnt = 0;
2090 while (systemReadLine(line))
2092 if ( linecnt < MAXRPMMESSAGELINES )
2096 rpmmsg += line+'\n';
2098 if ( linecnt > MAXRPMMESSAGELINES )
2099 rpmmsg += "[truncated]\n";
2100 report->progress( 50 );
2101 int rpm_status = systemStatus();
2103 if ( rpm_status != 0 )
2106 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2108 sstr << "rpm output:" << endl << rpmmsg << endl;
2109 historylog.comment(sstr.str());
2110 // TranslatorExplanation the colon is followed by an error message
2111 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2112 (rpmmsg.empty() ? error_message: rpmmsg)));
2114 else if ( ! rpmmsg.empty() )
2117 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2120 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2121 historylog.comment(sstr.str());
2123 // report additional rpm output in finish
2124 // TranslatorExplanation Text is followed by a ':' and the actual output.
2125 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2129 ///////////////////////////////////////////////////////////////////
2132 // METHOD NAME : RpmDb::backupPackage
2133 // METHOD TYPE : bool
2135 bool RpmDb::backupPackage( const Pathname & filename )
2137 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2141 return backupPackage( h->tag_name() );
2144 ///////////////////////////////////////////////////////////////////
2147 // METHOD NAME : RpmDb::backupPackage
2148 // METHOD TYPE : bool
2150 bool RpmDb::backupPackage(const string& packageName)
2152 HistoryLog progresslog;
2154 Pathname backupFilename;
2155 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2157 if (_backuppath.empty())
2159 INT << "_backuppath empty" << endl;
2165 if (!queryChangedFiles(fileList, packageName))
2167 ERR << "Error while getting changed files for package " <<
2168 packageName << endl;
2172 if (fileList.size() <= 0)
2174 DBG << "package " << packageName << " not changed -> no backup" << endl;
2178 if (filesystem::assert_dir(_root + _backuppath) != 0)
2184 // build up archive name
2185 time_t currentTime = time(0);
2186 struct tm *currentLocalTime = localtime(¤tTime);
2188 int date = (currentLocalTime->tm_year + 1900) * 10000
2189 + (currentLocalTime->tm_mon + 1) * 100
2190 + currentLocalTime->tm_mday;
2195 backupFilename = _root + _backuppath
2196 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2199 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2201 PathInfo pi(filestobackupfile);
2202 if (pi.isExist() && !pi.isFile())
2204 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2208 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2212 ERR << "could not open " << filestobackupfile.asString() << endl;
2216 for (FileList::const_iterator cit = fileList.begin();
2217 cit != fileList.end(); ++cit)
2220 if ( name[0] == '/' )
2222 // remove slash, file must be relative to -C parameter of tar
2223 name = name.substr( 1 );
2225 DBG << "saving file "<< name << endl;
2230 const char* const argv[] =
2235 _root.asString().c_str(),
2236 "--ignore-failed-read",
2238 backupFilename.asString().c_str(),
2240 filestobackupfile.asString().c_str(),
2244 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2245 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2249 // TODO: its probably possible to start tar with -v and watch it adding
2250 // files to report progress
2251 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2256 int ret = tar.close();
2260 ERR << "tar failed: " << tarmsg << endl;
2265 MIL << "tar backup ok" << endl;
2266 progresslog.comment(
2267 str::form(_("created backup %s"), backupFilename.asString().c_str())
2268 , /*timestamp*/true);
2271 filesystem::unlink(filestobackupfile);
2277 void RpmDb::setBackupPath(const Pathname& path)
2283 } // namespace target