1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
27 #include "zypp/base/Logger.h"
28 #include "zypp/base/String.h"
30 #include "zypp/Date.h"
31 #include "zypp/Pathname.h"
32 #include "zypp/PathInfo.h"
33 #include "zypp/PublicKey.h"
35 #include "zypp/target/rpm/RpmDb.h"
36 #include "zypp/target/rpm/RpmCallbacks.h"
38 #include "zypp/target/CommitLog.h"
39 #include "zypp/target/rpm/librpmDb.h"
40 #include "zypp/target/rpm/RpmPackageImpl.h"
41 #include "zypp/target/rpm/RpmException.h"
42 #include "zypp/CapSet.h"
43 #include "zypp/CapFactory.h"
44 #include "zypp/KeyRing.h"
45 #include "zypp/ZYppFactory.h"
46 #include "zypp/TmpPath.h"
53 using namespace zypp::filesystem;
63 const char* quoteInFilename_m = " \t";
64 inline string rpmQuoteFilename( const Pathname & path_r )
66 string path( path_r.asString() );
67 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
69 pos = path.find_first_of( quoteInFilename_m, pos ) )
71 path.insert( pos, "\\" );
72 pos += 2; // skip '\\' and the quoted char.
78 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
80 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
85 ~KeyRingSignalReceiver()
90 virtual void trustedKeyAdded( const PublicKey &key )
92 MIL << "trusted key added to zypp Keyring. Importing" << endl;
93 // now import the key in rpm
96 _rpmdb.importPubkey( key );
98 catch (RpmException &e)
100 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
104 virtual void trustedKeyRemoved( const PublicKey &key )
110 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
112 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
122 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
129 for (line = prog.receiveLine(), count=0;
131 line = prog.receiveLine(), count++ )
133 if (maxlines<0?true:count<maxlines)
142 /******************************************************************
145 ** FUNCTION NAME : stringPath
146 ** FUNCTION TYPE : inline string
148 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
150 return librpmDb::stringPath( root_r, sub_r );
153 /******************************************************************
156 ** FUNCTION NAME : operator<<
157 ** FUNCTION TYPE : ostream &
159 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
161 if ( obj == RpmDb::DbSI_NO_INIT )
167 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
169 ENUM_OUT( DbSI_HAVE_V4, 'X' );
170 ENUM_OUT( DbSI_MADE_V4, 'c' );
171 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
173 ENUM_OUT( DbSI_HAVE_V3, 'X' );
174 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
175 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
182 ///////////////////////////////////////////////////////////////////
183 // CLASS NAME : RpmDbPtr
184 // CLASS NAME : RpmDbconstPtr
185 ///////////////////////////////////////////////////////////////////
187 #define WARNINGMAILPATH "/var/log/YaST2/"
188 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
190 ///////////////////////////////////////////////////////////////////
192 // CLASS NAME : RpmDb::Packages
194 * Helper class for RpmDb::getPackages() to build the
195 * list<Package::Ptr> returned. We have to assert, that there
196 * is a unique entry for every string.
198 * In the first step we build the _list list which contains all
199 * packages (even those which are contained in multiple versions).
201 * At the end buildIndex() is called to build the _index is created
202 * and points to the last installed versions of all packages.
203 * Operations changing the rpmdb
204 * content (install/remove package) should set _valid to false. The
205 * next call to RpmDb::getPackages() will then reread the the rpmdb.
207 * Note that outside RpmDb::getPackages() _list and _index are always
208 * in sync. So you may use lookup(PkgName) to retrieve a specific
211 class RpmDb::Packages
214 list<Package::Ptr> _list;
215 map<string,Package::Ptr> _index;
217 Packages() : _valid( false )
225 Package::Ptr lookup( const string & name_r ) const
227 map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
228 if ( got != _index.end() )
230 return Package::Ptr();
235 for ( list<Package::Ptr>::iterator iter = _list.begin();
236 iter != _list.end(); ++iter )
238 string name = (*iter)->name();
239 Package::Ptr & nptr = _index[name]; // be shure to get a reference!
243 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
244 if ( nptr->installtime() > (*iter)->installtime() )
258 ///////////////////////////////////////////////////////////////////
260 ///////////////////////////////////////////////////////////////////
262 // CLASS NAME : RpmDb
264 ///////////////////////////////////////////////////////////////////
266 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
268 ///////////////////////////////////////////////////////////////////
270 ///////////////////////////////////////////////////////////////////
273 // METHOD NAME : RpmDb::RpmDb
274 // METHOD TYPE : Constructor
277 : _dbStateInfo( DbSI_NO_INIT )
278 , _packages( * new Packages ) // delete in destructor
279 #warning Check for obsolete memebers
280 , _backuppath ("/var/adm/backup")
281 , _packagebackups(false)
282 , _warndirexists(false)
287 // Some rpm versions are patched not to abort installation if
288 // symlink creation failed.
289 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
290 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
293 ///////////////////////////////////////////////////////////////////
296 // METHOD NAME : RpmDb::~RpmDb
297 // METHOD TYPE : Destructor
301 MIL << "~RpmDb()" << endl;
306 MIL << "~RpmDb() end" << endl;
307 sKeyRingReceiver.reset();
310 Date RpmDb::timestamp() const
315 if ( dbPath().empty() )
316 db_path = "/var/lib/rpm";
320 PathInfo rpmdb_info(root() + db_path + "/Packages");
322 if ( rpmdb_info.isExist() )
323 return rpmdb_info.mtime();
327 ///////////////////////////////////////////////////////////////////
330 // METHOD NAME : RpmDb::dumpOn
331 // METHOD TYPE : ostream &
333 ostream & RpmDb::dumpOn( ostream & str ) const
337 if ( _dbStateInfo == DbSI_NO_INIT )
343 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
345 ENUM_OUT( DbSI_HAVE_V4, 'X' );
346 ENUM_OUT( DbSI_MADE_V4, 'c' );
347 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
349 ENUM_OUT( DbSI_HAVE_V3, 'X' );
350 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
351 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
352 str << "): " << stringPath( _root, _dbPath );
358 ///////////////////////////////////////////////////////////////////
361 // METHOD NAME : RpmDb::initDatabase
362 // METHOD TYPE : PMError
364 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
366 ///////////////////////////////////////////////////////////////////
368 ///////////////////////////////////////////////////////////////////
369 if ( root_r.empty() )
372 if ( dbPath_r.empty() )
373 dbPath_r = "/var/lib/rpm";
375 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
377 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
378 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
381 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
383 ///////////////////////////////////////////////////////////////////
384 // Check whether already initialized
385 ///////////////////////////////////////////////////////////////////
388 if ( root_r == _root && dbPath_r == _dbPath )
394 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
398 ///////////////////////////////////////////////////////////////////
400 ///////////////////////////////////////////////////////////////////
401 librpmDb::unblockAccess();
402 DbStateInfoBits info = DbSI_NO_INIT;
405 internal_initDatabase( root_r, dbPath_r, info );
407 catch (const RpmException & excpt_r)
409 ZYPP_CAUGHT(excpt_r);
410 librpmDb::blockAccess();
411 ERR << "Cleanup on error: state " << info << endl;
413 if ( dbsi_has( info, DbSI_MADE_V4 ) )
415 // remove the newly created rpm4 database and
416 // any backup created on conversion.
417 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
419 ZYPP_RETHROW(excpt_r);
421 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
423 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
425 // Move obsolete rpm3 database beside.
426 MIL << "Cleanup: state " << info << endl;
427 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
428 dbsi_clr( info, DbSI_HAVE_V3 );
432 // Performing an update: Keep the original rpm3 database
433 // and wait if the rpm4 database gets modified by installing
434 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
435 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
438 #warning CHECK: notify root about conversion backup.
444 #warning Add rebuild database once have the info about context
446 if ( ! ( Y2PM::runningFromSystem() ) )
448 if ( dbsi_has( info, DbSI_HAVE_V4 )
449 && ! dbsi_has( info, DbSI_MADE_V4 ) )
451 err = rebuildDatabase();
456 MIL << "Syncronizing keys with zypp keyring" << endl;
457 // we do this one by one now.
458 //importZyppKeyRingTrustedKeys();
459 exportTrustedKeysInZyppKeyRing();
461 // Close the database in case any write acces (create/convert)
462 // happened during init. This should drop any lock acquired
463 // by librpm. On demand it will be reopened readonly and should
464 // not hold any lock.
465 librpmDb::dbRelease( true );
467 MIL << "InitDatabase: " << *this << endl;
470 ///////////////////////////////////////////////////////////////////
473 // METHOD NAME : RpmDb::internal_initDatabase
474 // METHOD TYPE : PMError
476 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
477 DbStateInfoBits & info_r )
479 info_r = DbSI_NO_INIT;
481 ///////////////////////////////////////////////////////////////////
482 // Get info about the desired database dir
483 ///////////////////////////////////////////////////////////////////
484 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
486 if ( dbInfo.illegalArgs() )
488 // should not happen (checked in initDatabase)
489 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
491 if ( ! dbInfo.usableArgs() )
493 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
494 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
497 if ( dbInfo.hasDbV4() )
499 dbsi_set( info_r, DbSI_HAVE_V4 );
500 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
504 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
507 if ( dbInfo.hasDbV3() )
509 dbsi_set( info_r, DbSI_HAVE_V3 );
511 if ( dbInfo.hasDbV3ToV4() )
513 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
516 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
517 librpmDb::dumpState( DBG ) << endl;
519 ///////////////////////////////////////////////////////////////////
520 // Access database, create if needed
521 ///////////////////////////////////////////////////////////////////
523 // creates dbdir and empty rpm4 database if not present
524 librpmDb::dbAccess( root_r, dbPath_r );
526 if ( ! dbInfo.hasDbV4() )
529 if ( dbInfo.hasDbV4() )
531 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
535 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
536 librpmDb::dumpState( DBG ) << endl;
538 ///////////////////////////////////////////////////////////////////
539 // Check whether to convert something. Create backup but do
540 // not remove anything here
541 ///////////////////////////////////////////////////////////////////
542 librpmDb::constPtr dbptr;
543 librpmDb::dbAccess( dbptr );
544 bool dbEmpty = dbptr->empty();
547 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
550 if ( dbInfo.hasDbV3() )
552 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
556 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
557 convertV3toV4( dbInfo.dbV3().path(), dbptr );
559 // create a backup copy
560 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
563 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
568 if ( dbInfo.hasDbV3ToV4() )
570 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
571 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
579 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
580 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
581 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
582 //report->start( dbInfo.dbV3().path() );
583 //report->stop( some error );
585 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
586 dbsi_set( info_r, DbSI_MODIFIED_V4 );
590 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
591 librpmDb::dumpState( DBG ) << endl;
594 if ( dbInfo.hasDbV3ToV4() )
596 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
600 ///////////////////////////////////////////////////////////////////
603 // METHOD NAME : RpmDb::removeV4
604 // METHOD TYPE : void
606 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
608 const char * v3backup = "packages.rpm3";
609 const char * master = "Packages";
610 const char * index[] =
632 PathInfo pi( dbdir_r );
635 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
639 for ( const char ** f = index; *f; ++f )
644 filesystem::unlink( pi.path() );
648 pi( dbdir_r + master );
651 MIL << "Removing rpm4 database " << pi << endl;
652 filesystem::unlink( pi.path() );
657 pi( dbdir_r + v3backup );
660 MIL << "Removing converted rpm3 database backup " << pi << endl;
661 filesystem::unlink( pi.path() );
666 ///////////////////////////////////////////////////////////////////
669 // METHOD NAME : RpmDb::removeV3
670 // METHOD TYPE : void
672 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
674 const char * master = "packages.rpm";
675 const char * index[] =
677 "conflictsindex.rpm",
688 PathInfo pi( dbdir_r );
691 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
695 for ( const char ** f = index; *f; ++f )
700 filesystem::unlink( pi.path() );
704 #warning CHECK: compare vs existing v3 backup. notify root
705 pi( dbdir_r + master );
708 Pathname m( pi.path() );
711 // backup was already created
712 filesystem::unlink( m );
713 Pathname b( m.extend( "3" ) );
714 pi( b ); // stat backup
718 Pathname b( m.extend( ".deleted" ) );
722 // rempve existing backup
723 filesystem::unlink( b );
725 filesystem::rename( m, b );
726 pi( b ); // stat backup
728 MIL << "(Re)moved rpm3 database to " << pi << endl;
732 ///////////////////////////////////////////////////////////////////
735 // METHOD NAME : RpmDb::modifyDatabase
736 // METHOD TYPE : void
738 void RpmDb::modifyDatabase()
740 if ( ! initialized() )
743 // tag database as modified
744 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
746 // Move outdated rpm3 database beside.
747 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
749 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
750 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
751 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
754 // invalidate Packages list
755 _packages._valid = false;
758 ///////////////////////////////////////////////////////////////////
761 // METHOD NAME : RpmDb::closeDatabase
762 // METHOD TYPE : PMError
764 void RpmDb::closeDatabase()
766 if ( ! initialized() )
771 MIL << "Calling closeDatabase: " << *this << endl;
773 ///////////////////////////////////////////////////////////////////
774 // Block further database access
775 ///////////////////////////////////////////////////////////////////
777 librpmDb::blockAccess();
779 ///////////////////////////////////////////////////////////////////
780 // Check fate if old version database still present
781 ///////////////////////////////////////////////////////////////////
782 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
784 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
785 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
787 // Move outdated rpm3 database beside.
788 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
792 // Remove unmodified rpm4 database
793 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
797 ///////////////////////////////////////////////////////////////////
799 ///////////////////////////////////////////////////////////////////
800 _root = _dbPath = Pathname();
801 _dbStateInfo = DbSI_NO_INIT;
803 MIL << "closeDatabase: " << *this << endl;
806 ///////////////////////////////////////////////////////////////////
809 // METHOD NAME : RpmDb::rebuildDatabase
810 // METHOD TYPE : PMError
812 void RpmDb::rebuildDatabase()
814 callback::SendReport<RebuildDBReport> report;
816 report->start( root() + dbPath() );
820 doRebuildDatabase(report);
822 catch (RpmException & excpt_r)
824 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
825 ZYPP_RETHROW(excpt_r);
827 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
830 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
832 FAILIFNOTINITIALIZED;
834 MIL << "RpmDb::rebuildDatabase" << *this << endl;
835 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
837 PathInfo dbMaster( root() + dbPath() + "Packages" );
838 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
842 opts.push_back("--rebuilddb");
843 opts.push_back("-vv");
845 // don't call modifyDatabase because it would remove the old
846 // rpm3 database, if the current database is a temporary one.
847 // But do invalidate packages list.
848 _packages._valid = false;
849 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
851 // progress report: watch this file growing
852 PathInfo newMaster( root()
853 + dbPath().extend( str::form( "rebuilddb.%d",
854 process?process->getpid():0) )
860 while ( systemReadLine( line ) )
863 { // file is removed at the end of rebuild.
864 // current size should be upper limit for new db
865 report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath());
868 if ( line.compare( 0, 2, "D:" ) )
870 errmsg += line + '\n';
871 // report.notify( line );
876 int rpm_status = systemStatus();
878 if ( rpm_status != 0 )
880 ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
884 report->progress( 100, root() + dbPath() ); // 100%
888 void RpmDb::exportTrustedKeysInZyppKeyRing()
890 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
892 set<Edition> rpm_keys = pubkeyEditions();
894 list<PublicKey> zypp_keys;
895 zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
897 for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
899 // search the zypp key into the rpm keys
900 // long id is edition version + release
901 string id = str::toUpper( (*it).version() + (*it).release());
902 list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
903 if ( ik != zypp_keys.end() )
905 MIL << "Key " << (*it) << " is already in zypp database." << endl;
909 // we export the rpm key into a file
910 RpmHeader::constPtr result = new RpmHeader();
911 getData( string("gpg-pubkey"), *it, result );
912 TmpFile file(getZYpp()->tmpPath());
916 os.open(file.path().asString().c_str());
917 // dump rpm key into the tmp file
918 os << result->tag_description();
919 //MIL << "-----------------------------------------------" << endl;
920 //MIL << result->tag_description() <<endl;
921 //MIL << "-----------------------------------------------" << endl;
926 ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << endl;
927 // just ignore the key
930 // now import the key in zypp
933 getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
934 MIL << "Trusted key " << (*it) << " imported in zypp keyring." << endl;
938 ERR << "Could not import key " << (*it) << " in zypp keyring" << endl;
944 ///////////////////////////////////////////////////////////////////
947 // METHOD NAME : RpmDb::importPubkey
948 // METHOD TYPE : PMError
950 void RpmDb::importPubkey( const PublicKey & pubkey_r )
952 FAILIFNOTINITIALIZED;
954 // check if the key is already in the rpm database and just
955 // return if it does.
956 set<Edition> rpm_keys = pubkeyEditions();
957 for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
959 string id = str::toUpper( (*it).version() );
960 string keyshortid = pubkey_r.id().substr(8,8);
961 MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
962 if ( id == keyshortid )
965 // FIXME id is not sufficient?
966 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
970 // key does not exists, lets import it
973 opts.push_back ( "--import" );
974 opts.push_back ( "--" );
975 opts.push_back ( pubkey_r.path().asString().c_str() );
977 // don't call modifyDatabase because it would remove the old
978 // rpm3 database, if the current database is a temporary one.
979 // But do invalidate packages list.
980 _packages._valid = false;
981 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
984 while ( systemReadLine( line ) )
986 if ( line.substr( 0, 6 ) == "error:" )
996 int rpm_status = systemStatus();
998 if ( rpm_status != 0 )
1000 ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned ") + str::numstring(rpm_status)));
1004 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1008 ///////////////////////////////////////////////////////////////////
1011 // METHOD NAME : RpmDb::pubkeys
1012 // METHOD TYPE : set<Edition>
1014 list<PublicKey> RpmDb::pubkeys() const
1016 list<PublicKey> ret;
1018 librpmDb::db_const_iterator it;
1019 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1021 Edition edition = it->tag_edition();
1022 if (edition != Edition::noedition)
1024 // we export the rpm key into a file
1025 RpmHeader::constPtr result = new RpmHeader();
1026 getData( string("gpg-pubkey"), edition, result );
1027 TmpFile file(getZYpp()->tmpPath());
1031 os.open(file.path().asString().c_str());
1032 // dump rpm key into the tmp file
1033 os << result->tag_description();
1034 //MIL << "-----------------------------------------------" << endl;
1035 //MIL << result->tag_description() <<endl;
1036 //MIL << "-----------------------------------------------" << endl;
1038 // read the public key from the dumped file
1039 PublicKey key(file.path());
1042 catch (exception &e)
1044 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1045 // just ignore the key
1052 set<Edition> RpmDb::pubkeyEditions() const
1056 librpmDb::db_const_iterator it;
1057 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1059 Edition edition = it->tag_edition();
1060 if (edition != Edition::noedition)
1061 ret.insert( edition );
1066 ///////////////////////////////////////////////////////////////////
1069 // METHOD NAME : RpmDb::packagesValid
1070 // METHOD TYPE : bool
1072 bool RpmDb::packagesValid() const
1074 return( _packages._valid || ! initialized() );
1077 ///////////////////////////////////////////////////////////////////
1080 // METHOD NAME : RpmDb::getPackages
1081 // METHOD TYPE : const list<Package::Ptr> &
1085 const list<Package::Ptr> & RpmDb::getPackages()
1087 callback::SendReport<ScanDBReport> report;
1093 const list<Package::Ptr> & ret = doGetPackages(report);
1094 report->finish(ScanDBReport::NO_ERROR, "");
1097 catch (RpmException & excpt_r)
1099 report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
1100 ZYPP_RETHROW(excpt_r);
1103 static const list<Package::Ptr> empty_list;
1107 inline static void insertCaps( CapSet &capset, capability::CapabilityImplPtrSet ptrset, CapFactory &factory )
1109 for ( capability::CapabilityImplPtrSet::const_iterator it = ptrset.begin();
1113 capset.insert( factory.fromImpl(*it) );
1118 // make Package::Ptr from RpmHeader
1119 // return NULL on error
1121 Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header,
1122 set<string> * filerequires,
1123 const Pathname & location, Repository repo )
1128 if ( header->isSrc() )
1130 WAR << "Can't make Package from SourcePackage header" << endl;
1136 string name = header->tag_name();
1138 // create dataprovider
1139 detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
1141 impl->setRepository( repo );
1142 if (!location.empty())
1143 impl->setLocation( OnMediaLocation(location,1) );
1148 edition = Edition( header->tag_version(),
1149 header->tag_release(),
1150 header->tag_epoch());
1152 catch (Exception & excpt_r)
1154 ZYPP_CAUGHT( excpt_r );
1155 WAR << "Package " << name << " has bad edition '"
1156 << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
1157 << header->tag_version()
1158 << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
1165 arch = Arch( header->tag_arch() );
1167 catch (Exception & excpt_r)
1169 ZYPP_CAUGHT( excpt_r );
1170 WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
1174 // Collect basic Resolvable data
1175 NVRAD dataCollect( header->tag_name(),
1179 list<string> filenames = impl->filenames();
1180 CapFactory capfactory;
1181 insertCaps( dataCollect[Dep::PROVIDES], header->tag_provides( filerequires ), capfactory );
1183 static str::smatch what;
1184 static const str::regex filenameRegex( "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
1185 str::regex::optimize|str::regex::nosubs );
1187 for (list<string>::const_iterator filename = filenames.begin();
1188 filename != filenames.end();
1191 if ( str::regex_search( filename->begin(), filename->end(), what, filenameRegex ) )
1195 dataCollect[Dep::PROVIDES].insert(capfactory.fromImpl(capability::buildFile(ResTraits<Package>::kind, *filename) ));
1197 catch (Exception & excpt_r)
1199 ZYPP_CAUGHT( excpt_r );
1200 WAR << "Ignoring invalid capability: " << *filename << endl;
1205 insertCaps( dataCollect[Dep::REQUIRES], header->tag_requires( filerequires ), capfactory );
1206 insertCaps( dataCollect[Dep::PREREQUIRES], header->tag_prerequires( filerequires ), capfactory );
1207 insertCaps( dataCollect[Dep::CONFLICTS], header->tag_conflicts( filerequires ), capfactory );
1208 insertCaps( dataCollect[Dep::OBSOLETES], header->tag_obsoletes( filerequires ), capfactory );
1209 insertCaps( dataCollect[Dep::ENHANCES], header->tag_enhances( filerequires ), capfactory );
1210 insertCaps( dataCollect[Dep::SUPPLEMENTS], header->tag_supplements( filerequires ), capfactory );
1214 // create package from dataprovider
1215 pptr = detail::makeResolvableFromImpl( dataCollect, impl );
1217 catch (Exception & excpt_r)
1219 ZYPP_CAUGHT( excpt_r );
1220 ERR << "Can't create Package::Ptr" << endl;
1226 const list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
1228 if ( packagesValid() )
1230 return _packages._list;
1235 ///////////////////////////////////////////////////////////////////
1236 // Collect package data.
1237 ///////////////////////////////////////////////////////////////////
1238 unsigned expect = 0;
1239 librpmDb::constPtr dbptr;
1240 librpmDb::dbAccess( dbptr );
1241 expect = dbptr->size();
1242 DBG << "Expecting " << expect << " packages" << endl;
1244 librpmDb::db_const_iterator iter;
1245 unsigned current = 0;
1249 for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
1252 string name = iter->tag_name();
1253 if ( name == string( "gpg-pubkey" ) )
1255 DBG << "Ignoring pseudo package " << name << endl;
1256 // pseudo package filtered, as we can't handle multiple instances
1257 // of 'gpg-pubkey-VERS-REL'.
1261 Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Repository() );
1264 WAR << "Failed to make package from database header '" << name << "'" << endl;
1268 _packages._list.push_back( pptr );
1270 _packages.buildIndex();
1271 DBG << "Found installed packages: " << _packages._list.size() << endl;
1273 ///////////////////////////////////////////////////////////////////
1274 // Evaluate filerequires collected so far
1275 ///////////////////////////////////////////////////////////////////
1276 for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
1279 for ( iter.findByFile( *it ); *iter; ++iter )
1281 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1284 WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1287 pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
1292 ///////////////////////////////////////////////////////////////////
1293 // Build final packages list
1294 ///////////////////////////////////////////////////////////////////
1295 return _packages._list;
1298 ///////////////////////////////////////////////////////////////////
1301 // METHOD NAME : RpmDb::fileList
1302 // METHOD TYPE : bool
1307 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1309 list<FileInfo> result;
1311 librpmDb::db_const_iterator it;
1313 if (edition_r == Edition::noedition)
1315 found = it.findPackage( name_r );
1319 found = it.findPackage( name_r, edition_r );
1328 ///////////////////////////////////////////////////////////////////
1331 // METHOD NAME : RpmDb::hasFile
1332 // METHOD TYPE : bool
1336 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1338 librpmDb::db_const_iterator it;
1342 res = it.findByFile( file_r );
1344 if (!name_r.empty())
1346 res = (it->tag_name() == name_r);
1354 ///////////////////////////////////////////////////////////////////
1357 // METHOD NAME : RpmDb::whoOwnsFile
1358 // METHOD TYPE : string
1362 string RpmDb::whoOwnsFile( const string & file_r) const
1364 librpmDb::db_const_iterator it;
1365 if (it.findByFile( file_r ))
1367 return it->tag_name();
1372 ///////////////////////////////////////////////////////////////////
1375 // METHOD NAME : RpmDb::hasProvides
1376 // METHOD TYPE : bool
1380 bool RpmDb::hasProvides( const string & tag_r ) const
1382 librpmDb::db_const_iterator it;
1383 return it.findByProvides( tag_r );
1386 ///////////////////////////////////////////////////////////////////
1389 // METHOD NAME : RpmDb::hasRequiredBy
1390 // METHOD TYPE : bool
1394 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1396 librpmDb::db_const_iterator it;
1397 return it.findByRequiredBy( tag_r );
1400 ///////////////////////////////////////////////////////////////////
1403 // METHOD NAME : RpmDb::hasConflicts
1404 // METHOD TYPE : bool
1408 bool RpmDb::hasConflicts( const string & tag_r ) const
1410 librpmDb::db_const_iterator it;
1411 return it.findByConflicts( tag_r );
1414 ///////////////////////////////////////////////////////////////////
1417 // METHOD NAME : RpmDb::hasPackage
1418 // METHOD TYPE : bool
1422 bool RpmDb::hasPackage( const string & name_r ) const
1424 librpmDb::db_const_iterator it;
1425 return it.findPackage( name_r );
1428 ///////////////////////////////////////////////////////////////////
1431 // METHOD NAME : RpmDb::hasPackage
1432 // METHOD TYPE : bool
1436 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1438 librpmDb::db_const_iterator it;
1439 return it.findPackage( name_r, ed_r );
1442 ///////////////////////////////////////////////////////////////////
1445 // METHOD NAME : RpmDb::getData
1446 // METHOD TYPE : PMError
1450 void RpmDb::getData( const string & name_r,
1451 RpmHeader::constPtr & result_r ) const
1453 librpmDb::db_const_iterator it;
1454 it.findPackage( name_r );
1457 ZYPP_THROW(*(it.dbError()));
1460 ///////////////////////////////////////////////////////////////////
1463 // METHOD NAME : RpmDb::getData
1464 // METHOD TYPE : void
1468 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1469 RpmHeader::constPtr & result_r ) const
1471 librpmDb::db_const_iterator it;
1472 it.findPackage( name_r, ed_r );
1475 ZYPP_THROW(*(it.dbError()));
1478 ///////////////////////////////////////////////////////////////////
1480 // METHOD NAME : RpmDb::checkPackage
1481 // METHOD TYPE : RpmDb::checkPackageResult
1483 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
1485 PathInfo file( path_r );
1486 if ( ! file.isFile() )
1488 ERR << "Not a file: " << file << endl;
1492 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1493 if ( fd == 0 || ::Ferror(fd) )
1495 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1501 rpmts ts = ::rpmtsCreate();
1502 ::rpmtsSetRootDir( ts, root().asString().c_str() );
1503 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1504 int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
1505 ts = ::rpmtsFree(ts);
1514 case RPMRC_NOTFOUND:
1515 WAR << "Signature is unknown type. " << file << endl;
1516 return CHK_NOTFOUND;
1519 WAR << "Signature does not verify. " << file << endl;
1522 case RPMRC_NOTTRUSTED:
1523 WAR << "Signature is OK, but key is not trusted. " << file << endl;
1524 return CHK_NOTTRUSTED;
1527 WAR << "Public key is unavailable. " << file << endl;
1531 ERR << "Error reading header." << file << endl;
1535 // determine changed files of installed package
1537 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1543 if ( ! initialized() ) return false;
1547 opts.push_back ("-V");
1548 opts.push_back ("--nodeps");
1549 opts.push_back ("--noscripts");
1550 opts.push_back ("--nomd5");
1551 opts.push_back ("--");
1552 opts.push_back (packageName.c_str());
1554 run_rpm (opts, ExternalProgram::Discard_Stderr);
1556 if ( process == NULL )
1567 M Mode (includes permissions and file type)
1571 while (systemReadLine(line))
1573 if (line.length() > 12 &&
1574 (line[0] == 'S' || line[0] == 's' ||
1575 (line[0] == '.' && line[7] == 'T')))
1577 // file has been changed
1580 filename.assign(line, 11, line.length() - 11);
1581 fileList.insert(filename);
1586 // exit code ignored, rpm returns 1 no matter if package is installed or
1594 /****************************************************************/
1595 /* private member-functions */
1596 /****************************************************************/
1598 /*--------------------------------------------------------------*/
1599 /* Run rpm with the specified arguments, handling stderr */
1600 /* as specified by disp */
1601 /*--------------------------------------------------------------*/
1603 RpmDb::run_rpm (const RpmArgVec& opts,
1604 ExternalProgram::Stderr_Disposition disp)
1613 if ( ! initialized() )
1615 ZYPP_THROW(RpmDbNotOpenException());
1620 // always set root and dbpath
1621 args.push_back("rpm");
1622 args.push_back("--root");
1623 args.push_back(_root.asString().c_str());
1624 args.push_back("--dbpath");
1625 args.push_back(_dbPath.asString().c_str());
1627 const char* argv[args.size() + opts.size() + 1];
1629 const char** p = argv;
1630 p = copy (args.begin (), args.end (), p);
1631 p = copy (opts.begin (), opts.end (), p);
1634 // Invalidate all outstanding database handles in case
1635 // the database gets modified.
1636 librpmDb::dbRelease( true );
1638 // Launch the program with default locale
1639 process = new ExternalProgram(argv, disp, false, -1, true);
1643 /*--------------------------------------------------------------*/
1644 /* Read a line from the rpm process */
1645 /*--------------------------------------------------------------*/
1647 RpmDb::systemReadLine(string &line)
1651 if ( process == NULL )
1654 line = process->receiveLine();
1656 if (line.length() == 0)
1659 if (line[line.length() - 1] == '\n')
1660 line.erase(line.length() - 1);
1665 /*--------------------------------------------------------------*/
1666 /* Return the exit status of the rpm process, closing the */
1667 /* connection if not already done */
1668 /*--------------------------------------------------------------*/
1670 RpmDb::systemStatus()
1672 if ( process == NULL )
1675 exit_code = process->close();
1680 // DBG << "exit code " << exit_code << endl;
1685 /*--------------------------------------------------------------*/
1686 /* Forcably kill the rpm process */
1687 /*--------------------------------------------------------------*/
1691 if (process) process->kill();
1695 // generate diff mails for config files
1696 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1698 string msg = line.substr(9);
1699 string::size_type pos1 = string::npos;
1700 string::size_type pos2 = string::npos;
1701 string file1s, file2s;
1705 pos1 = msg.find (typemsg);
1708 if ( pos1 == string::npos )
1711 pos2 = pos1 + strlen (typemsg);
1713 if (pos2 >= msg.length() )
1716 file1 = msg.substr (0, pos1);
1717 file2 = msg.substr (pos2);
1719 file1s = file1.asString();
1720 file2s = file2.asString();
1722 if (!_root.empty() && _root != "/")
1724 file1 = _root + file1;
1725 file2 = _root + file2;
1729 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1732 Pathname file = _root + WARNINGMAILPATH;
1733 if (filesystem::assert_dir(file) != 0)
1735 ERR << "Could not create " << file.asString() << endl;
1738 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1739 ofstream notify(file.asString().c_str(), ios::out|ios::app);
1742 ERR << "Could not open " << file << endl;
1746 // Translator: %s = name of an rpm package. A list of diffs follows
1748 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1751 ERR << "diff failed" << endl;
1752 notify << str::form(difffailmsg,
1753 file1s.c_str(), file2s.c_str()) << endl;
1757 notify << str::form(diffgenmsg,
1758 file1s.c_str(), file2s.c_str()) << endl;
1760 // remove root for the viewer's pleasure (#38240)
1761 if (!_root.empty() && _root != "/")
1763 if (out.substr(0,4) == "--- ")
1765 out.replace(4, file1.asString().length(), file1s);
1767 string::size_type pos = out.find("\n+++ ");
1768 if (pos != string::npos)
1770 out.replace(pos+5, file2.asString().length(), file2s);
1773 notify << out << endl;
1776 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1781 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1787 ///////////////////////////////////////////////////////////////////
1790 // METHOD NAME : RpmDb::installPackage
1791 // METHOD TYPE : PMError
1793 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1795 callback::SendReport<RpmInstallReport> report;
1797 report->start(filename);
1802 doInstallPackage(filename, flags, report);
1806 catch (RpmException & excpt_r)
1808 RpmInstallReport::Action user = report->problem( excpt_r );
1810 if ( user == RpmInstallReport::ABORT )
1812 report->finish( excpt_r );
1813 ZYPP_RETHROW(excpt_r);
1815 else if ( user == RpmInstallReport::IGNORE )
1823 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
1825 FAILIFNOTINITIALIZED;
1826 CommitLog progresslog;
1828 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1832 if ( _packagebackups )
1834 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1835 if ( ! backupPackage( filename ) )
1837 ERR << "backup of " << filename.asString() << " failed" << endl;
1839 // FIXME status handling
1840 report->progress( 0 ); // allow 1% for backup creation.
1844 report->progress( 100 );
1849 if (flags & RPMINST_NOUPGRADE)
1850 opts.push_back("-i");
1852 opts.push_back("-U");
1853 opts.push_back("--percent");
1855 if (flags & RPMINST_NODIGEST)
1856 opts.push_back("--nodigest");
1857 if (flags & RPMINST_NOSIGNATURE)
1858 opts.push_back("--nosignature");
1859 if (flags & RPMINST_NODOCS)
1860 opts.push_back ("--excludedocs");
1861 if (flags & RPMINST_NOSCRIPTS)
1862 opts.push_back ("--noscripts");
1863 if (flags & RPMINST_FORCE)
1864 opts.push_back ("--force");
1865 if (flags & RPMINST_NODEPS)
1866 opts.push_back ("--nodeps");
1867 if (flags & RPMINST_IGNORESIZE)
1868 opts.push_back ("--ignoresize");
1869 if (flags & RPMINST_JUSTDB)
1870 opts.push_back ("--justdb");
1871 if (flags & RPMINST_TEST)
1872 opts.push_back ("--test");
1874 opts.push_back("--");
1876 // rpm requires additional quoting of special chars:
1877 string quotedFilename( rpmQuoteFilename( filename ) );
1878 opts.push_back ( quotedFilename.c_str() );
1880 modifyDatabase(); // BEFORE run_rpm
1881 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1885 vector<string> configwarnings;
1886 vector<string> errorlines;
1888 while (systemReadLine(line))
1890 if (line.substr(0,2)=="%%")
1893 sscanf (line.c_str () + 2, "%d", &percent);
1894 report->progress( percent );
1897 rpmmsg += line+'\n';
1899 if ( line.substr(0,8) == "warning:" )
1901 configwarnings.push_back(line);
1904 int rpm_status = systemStatus();
1907 for (vector<string>::iterator it = configwarnings.begin();
1908 it != configwarnings.end(); ++it)
1910 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1912 _("rpm saved %s as %s but it was impossible to determine the difference"),
1914 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1915 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1917 _("rpm created %s as %s but it was impossible to determine the difference"),
1919 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1922 if ( rpm_status != 0 )
1924 // %s = filename of rpm package
1925 progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1926 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1927 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1931 // %s = filename of rpm package
1932 progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1933 if ( ! rpmmsg.empty() )
1935 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1940 ///////////////////////////////////////////////////////////////////
1943 // METHOD NAME : RpmDb::removePackage
1944 // METHOD TYPE : PMError
1946 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1948 return removePackage( package->name()
1949 + "-" + package->edition().asString()
1950 + "." + package->arch().asString(), flags );
1953 ///////////////////////////////////////////////////////////////////
1956 // METHOD NAME : RpmDb::removePackage
1957 // METHOD TYPE : PMError
1959 void RpmDb::removePackage( const string & name_r, unsigned flags )
1961 callback::SendReport<RpmRemoveReport> report;
1963 report->start( name_r );
1967 doRemovePackage(name_r, flags, report);
1969 catch (RpmException & excpt_r)
1971 report->finish(excpt_r);
1972 ZYPP_RETHROW(excpt_r);
1978 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
1980 FAILIFNOTINITIALIZED;
1981 CommitLog progresslog;
1983 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1986 if ( _packagebackups )
1988 // FIXME solve this status report somehow
1989 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1990 if ( ! backupPackage( name_r ) )
1992 ERR << "backup of " << name_r << " failed" << endl;
1994 report->progress( 0 );
1998 report->progress( 100 );
2003 opts.push_back("-e");
2004 opts.push_back("--allmatches");
2006 if (flags & RPMINST_NOSCRIPTS)
2007 opts.push_back("--noscripts");
2008 if (flags & RPMINST_NODEPS)
2009 opts.push_back("--nodeps");
2010 if (flags & RPMINST_JUSTDB)
2011 opts.push_back("--justdb");
2012 if (flags & RPMINST_TEST)
2013 opts.push_back ("--test");
2014 if (flags & RPMINST_FORCE)
2016 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2019 opts.push_back("--");
2020 opts.push_back(name_r.c_str());
2022 modifyDatabase(); // BEFORE run_rpm
2023 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2028 // got no progress from command, so we fake it:
2029 // 5 - command started
2030 // 50 - command completed
2032 report->progress( 5 );
2033 while (systemReadLine(line))
2035 rpmmsg += line+'\n';
2037 report->progress( 50 );
2038 int rpm_status = systemStatus();
2040 if ( rpm_status != 0 )
2042 // %s = name of rpm package
2043 progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
2044 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
2045 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
2049 progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
2050 if ( ! rpmmsg.empty() )
2052 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
2057 ///////////////////////////////////////////////////////////////////
2060 // METHOD NAME : RpmDb::backupPackage
2061 // METHOD TYPE : bool
2063 bool RpmDb::backupPackage( const Pathname & filename )
2065 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2069 return backupPackage( h->tag_name() );
2072 ///////////////////////////////////////////////////////////////////
2075 // METHOD NAME : RpmDb::backupPackage
2076 // METHOD TYPE : bool
2078 bool RpmDb::backupPackage(const string& packageName)
2080 CommitLog progresslog;
2082 Pathname backupFilename;
2083 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2085 if (_backuppath.empty())
2087 INT << "_backuppath empty" << endl;
2093 if (!queryChangedFiles(fileList, packageName))
2095 ERR << "Error while getting changed files for package " <<
2096 packageName << endl;
2100 if (fileList.size() <= 0)
2102 DBG << "package " << packageName << " not changed -> no backup" << endl;
2106 if (filesystem::assert_dir(_root + _backuppath) != 0)
2112 // build up archive name
2113 time_t currentTime = time(0);
2114 struct tm *currentLocalTime = localtime(¤tTime);
2116 int date = (currentLocalTime->tm_year + 1900) * 10000
2117 + (currentLocalTime->tm_mon + 1) * 100
2118 + currentLocalTime->tm_mday;
2123 backupFilename = _root + _backuppath
2124 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2127 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2129 PathInfo pi(filestobackupfile);
2130 if (pi.isExist() && !pi.isFile())
2132 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2136 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2140 ERR << "could not open " << filestobackupfile.asString() << endl;
2144 for (FileList::const_iterator cit = fileList.begin();
2145 cit != fileList.end(); ++cit)
2148 if ( name[0] == '/' )
2150 // remove slash, file must be relative to -C parameter of tar
2151 name = name.substr( 1 );
2153 DBG << "saving file "<< name << endl;
2158 const char* const argv[] =
2163 _root.asString().c_str(),
2164 "--ignore-failed-read",
2166 backupFilename.asString().c_str(),
2168 filestobackupfile.asString().c_str(),
2172 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2173 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2177 // TODO: its probably possible to start tar with -v and watch it adding
2178 // files to report progress
2179 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2184 int ret = tar.close();
2188 ERR << "tar failed: " << tarmsg << endl;
2193 MIL << "tar backup ok" << endl;
2194 progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
2197 filesystem::unlink(filestobackupfile);
2203 void RpmDb::setBackupPath(const Pathname& path)
2209 } // namespace target