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"
50 using namespace zypp::filesystem;
60 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
61 const char* quoteInFilename_m = "\'\"";
63 const char* quoteInFilename_m = " \t\'\"";
65 inline string rpmQuoteFilename( const Pathname & path_r )
67 string path( path_r.asString() );
68 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
70 pos = path.find_first_of( quoteInFilename_m, pos ) )
72 path.insert( pos, "\\" );
73 pos += 2; // skip '\\' and the quoted char.
79 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
81 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
86 ~KeyRingSignalReceiver()
91 virtual void trustedKeyAdded( const PublicKey &key )
93 MIL << "trusted key added to zypp Keyring. Importing" << endl;
94 // now import the key in rpm
97 _rpmdb.importPubkey( key );
99 catch (RpmException &e)
101 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
105 virtual void trustedKeyRemoved( const PublicKey &key )
107 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
109 // remove the key from rpm
112 _rpmdb.removePubkey( key );
114 catch (RpmException &e)
116 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
123 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
125 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
135 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
142 for (line = prog.receiveLine(), count=0;
144 line = prog.receiveLine(), count++ )
146 if (maxlines<0?true:count<maxlines)
155 /******************************************************************
158 ** FUNCTION NAME : stringPath
159 ** FUNCTION TYPE : inline string
161 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
163 return librpmDb::stringPath( root_r, sub_r );
166 /******************************************************************
169 ** FUNCTION NAME : operator<<
170 ** FUNCTION TYPE : ostream &
172 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
174 if ( obj == RpmDb::DbSI_NO_INIT )
180 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
182 ENUM_OUT( DbSI_HAVE_V4, 'X' );
183 ENUM_OUT( DbSI_MADE_V4, 'c' );
184 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
186 ENUM_OUT( DbSI_HAVE_V3, 'X' );
187 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
188 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
195 ///////////////////////////////////////////////////////////////////
196 // CLASS NAME : RpmDbPtr
197 // CLASS NAME : RpmDbconstPtr
198 ///////////////////////////////////////////////////////////////////
200 #define WARNINGMAILPATH "/var/log/YaST2/"
201 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
203 ///////////////////////////////////////////////////////////////////
205 // CLASS NAME : RpmDb::Packages
207 * Helper class for RpmDb::getPackages() to build the
208 * list<Package::Ptr> returned. We have to assert, that there
209 * is a unique entry for every string.
211 * In the first step we build the _list list which contains all
212 * packages (even those which are contained in multiple versions).
214 * At the end buildIndex() is called to build the _index is created
215 * and points to the last installed versions of all packages.
216 * Operations changing the rpmdb
217 * content (install/remove package) should set _valid to false. The
218 * next call to RpmDb::getPackages() will then reread the the rpmdb.
220 * Note that outside RpmDb::getPackages() _list and _index are always
221 * in sync. So you may use lookup(PkgName) to retrieve a specific
224 class RpmDb::Packages
227 list<Package::Ptr> _list;
228 map<string,Package::Ptr> _index;
230 Packages() : _valid( false )
238 Package::Ptr lookup( const string & name_r ) const
240 map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
241 if ( got != _index.end() )
243 return Package::Ptr();
248 for ( list<Package::Ptr>::iterator iter = _list.begin();
249 iter != _list.end(); ++iter )
251 string name = (*iter)->name();
252 Package::Ptr & nptr = _index[name]; // be shure to get a reference!
256 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
257 if ( nptr->installtime() > (*iter)->installtime() )
271 ///////////////////////////////////////////////////////////////////
273 ///////////////////////////////////////////////////////////////////
275 // CLASS NAME : RpmDb
277 ///////////////////////////////////////////////////////////////////
279 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
281 ///////////////////////////////////////////////////////////////////
283 ///////////////////////////////////////////////////////////////////
286 // METHOD NAME : RpmDb::RpmDb
287 // METHOD TYPE : Constructor
290 : _dbStateInfo( DbSI_NO_INIT )
291 , _packages( * new Packages ) // delete in destructor
292 #warning Check for obsolete memebers
293 , _backuppath ("/var/adm/backup")
294 , _packagebackups(false)
295 , _warndirexists(false)
300 // Some rpm versions are patched not to abort installation if
301 // symlink creation failed.
302 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
303 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
306 ///////////////////////////////////////////////////////////////////
309 // METHOD NAME : RpmDb::~RpmDb
310 // METHOD TYPE : Destructor
314 MIL << "~RpmDb()" << endl;
319 MIL << "~RpmDb() end" << endl;
320 sKeyRingReceiver.reset();
323 Date RpmDb::timestamp() const
328 if ( dbPath().empty() )
329 db_path = "/var/lib/rpm";
333 PathInfo rpmdb_info(root() + db_path + "/Packages");
335 if ( rpmdb_info.isExist() )
336 return rpmdb_info.mtime();
340 ///////////////////////////////////////////////////////////////////
343 // METHOD NAME : RpmDb::dumpOn
344 // METHOD TYPE : ostream &
346 ostream & RpmDb::dumpOn( ostream & str ) const
350 if ( _dbStateInfo == DbSI_NO_INIT )
356 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
358 ENUM_OUT( DbSI_HAVE_V4, 'X' );
359 ENUM_OUT( DbSI_MADE_V4, 'c' );
360 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
362 ENUM_OUT( DbSI_HAVE_V3, 'X' );
363 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
364 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
365 str << "): " << stringPath( _root, _dbPath );
371 ///////////////////////////////////////////////////////////////////
374 // METHOD NAME : RpmDb::initDatabase
375 // METHOD TYPE : PMError
377 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
379 ///////////////////////////////////////////////////////////////////
381 ///////////////////////////////////////////////////////////////////
382 if ( root_r.empty() )
385 if ( dbPath_r.empty() )
386 dbPath_r = "/var/lib/rpm";
388 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
390 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
391 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
394 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
395 << ( doRebuild_r ? " (rebuilddb)" : "" ) << endl;
397 ///////////////////////////////////////////////////////////////////
398 // Check whether already initialized
399 ///////////////////////////////////////////////////////////////////
402 if ( root_r == _root && dbPath_r == _dbPath )
408 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
412 ///////////////////////////////////////////////////////////////////
414 ///////////////////////////////////////////////////////////////////
415 librpmDb::unblockAccess();
416 DbStateInfoBits info = DbSI_NO_INIT;
419 internal_initDatabase( root_r, dbPath_r, info );
421 catch (const RpmException & excpt_r)
423 ZYPP_CAUGHT(excpt_r);
424 librpmDb::blockAccess();
425 ERR << "Cleanup on error: state " << info << endl;
427 if ( dbsi_has( info, DbSI_MADE_V4 ) )
429 // remove the newly created rpm4 database and
430 // any backup created on conversion.
431 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
433 ZYPP_RETHROW(excpt_r);
435 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
437 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
439 // Move obsolete rpm3 database beside.
440 MIL << "Cleanup: state " << info << endl;
441 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
442 dbsi_clr( info, DbSI_HAVE_V3 );
446 // Performing an update: Keep the original rpm3 database
447 // and wait if the rpm4 database gets modified by installing
448 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
449 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
452 #warning CHECK: notify root about conversion backup.
460 if ( dbsi_has( info, DbSI_HAVE_V4 )
461 && ! dbsi_has( info, DbSI_MADE_V4 ) )
467 MIL << "Syncronizing keys with zypp keyring" << endl;
468 // we do this one by one now.
469 importZyppKeyRingTrustedKeys();
470 exportTrustedKeysInZyppKeyRing();
472 // Close the database in case any write acces (create/convert)
473 // happened during init. This should drop any lock acquired
474 // by librpm. On demand it will be reopened readonly and should
475 // not hold any lock.
476 librpmDb::dbRelease( true );
478 MIL << "InitDatabase: " << *this << endl;
481 ///////////////////////////////////////////////////////////////////
484 // METHOD NAME : RpmDb::internal_initDatabase
485 // METHOD TYPE : PMError
487 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
488 DbStateInfoBits & info_r )
490 info_r = DbSI_NO_INIT;
492 ///////////////////////////////////////////////////////////////////
493 // Get info about the desired database dir
494 ///////////////////////////////////////////////////////////////////
495 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
497 if ( dbInfo.illegalArgs() )
499 // should not happen (checked in initDatabase)
500 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
502 if ( ! dbInfo.usableArgs() )
504 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
505 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
508 if ( dbInfo.hasDbV4() )
510 dbsi_set( info_r, DbSI_HAVE_V4 );
511 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
515 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
518 if ( dbInfo.hasDbV3() )
520 dbsi_set( info_r, DbSI_HAVE_V3 );
522 if ( dbInfo.hasDbV3ToV4() )
524 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
527 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
528 librpmDb::dumpState( DBG ) << endl;
530 ///////////////////////////////////////////////////////////////////
531 // Access database, create if needed
532 ///////////////////////////////////////////////////////////////////
534 // creates dbdir and empty rpm4 database if not present
535 librpmDb::dbAccess( root_r, dbPath_r );
537 if ( ! dbInfo.hasDbV4() )
540 if ( dbInfo.hasDbV4() )
542 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
546 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
547 librpmDb::dumpState( DBG ) << endl;
549 ///////////////////////////////////////////////////////////////////
550 // Check whether to convert something. Create backup but do
551 // not remove anything here
552 ///////////////////////////////////////////////////////////////////
553 librpmDb::constPtr dbptr;
554 librpmDb::dbAccess( dbptr );
555 bool dbEmpty = dbptr->empty();
558 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
561 if ( dbInfo.hasDbV3() )
563 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
567 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
568 convertV3toV4( dbInfo.dbV3().path(), dbptr );
570 // create a backup copy
571 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
574 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
579 if ( dbInfo.hasDbV3ToV4() )
581 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
582 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
590 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
591 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
592 dbsi_set( info_r, DbSI_MODIFIED_V4 );
596 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
597 librpmDb::dumpState( DBG ) << endl;
600 if ( dbInfo.hasDbV3ToV4() )
602 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
606 ///////////////////////////////////////////////////////////////////
609 // METHOD NAME : RpmDb::removeV4
610 // METHOD TYPE : void
612 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
614 const char * v3backup = "packages.rpm3";
615 const char * master = "Packages";
616 const char * index[] =
638 PathInfo pi( dbdir_r );
641 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
645 for ( const char ** f = index; *f; ++f )
650 filesystem::unlink( pi.path() );
654 pi( dbdir_r + master );
657 MIL << "Removing rpm4 database " << pi << endl;
658 filesystem::unlink( pi.path() );
663 pi( dbdir_r + v3backup );
666 MIL << "Removing converted rpm3 database backup " << pi << endl;
667 filesystem::unlink( pi.path() );
672 ///////////////////////////////////////////////////////////////////
675 // METHOD NAME : RpmDb::removeV3
676 // METHOD TYPE : void
678 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
680 const char * master = "packages.rpm";
681 const char * index[] =
683 "conflictsindex.rpm",
694 PathInfo pi( dbdir_r );
697 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
701 for ( const char ** f = index; *f; ++f )
706 filesystem::unlink( pi.path() );
710 #warning CHECK: compare vs existing v3 backup. notify root
711 pi( dbdir_r + master );
714 Pathname m( pi.path() );
717 // backup was already created
718 filesystem::unlink( m );
719 Pathname b( m.extend( "3" ) );
720 pi( b ); // stat backup
724 Pathname b( m.extend( ".deleted" ) );
728 // rempve existing backup
729 filesystem::unlink( b );
731 filesystem::rename( m, b );
732 pi( b ); // stat backup
734 MIL << "(Re)moved rpm3 database to " << pi << endl;
738 ///////////////////////////////////////////////////////////////////
741 // METHOD NAME : RpmDb::modifyDatabase
742 // METHOD TYPE : void
744 void RpmDb::modifyDatabase()
746 if ( ! initialized() )
749 // tag database as modified
750 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
752 // Move outdated rpm3 database beside.
753 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
755 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
756 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
757 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
760 // invalidate Packages list
761 _packages._valid = false;
764 ///////////////////////////////////////////////////////////////////
767 // METHOD NAME : RpmDb::closeDatabase
768 // METHOD TYPE : PMError
770 void RpmDb::closeDatabase()
772 if ( ! initialized() )
777 MIL << "Calling closeDatabase: " << *this << endl;
779 ///////////////////////////////////////////////////////////////////
780 // Block further database access
781 ///////////////////////////////////////////////////////////////////
783 librpmDb::blockAccess();
785 ///////////////////////////////////////////////////////////////////
786 // Check fate if old version database still present
787 ///////////////////////////////////////////////////////////////////
788 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
790 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
791 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
793 // Move outdated rpm3 database beside.
794 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
798 // Remove unmodified rpm4 database
799 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
803 ///////////////////////////////////////////////////////////////////
805 ///////////////////////////////////////////////////////////////////
806 _root = _dbPath = Pathname();
807 _dbStateInfo = DbSI_NO_INIT;
809 MIL << "closeDatabase: " << *this << endl;
812 ///////////////////////////////////////////////////////////////////
815 // METHOD NAME : RpmDb::rebuildDatabase
816 // METHOD TYPE : PMError
818 void RpmDb::rebuildDatabase()
820 callback::SendReport<RebuildDBReport> report;
822 report->start( root() + dbPath() );
826 doRebuildDatabase(report);
828 catch (RpmException & excpt_r)
830 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
831 ZYPP_RETHROW(excpt_r);
833 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
836 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
838 FAILIFNOTINITIALIZED;
840 MIL << "RpmDb::rebuildDatabase" << *this << endl;
841 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
843 PathInfo dbMaster( root() + dbPath() + "Packages" );
844 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
848 opts.push_back("--rebuilddb");
849 opts.push_back("-vv");
851 // don't call modifyDatabase because it would remove the old
852 // rpm3 database, if the current database is a temporary one.
853 // But do invalidate packages list.
854 _packages._valid = false;
855 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
857 // progress report: watch this file growing
858 PathInfo newMaster( root()
859 + dbPath().extend( str::form( "rebuilddb.%d",
860 process?process->getpid():0) )
866 while ( systemReadLine( line ) )
869 { // file is removed at the end of rebuild.
870 // current size should be upper limit for new db
871 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
873 WAR << "User requested abort." << endl;
875 filesystem::recursive_rmdir( newMaster.path().dirname() );
879 if ( line.compare( 0, 2, "D:" ) )
881 errmsg += line + '\n';
882 // report.notify( line );
887 int rpm_status = systemStatus();
889 if ( rpm_status != 0 )
891 //TranslatorExplanation after semicolon is error message
892 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
893 (errmsg.empty() ? error_message: errmsg))));
897 report->progress( 100, root() + dbPath() ); // 100%
901 void RpmDb::importZyppKeyRingTrustedKeys()
903 MIL << "Importing zypp trusted keyring" << std::endl;
905 std::list<PublicKey> zypp_keys;
906 zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
907 /* The pubkeys() call below is expensive. It calls gpg2 for each
908 gpg-pubkey in the rpm db. Useless if we don't have any keys in
910 if (zypp_keys.empty())
913 std::list<PublicKey> rpm_keys = pubkeys();
915 for ( std::list<PublicKey>::const_iterator it = zypp_keys.begin(); it != zypp_keys.end(); ++it)
917 // we find only the left part of the long gpg key, as rpm does not support long ids
918 std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
919 if ( ik != rpm_keys.end() )
921 MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
925 // now import the key in rpm
928 importPubkey((*it).path());
929 MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
931 catch (RpmException &e)
933 ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
939 void RpmDb::exportTrustedKeysInZyppKeyRing()
941 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
943 set<Edition> rpm_keys = pubkeyEditions();
945 list<PublicKey> zypp_keys;
946 zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
948 for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
950 // search the zypp key into the rpm keys
951 // long id is edition version + release
952 string id = str::toUpper( (*it).version() + (*it).release());
953 list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
954 if ( ik != zypp_keys.end() )
956 MIL << "Key " << (*it) << " is already in zypp database." << endl;
960 // we export the rpm key into a file
961 RpmHeader::constPtr result = new RpmHeader();
962 getData( string("gpg-pubkey"), *it, result );
963 TmpFile file(getZYpp()->tmpPath());
967 os.open(file.path().asString().c_str());
968 // dump rpm key into the tmp file
969 os << result->tag_description();
970 //MIL << "-----------------------------------------------" << endl;
971 //MIL << result->tag_description() <<endl;
972 //MIL << "-----------------------------------------------" << endl;
977 ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << endl;
978 // just ignore the key
981 // now import the key in zypp
984 getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
985 MIL << "Trusted key " << (*it) << " imported in zypp keyring." << endl;
989 ERR << "Could not import key " << (*it) << " in zypp keyring" << endl;
995 ///////////////////////////////////////////////////////////////////
998 // METHOD NAME : RpmDb::importPubkey
999 // METHOD TYPE : PMError
1001 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1003 FAILIFNOTINITIALIZED;
1005 // check if the key is already in the rpm database and just
1006 // return if it does.
1007 set<Edition> rpm_keys = pubkeyEditions();
1008 string keyshortid = pubkey_r.id().substr(8,8);
1009 MIL << "Comparing '" << keyshortid << "' to: ";
1010 for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
1012 string id = str::toUpper( (*it).version() );
1013 MIL << ", '" << id << "'";
1014 if ( id == keyshortid )
1017 // now check if timestamp is different
1018 Date date = Date(str::strtonum<Date::ValueType>("0x" + (*it).release()));
1019 if ( date == pubkey_r.created() )
1022 MIL << endl << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
1027 MIL << endl << "Key " << pubkey_r << " has another version in keyring. ( " << date << " & " << pubkey_r.created() << ")" << endl;
1033 // key does not exists, lets import it
1037 opts.push_back ( "--import" );
1038 opts.push_back ( "--" );
1039 opts.push_back ( pubkey_r.path().asString().c_str() );
1041 // don't call modifyDatabase because it would remove the old
1042 // rpm3 database, if the current database is a temporary one.
1043 // But do invalidate packages list.
1044 _packages._valid = false;
1045 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1048 while ( systemReadLine( line ) )
1050 if ( line.substr( 0, 6 ) == "error:" )
1052 WAR << line << endl;
1056 DBG << line << endl;
1060 int rpm_status = systemStatus();
1062 if ( rpm_status != 0 )
1064 //TranslatorExplanation first %s is file name, second is error message
1065 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1066 _("Failed to import public key from file %s: %s"))
1067 % pubkey_r.asString() % error_message)));
1071 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1075 ///////////////////////////////////////////////////////////////////
1078 // METHOD NAME : RpmDb::removePubkey
1079 // METHOD TYPE : PMError
1081 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1083 FAILIFNOTINITIALIZED;
1085 // check if the key is in the rpm database and just
1086 // return if it does not.
1087 set<Edition> rpm_keys = pubkeyEditions();
1090 set<Edition>::const_iterator found_edition = rpm_keys.end();
1092 for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
1094 string id = str::toUpper( (*it).version() );
1095 string keyshortid = pubkey_r.id().substr(8,8);
1096 MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
1097 if ( id == keyshortid )
1104 // the key does not exist, cannot be removed
1105 if (found_edition == rpm_keys.end())
1107 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1111 string rpm_name("gpg-pubkey-" + found_edition->asString());
1114 opts.push_back ( "-e" );
1115 opts.push_back ( "--" );
1116 opts.push_back ( rpm_name.c_str() );
1118 // don't call modifyDatabase because it would remove the old
1119 // rpm3 database, if the current database is a temporary one.
1120 // But do invalidate packages list.
1121 _packages._valid = false;
1122 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1125 while ( systemReadLine( line ) )
1127 if ( line.substr( 0, 6 ) == "error:" )
1129 WAR << line << endl;
1133 DBG << line << endl;
1137 int rpm_status = systemStatus();
1139 if ( rpm_status != 0 )
1141 //TranslatorExplanation first %s is key name, second is error message
1142 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1143 _("Failed to remove public key %s: %s")) % pubkey_r.asString()
1148 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1152 ///////////////////////////////////////////////////////////////////
1155 // METHOD NAME : RpmDb::pubkeys
1156 // METHOD TYPE : set<Edition>
1158 list<PublicKey> RpmDb::pubkeys() const
1160 list<PublicKey> ret;
1162 librpmDb::db_const_iterator it;
1163 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1165 Edition edition = it->tag_edition();
1166 if (edition != Edition::noedition)
1168 // we export the rpm key into a file
1169 RpmHeader::constPtr result = new RpmHeader();
1170 getData( string("gpg-pubkey"), edition, result );
1171 TmpFile file(getZYpp()->tmpPath());
1175 os.open(file.path().asString().c_str());
1176 // dump rpm key into the tmp file
1177 os << result->tag_description();
1178 //MIL << "-----------------------------------------------" << endl;
1179 //MIL << result->tag_description() <<endl;
1180 //MIL << "-----------------------------------------------" << endl;
1182 // read the public key from the dumped file
1183 PublicKey key(file.path());
1186 catch (exception &e)
1188 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1189 // just ignore the key
1196 set<Edition> RpmDb::pubkeyEditions() const
1200 librpmDb::db_const_iterator it;
1201 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1203 Edition edition = it->tag_edition();
1204 if (edition != Edition::noedition)
1205 ret.insert( edition );
1210 ///////////////////////////////////////////////////////////////////
1213 // METHOD NAME : RpmDb::packagesValid
1214 // METHOD TYPE : bool
1216 bool RpmDb::packagesValid() const
1218 return( _packages._valid || ! initialized() );
1221 ///////////////////////////////////////////////////////////////////
1224 // METHOD NAME : RpmDb::getPackages
1225 // METHOD TYPE : const list<Package::Ptr> &
1229 const list<Package::Ptr> & RpmDb::getPackages()
1231 callback::SendReport<ScanDBReport> report;
1237 const list<Package::Ptr> & ret = doGetPackages(report);
1238 report->finish(ScanDBReport::NO_ERROR, "");
1241 catch (RpmException & excpt_r)
1243 report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
1244 ZYPP_RETHROW(excpt_r);
1247 static const list<Package::Ptr> empty_list;
1251 #warning FIX READING RPM DATBASE TO POOL
1252 #if 0 // obsolete helper
1253 inline static void insertCaps( Capabilities &capset, capability::CapabilityImplPtrSet ptrset, CapFactory &factory )
1255 for ( capability::CapabilityImplPtrSet::const_iterator it = ptrset.begin();
1259 capset.insert( factory.fromImpl(*it) );
1265 // make Package::Ptr from RpmHeader
1266 // return NULL on error
1268 Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header,
1269 set<string> * filerequires,
1270 const Pathname & location,
1276 if ( header->isSrc() )
1278 WAR << "Can't make Package from SourcePackage header" << endl;
1283 #warning FIX READING RPM DATBASE TO POOL
1285 string name = header->tag_name();
1287 // create dataprovider
1288 detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
1290 impl->setRepository( repo );
1291 if (!location.empty())
1292 impl->setLocation( OnMediaLocation(location,1) );
1297 edition = Edition( header->tag_version(),
1298 header->tag_release(),
1299 header->tag_epoch());
1301 catch (Exception & excpt_r)
1303 ZYPP_CAUGHT( excpt_r );
1304 WAR << "Package " << name << " has bad edition '"
1305 << (header->tag_epoch()==0?"":(header->tag_epoch()+":"))
1306 << header->tag_version()
1307 << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
1314 arch = Arch( header->tag_arch() );
1316 catch (Exception & excpt_r)
1318 ZYPP_CAUGHT( excpt_r );
1319 WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
1323 // Collect basic Resolvable data
1324 NVRAD dataCollect( header->tag_name(),
1328 list<string> filenames = impl->filenames();
1329 CapFactory capfactory;
1330 insertCaps( dataCollect[Dep::PROVIDES], header->tag_provides( filerequires ), capfactory );
1332 for (list<string>::const_iterator filename = filenames.begin();
1333 filename != filenames.end();
1336 if ( capability::isInterestingFileSpec( *filename ) )
1340 dataCollect[Dep::PROVIDES].insert(capfactory.fromImpl(capability::buildFile(ResKind::package, *filename) ));
1342 catch (Exception & excpt_r)
1344 ZYPP_CAUGHT( excpt_r );
1345 WAR << "Ignoring invalid capability: " << *filename << endl;
1350 insertCaps( dataCollect[Dep::REQUIRES], header->tag_requires( filerequires ), capfactory );
1351 insertCaps( dataCollect[Dep::PREREQUIRES], header->tag_prerequires( filerequires ), capfactory );
1352 insertCaps( dataCollect[Dep::CONFLICTS], header->tag_conflicts( filerequires ), capfactory );
1353 insertCaps( dataCollect[Dep::OBSOLETES], header->tag_obsoletes( filerequires ), capfactory );
1354 insertCaps( dataCollect[Dep::ENHANCES], header->tag_enhances( filerequires ), capfactory );
1355 insertCaps( dataCollect[Dep::SUPPLEMENTS], header->tag_supplements( filerequires ), capfactory );
1359 // create package from dataprovider
1360 pptr = detail::makeResolvableFromImpl( dataCollect, impl );
1362 catch (Exception & excpt_r)
1364 ZYPP_CAUGHT( excpt_r );
1365 ERR << "Can't create Package::Ptr" << endl;
1371 const list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
1373 if ( packagesValid() )
1375 return _packages._list;
1380 ///////////////////////////////////////////////////////////////////
1381 // Collect package data.
1382 ///////////////////////////////////////////////////////////////////
1383 unsigned expect = 0;
1384 librpmDb::constPtr dbptr;
1385 librpmDb::dbAccess( dbptr );
1386 expect = dbptr->size();
1387 DBG << "Expecting " << expect << " packages" << endl;
1389 librpmDb::db_const_iterator iter;
1390 unsigned current = 0;
1393 for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
1396 string name = iter->tag_name();
1397 if ( name == string( "gpg-pubkey" ) )
1399 DBG << "Ignoring pseudo package " << name << endl;
1400 // pseudo package filtered, as we can't handle multiple instances
1401 // of 'gpg-pubkey-VERS-REL'.
1405 Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Repository() );
1408 WAR << "Failed to make package from database header '" << name << "'" << endl;
1412 _packages._list.push_back( pptr );
1414 _packages.buildIndex();
1415 DBG << "Found installed packages: " << _packages._list.size() << endl;
1417 #warning FILEREQUIRES HACK SHOULD BE DONE WHEN WRITING THE RPMDB SOLV FILE
1419 ///////////////////////////////////////////////////////////////////
1420 // Evaluate filerequires collected so far
1421 ///////////////////////////////////////////////////////////////////
1422 for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
1425 for ( iter.findByFile( *it ); *iter; ++iter )
1427 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1430 WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1433 pptr->injectProvides(_f.parse(ResKind::package, *it));
1438 ///////////////////////////////////////////////////////////////////
1439 // Build final packages list
1440 ///////////////////////////////////////////////////////////////////
1441 return _packages._list;
1444 ///////////////////////////////////////////////////////////////////
1447 // METHOD NAME : RpmDb::fileList
1448 // METHOD TYPE : bool
1453 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1455 list<FileInfo> result;
1457 librpmDb::db_const_iterator it;
1459 if (edition_r == Edition::noedition)
1461 found = it.findPackage( name_r );
1465 found = it.findPackage( name_r, edition_r );
1474 ///////////////////////////////////////////////////////////////////
1477 // METHOD NAME : RpmDb::hasFile
1478 // METHOD TYPE : bool
1482 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1484 librpmDb::db_const_iterator it;
1488 res = it.findByFile( file_r );
1490 if (!name_r.empty())
1492 res = (it->tag_name() == name_r);
1500 ///////////////////////////////////////////////////////////////////
1503 // METHOD NAME : RpmDb::whoOwnsFile
1504 // METHOD TYPE : string
1508 string RpmDb::whoOwnsFile( const string & file_r) const
1510 librpmDb::db_const_iterator it;
1511 if (it.findByFile( file_r ))
1513 return it->tag_name();
1518 ///////////////////////////////////////////////////////////////////
1521 // METHOD NAME : RpmDb::hasProvides
1522 // METHOD TYPE : bool
1526 bool RpmDb::hasProvides( const string & tag_r ) const
1528 librpmDb::db_const_iterator it;
1529 return it.findByProvides( tag_r );
1532 ///////////////////////////////////////////////////////////////////
1535 // METHOD NAME : RpmDb::hasRequiredBy
1536 // METHOD TYPE : bool
1540 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1542 librpmDb::db_const_iterator it;
1543 return it.findByRequiredBy( tag_r );
1546 ///////////////////////////////////////////////////////////////////
1549 // METHOD NAME : RpmDb::hasConflicts
1550 // METHOD TYPE : bool
1554 bool RpmDb::hasConflicts( const string & tag_r ) const
1556 librpmDb::db_const_iterator it;
1557 return it.findByConflicts( tag_r );
1560 ///////////////////////////////////////////////////////////////////
1563 // METHOD NAME : RpmDb::hasPackage
1564 // METHOD TYPE : bool
1568 bool RpmDb::hasPackage( const string & name_r ) const
1570 librpmDb::db_const_iterator it;
1571 return it.findPackage( name_r );
1574 ///////////////////////////////////////////////////////////////////
1577 // METHOD NAME : RpmDb::hasPackage
1578 // METHOD TYPE : bool
1582 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1584 librpmDb::db_const_iterator it;
1585 return it.findPackage( name_r, ed_r );
1588 ///////////////////////////////////////////////////////////////////
1591 // METHOD NAME : RpmDb::getData
1592 // METHOD TYPE : PMError
1596 void RpmDb::getData( const string & name_r,
1597 RpmHeader::constPtr & result_r ) const
1599 librpmDb::db_const_iterator it;
1600 it.findPackage( name_r );
1603 ZYPP_THROW(*(it.dbError()));
1606 ///////////////////////////////////////////////////////////////////
1609 // METHOD NAME : RpmDb::getData
1610 // METHOD TYPE : void
1614 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1615 RpmHeader::constPtr & result_r ) const
1617 librpmDb::db_const_iterator it;
1618 it.findPackage( name_r, ed_r );
1621 ZYPP_THROW(*(it.dbError()));
1624 ///////////////////////////////////////////////////////////////////
1626 // METHOD NAME : RpmDb::checkPackage
1627 // METHOD TYPE : RpmDb::checkPackageResult
1629 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
1631 PathInfo file( path_r );
1632 if ( ! file.isFile() )
1634 ERR << "Not a file: " << file << endl;
1638 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1639 if ( fd == 0 || ::Ferror(fd) )
1641 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1647 rpmts ts = ::rpmtsCreate();
1648 ::rpmtsSetRootDir( ts, root().asString().c_str() );
1649 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1650 int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
1651 ts = ::rpmtsFree(ts);
1660 case RPMRC_NOTFOUND:
1661 WAR << "Signature is unknown type. " << file << endl;
1662 return CHK_NOTFOUND;
1665 WAR << "Signature does not verify. " << file << endl;
1668 case RPMRC_NOTTRUSTED:
1669 WAR << "Signature is OK, but key is not trusted. " << file << endl;
1670 return CHK_NOTTRUSTED;
1673 WAR << "Public key is unavailable. " << file << endl;
1677 ERR << "Error reading header." << file << endl;
1681 // determine changed files of installed package
1683 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1689 if ( ! initialized() ) return false;
1693 opts.push_back ("-V");
1694 opts.push_back ("--nodeps");
1695 opts.push_back ("--noscripts");
1696 opts.push_back ("--nomd5");
1697 opts.push_back ("--");
1698 opts.push_back (packageName.c_str());
1700 run_rpm (opts, ExternalProgram::Discard_Stderr);
1702 if ( process == NULL )
1713 M Mode (includes permissions and file type)
1717 while (systemReadLine(line))
1719 if (line.length() > 12 &&
1720 (line[0] == 'S' || line[0] == 's' ||
1721 (line[0] == '.' && line[7] == 'T')))
1723 // file has been changed
1726 filename.assign(line, 11, line.length() - 11);
1727 fileList.insert(filename);
1732 // exit code ignored, rpm returns 1 no matter if package is installed or
1740 /****************************************************************/
1741 /* private member-functions */
1742 /****************************************************************/
1744 /*--------------------------------------------------------------*/
1745 /* Run rpm with the specified arguments, handling stderr */
1746 /* as specified by disp */
1747 /*--------------------------------------------------------------*/
1749 RpmDb::run_rpm (const RpmArgVec& opts,
1750 ExternalProgram::Stderr_Disposition disp)
1759 if ( ! initialized() )
1761 ZYPP_THROW(RpmDbNotOpenException());
1766 // always set root and dbpath
1767 args.push_back("rpm");
1768 args.push_back("--root");
1769 args.push_back(_root.asString().c_str());
1770 args.push_back("--dbpath");
1771 args.push_back(_dbPath.asString().c_str());
1773 const char* argv[args.size() + opts.size() + 1];
1775 const char** p = argv;
1776 p = copy (args.begin (), args.end (), p);
1777 p = copy (opts.begin (), opts.end (), p);
1780 // Invalidate all outstanding database handles in case
1781 // the database gets modified.
1782 librpmDb::dbRelease( true );
1784 // Launch the program with default locale
1785 process = new ExternalProgram(argv, disp, false, -1, true);
1789 /*--------------------------------------------------------------*/
1790 /* Read a line from the rpm process */
1791 /*--------------------------------------------------------------*/
1793 RpmDb::systemReadLine(string &line)
1797 if ( process == NULL )
1800 line = process->receiveLine();
1802 if (line.length() == 0)
1805 if (line[line.length() - 1] == '\n')
1806 line.erase(line.length() - 1);
1811 /*--------------------------------------------------------------*/
1812 /* Return the exit status of the rpm process, closing the */
1813 /* connection if not already done */
1814 /*--------------------------------------------------------------*/
1816 RpmDb::systemStatus()
1818 if ( process == NULL )
1821 exit_code = process->close();
1825 error_message = process->execError();
1830 // DBG << "exit code " << exit_code << endl;
1835 /*--------------------------------------------------------------*/
1836 /* Forcably kill the rpm process */
1837 /*--------------------------------------------------------------*/
1841 if (process) process->kill();
1845 // generate diff mails for config files
1846 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1848 string msg = line.substr(9);
1849 string::size_type pos1 = string::npos;
1850 string::size_type pos2 = string::npos;
1851 string file1s, file2s;
1855 pos1 = msg.find (typemsg);
1858 if ( pos1 == string::npos )
1861 pos2 = pos1 + strlen (typemsg);
1863 if (pos2 >= msg.length() )
1866 file1 = msg.substr (0, pos1);
1867 file2 = msg.substr (pos2);
1869 file1s = file1.asString();
1870 file2s = file2.asString();
1872 if (!_root.empty() && _root != "/")
1874 file1 = _root + file1;
1875 file2 = _root + file2;
1879 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1882 Pathname file = _root + WARNINGMAILPATH;
1883 if (filesystem::assert_dir(file) != 0)
1885 ERR << "Could not create " << file.asString() << endl;
1888 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1889 ofstream notify(file.asString().c_str(), ios::out|ios::app);
1892 ERR << "Could not open " << file << endl;
1896 // Translator: %s = name of an rpm package. A list of diffs follows
1898 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1901 ERR << "diff failed" << endl;
1902 notify << str::form(difffailmsg,
1903 file1s.c_str(), file2s.c_str()) << endl;
1907 notify << str::form(diffgenmsg,
1908 file1s.c_str(), file2s.c_str()) << endl;
1910 // remove root for the viewer's pleasure (#38240)
1911 if (!_root.empty() && _root != "/")
1913 if (out.substr(0,4) == "--- ")
1915 out.replace(4, file1.asString().length(), file1s);
1917 string::size_type pos = out.find("\n+++ ");
1918 if (pos != string::npos)
1920 out.replace(pos+5, file2.asString().length(), file2s);
1923 notify << out << endl;
1926 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1931 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1937 ///////////////////////////////////////////////////////////////////
1940 // METHOD NAME : RpmDb::installPackage
1941 // METHOD TYPE : PMError
1943 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1945 callback::SendReport<RpmInstallReport> report;
1947 report->start(filename);
1952 doInstallPackage(filename, flags, report);
1956 catch (RpmException & excpt_r)
1958 RpmInstallReport::Action user = report->problem( excpt_r );
1960 if ( user == RpmInstallReport::ABORT )
1962 report->finish( excpt_r );
1963 ZYPP_RETHROW(excpt_r);
1965 else if ( user == RpmInstallReport::IGNORE )
1973 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1975 FAILIFNOTINITIALIZED;
1976 HistoryLog historylog;
1978 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1982 if ( _packagebackups )
1984 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1985 if ( ! backupPackage( filename ) )
1987 ERR << "backup of " << filename.asString() << " failed" << endl;
1989 // FIXME status handling
1990 report->progress( 0 ); // allow 1% for backup creation.
1994 report->progress( 100 );
1999 if (flags & RPMINST_NOUPGRADE)
2000 opts.push_back("-i");
2002 opts.push_back("-U");
2003 opts.push_back("--percent");
2005 if (flags & RPMINST_NODIGEST)
2006 opts.push_back("--nodigest");
2007 if (flags & RPMINST_NOSIGNATURE)
2008 opts.push_back("--nosignature");
2009 if (flags & RPMINST_EXCLUDEDOCS)
2010 opts.push_back ("--excludedocs");
2011 if (flags & RPMINST_NOSCRIPTS)
2012 opts.push_back ("--noscripts");
2013 if (flags & RPMINST_FORCE)
2014 opts.push_back ("--force");
2015 if (flags & RPMINST_NODEPS)
2016 opts.push_back ("--nodeps");
2017 if (flags & RPMINST_IGNORESIZE)
2018 opts.push_back ("--ignoresize");
2019 if (flags & RPMINST_JUSTDB)
2020 opts.push_back ("--justdb");
2021 if (flags & RPMINST_TEST)
2022 opts.push_back ("--test");
2024 opts.push_back("--");
2026 // rpm requires additional quoting of special chars:
2027 string quotedFilename( rpmQuoteFilename( filename ) );
2028 opts.push_back ( quotedFilename.c_str() );
2030 modifyDatabase(); // BEFORE run_rpm
2031 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
2035 vector<string> configwarnings;
2036 vector<string> errorlines;
2038 while (systemReadLine(line))
2040 if (line.substr(0,2)=="%%")
2043 sscanf (line.c_str () + 2, "%d", &percent);
2044 report->progress( percent );
2047 rpmmsg += line+'\n';
2049 if ( line.substr(0,8) == "warning:" )
2051 configwarnings.push_back(line);
2054 int rpm_status = systemStatus();
2057 for (vector<string>::iterator it = configwarnings.begin();
2058 it != configwarnings.end(); ++it)
2060 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2062 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2064 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2065 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2067 _("rpm created %s as %s, but it was impossible to determine the difference"),
2069 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2072 if ( rpm_status != 0 )
2074 // %s = filename of rpm package
2075 // historylog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
2077 sstr << _("rpm output:") << endl << rpmmsg << endl;
2078 historylog.comment(sstr.str());
2079 //TranslatorExplanation after semicolon is error message
2080 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2081 (rpmmsg.empty() ? error_message : rpmmsg)));
2085 // %s = filename of rpm package
2086 // historylog.comment(
2087 // str::form(_("%s installed ok"), Pathname::basename(filename).c_str()),
2088 // /*timestamp*/true);
2089 if ( ! rpmmsg.empty() )
2092 sstr << _("Additional rpm output:") << endl << rpmmsg << endl;
2093 historylog.comment(sstr.str());
2098 ///////////////////////////////////////////////////////////////////
2101 // METHOD NAME : RpmDb::removePackage
2102 // METHOD TYPE : PMError
2104 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2106 // 'rpm -e' does not like epochs
2107 return removePackage( package->name()
2108 + "-" + package->edition().version()
2109 + "-" + package->edition().release()
2110 + "." + package->arch().asString(), flags );
2113 ///////////////////////////////////////////////////////////////////
2116 // METHOD NAME : RpmDb::removePackage
2117 // METHOD TYPE : PMError
2119 void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
2121 callback::SendReport<RpmRemoveReport> report;
2123 report->start( name_r );
2128 doRemovePackage(name_r, flags, report);
2132 catch (RpmException & excpt_r)
2134 RpmRemoveReport::Action user = report->problem( excpt_r );
2136 if ( user == RpmRemoveReport::ABORT )
2138 report->finish( excpt_r );
2139 ZYPP_RETHROW(excpt_r);
2141 else if ( user == RpmRemoveReport::IGNORE )
2150 void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2152 FAILIFNOTINITIALIZED;
2153 HistoryLog historylog;
2155 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2158 if ( _packagebackups )
2160 // FIXME solve this status report somehow
2161 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2162 if ( ! backupPackage( name_r ) )
2164 ERR << "backup of " << name_r << " failed" << endl;
2166 report->progress( 0 );
2170 report->progress( 100 );
2175 opts.push_back("-e");
2176 opts.push_back("--allmatches");
2178 if (flags & RPMINST_NOSCRIPTS)
2179 opts.push_back("--noscripts");
2180 if (flags & RPMINST_NODEPS)
2181 opts.push_back("--nodeps");
2182 if (flags & RPMINST_JUSTDB)
2183 opts.push_back("--justdb");
2184 if (flags & RPMINST_TEST)
2185 opts.push_back ("--test");
2186 if (flags & RPMINST_FORCE)
2188 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2191 opts.push_back("--");
2192 opts.push_back(name_r.c_str());
2194 modifyDatabase(); // BEFORE run_rpm
2195 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2200 // got no progress from command, so we fake it:
2201 // 5 - command started
2202 // 50 - command completed
2204 report->progress( 5 );
2205 while (systemReadLine(line))
2207 rpmmsg += line+'\n';
2209 report->progress( 50 );
2210 int rpm_status = systemStatus();
2212 if ( rpm_status != 0 )
2214 // %s = name of rpm package
2216 str::form(_("%s remove failed"), name_r.c_str()), /*timestamp*/true);
2218 sstr << _("rpm output:") << endl << rpmmsg << endl;
2219 historylog.comment(sstr.str());
2220 // TranslatorExplanation after semicolon is error message
2221 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2222 (rpmmsg.empty() ? error_message: rpmmsg)));
2226 // historylog.comment(str::form(_("%s remove ok"), name_r.c_str()), /*timestamp*/true);
2227 if ( ! rpmmsg.empty() )
2230 sstr << _("Additional rpm output:") << endl << rpmmsg << endl;
2231 historylog.comment(sstr.str());
2236 ///////////////////////////////////////////////////////////////////
2239 // METHOD NAME : RpmDb::backupPackage
2240 // METHOD TYPE : bool
2242 bool RpmDb::backupPackage( const Pathname & filename )
2244 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2248 return backupPackage( h->tag_name() );
2251 ///////////////////////////////////////////////////////////////////
2254 // METHOD NAME : RpmDb::backupPackage
2255 // METHOD TYPE : bool
2257 bool RpmDb::backupPackage(const string& packageName)
2259 HistoryLog progresslog;
2261 Pathname backupFilename;
2262 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2264 if (_backuppath.empty())
2266 INT << "_backuppath empty" << endl;
2272 if (!queryChangedFiles(fileList, packageName))
2274 ERR << "Error while getting changed files for package " <<
2275 packageName << endl;
2279 if (fileList.size() <= 0)
2281 DBG << "package " << packageName << " not changed -> no backup" << endl;
2285 if (filesystem::assert_dir(_root + _backuppath) != 0)
2291 // build up archive name
2292 time_t currentTime = time(0);
2293 struct tm *currentLocalTime = localtime(¤tTime);
2295 int date = (currentLocalTime->tm_year + 1900) * 10000
2296 + (currentLocalTime->tm_mon + 1) * 100
2297 + currentLocalTime->tm_mday;
2302 backupFilename = _root + _backuppath
2303 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2306 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2308 PathInfo pi(filestobackupfile);
2309 if (pi.isExist() && !pi.isFile())
2311 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2315 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2319 ERR << "could not open " << filestobackupfile.asString() << endl;
2323 for (FileList::const_iterator cit = fileList.begin();
2324 cit != fileList.end(); ++cit)
2327 if ( name[0] == '/' )
2329 // remove slash, file must be relative to -C parameter of tar
2330 name = name.substr( 1 );
2332 DBG << "saving file "<< name << endl;
2337 const char* const argv[] =
2342 _root.asString().c_str(),
2343 "--ignore-failed-read",
2345 backupFilename.asString().c_str(),
2347 filestobackupfile.asString().c_str(),
2351 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2352 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2356 // TODO: its probably possible to start tar with -v and watch it adding
2357 // files to report progress
2358 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2363 int ret = tar.close();
2367 ERR << "tar failed: " << tarmsg << endl;
2372 MIL << "tar backup ok" << endl;
2373 progresslog.comment(
2374 str::form(_("created backup %s"), backupFilename.asString().c_str())
2375 , /*timestamp*/true);
2378 filesystem::unlink(filestobackupfile);
2384 void RpmDb::setBackupPath(const Pathname& path)
2390 } // namespace target