1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.h
26 #include "zypp/base/Logger.h"
28 #include "zypp/Date.h"
29 #include "zypp/Pathname.h"
30 #include "zypp/PathInfo.h"
32 #include "zypp/target/rpm/RpmDb.h"
33 #include "zypp/target/rpm/RpmCallbacks.h"
35 #include "zypp/target/rpm/librpmDb.h"
36 #include "zypp/target/rpm/RpmPackageImpl.h"
37 #include "zypp/target/rpm/RpmException.h"
38 #include "zypp/CapSet.h"
39 #include "zypp/CapFactory.h"
51 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
61 ExternalProgram* prog = new ExternalProgram(argv,ExternalProgram::Discard_Stderr, false, -1, true);
68 for(line = prog->receiveLine(), count=0;
70 line = prog->receiveLine(), count++ )
72 if(maxlines<0?true:count<maxlines)
81 /******************************************************************
84 ** FUNCTION NAME : stringPath
85 ** FUNCTION TYPE : inline string
87 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
89 return librpmDb::stringPath( root_r, sub_r );
92 /******************************************************************
95 ** FUNCTION NAME : operator<<
96 ** FUNCTION TYPE : ostream &
98 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
100 if ( obj == RpmDb::DbSI_NO_INIT ) {
103 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
105 ENUM_OUT( DbSI_HAVE_V4, 'X' );
106 ENUM_OUT( DbSI_MADE_V4, 'c' );
107 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
109 ENUM_OUT( DbSI_HAVE_V3, 'X' );
110 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
111 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
118 ///////////////////////////////////////////////////////////////////
119 // CLASS NAME : RpmDbPtr
120 // CLASS NAME : RpmDbconstPtr
121 ///////////////////////////////////////////////////////////////////
123 #define WARNINGMAILPATH "/var/log/YaST2/"
124 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
126 ///////////////////////////////////////////////////////////////////
128 // CLASS NAME : RpmDb::Logfile
130 * Simple wrapper for progress log. Refcnt, filename and corresponding
131 * ofstream are static members. Logfile constructor raises, destructor
132 * lowers refcounter. On refcounter changing from 0->1, file is opened.
133 * Changing from 1->0 the file is closed. Thus Logfile objects should be
134 * local to those functions, writing the log, and must not be stored
140 * Logfile progresslog;
142 * progresslog() << "some message" << endl;
146 class RpmDb::Logfile {
147 Logfile( const Logfile & );
148 Logfile & operator=( const Logfile & );
150 static ofstream _log;
151 static unsigned _refcnt;
152 static Pathname _fname;
153 static void openLog() {
154 if ( !_fname.empty() ) {
156 _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
158 ERR << "Could not open logfile '" << _fname << "'" << endl;
161 static void closeLog() {
165 static void refUp() {
170 static void refDown() {
176 Logfile() { refUp(); }
177 ~Logfile() { refDown(); }
178 ostream & operator()( bool timestamp = false ) {
180 _log << Date(Date::now()).form( "%Y-%m-%d %H:%M:%S ");
184 static void setFname( const Pathname & fname_r ) {
185 MIL << "installation log file " << fname_r << endl;
194 ///////////////////////////////////////////////////////////////////
196 Pathname RpmDb::Logfile::_fname;
197 ofstream RpmDb::Logfile::_log;
198 unsigned RpmDb::Logfile::_refcnt = 0;
200 ///////////////////////////////////////////////////////////////////
202 ///////////////////////////////////////////////////////////////////
205 // METHOD NAME : RpmDb::setInstallationLogfile
206 // METHOD TYPE : bool
208 bool RpmDb::setInstallationLogfile( const Pathname & filename )
210 Logfile::setFname( filename );
214 ///////////////////////////////////////////////////////////////////
216 // CLASS NAME : RpmDb::Packages
218 * Helper class for RpmDb::getPackages() to build the
219 * list<Package::Ptr> returned. We have to assert, that there
220 * is a unique entry for every string.
222 * In the first step we build the _index map which helps to catch
223 * multiple occurances of a string in the rpmdb. That's not desired,
224 * but possible. Usg. the last package instance installed is strored
227 * At the end buildList() is called to build the list<Package::Ptr>
228 * from the _index map. _valid is set true to assign that the list
229 * is in sync with the rpmdb content. Operations changing the rpmdb
230 * content (install/remove package) should set _valid to false. The
231 * next call to RpmDb::getPackages() will then reread the the rpmdb.
233 * Note that outside RpmDb::getPackages() _list and _index are always
234 * in sync. So you may use lookup(PkgName) to retrieve a specific
237 class RpmDb::Packages {
239 list<Package::Ptr> _list;
240 map<std::string,Package::Ptr> _index;
242 Packages() : _valid( false ) {}
248 Package::Ptr lookup( const string & name_r ) const {
249 map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
250 if ( got != _index.end() )
252 return Package::Ptr();
256 for ( map<string,Package::Ptr>::iterator iter = _index.begin();
257 iter != _index.end(); ++iter ) {
259 _list.push_back( iter->second );
265 ///////////////////////////////////////////////////////////////////
267 ///////////////////////////////////////////////////////////////////
269 // CLASS NAME : RpmDb
271 ///////////////////////////////////////////////////////////////////
273 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
275 ///////////////////////////////////////////////////////////////////
277 ///////////////////////////////////////////////////////////////////
280 // METHOD NAME : RpmDb::RpmDb
281 // METHOD TYPE : Constructor
284 : _dbStateInfo( DbSI_NO_INIT )
285 , _packages( * new Packages ) // delete in destructor
286 #warning Check for obsolete memebers
287 , _backuppath ("/var/adm/backup")
288 , _packagebackups(false)
289 , _warndirexists(false)
294 // Some rpm versions are patched not to abort installation if
295 // symlink creation failed.
296 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
299 ///////////////////////////////////////////////////////////////////
302 // METHOD NAME : RpmDb::~RpmDb
303 // METHOD TYPE : Destructor
307 MIL << "~RpmDb()" << endl;
312 MIL << "~RpmDb() end" << endl;
315 ///////////////////////////////////////////////////////////////////
318 // METHOD NAME : RpmDb::dumpOn
319 // METHOD TYPE : std::ostream &
321 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
325 if ( _dbStateInfo == DbSI_NO_INIT ) {
328 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
330 ENUM_OUT( DbSI_HAVE_V4, 'X' );
331 ENUM_OUT( DbSI_MADE_V4, 'c' );
332 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
334 ENUM_OUT( DbSI_HAVE_V3, 'X' );
335 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
336 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
337 str << "): " << stringPath( _root, _dbPath );
343 ///////////////////////////////////////////////////////////////////
346 // METHOD NAME : RpmDb::initDatabase
347 // METHOD TYPE : PMError
349 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
351 ///////////////////////////////////////////////////////////////////
353 ///////////////////////////////////////////////////////////////////
354 if ( root_r.empty() )
357 if ( dbPath_r.empty() )
358 dbPath_r = "/var/lib/rpm";
360 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
361 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
362 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
365 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
367 ///////////////////////////////////////////////////////////////////
368 // Check whether already initialized
369 ///////////////////////////////////////////////////////////////////
370 if ( initialized() ) {
371 if ( root_r == _root && dbPath_r == _dbPath ) {
374 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
378 ///////////////////////////////////////////////////////////////////
380 ///////////////////////////////////////////////////////////////////
381 librpmDb::unblockAccess();
382 DbStateInfoBits info = DbSI_NO_INIT;
384 internal_initDatabase( root_r, dbPath_r, info );
386 catch (const RpmException & excpt_r)
388 ZYPP_CAUGHT(excpt_r);
389 librpmDb::blockAccess();
390 ERR << "Cleanup on error: state " << info << endl;
392 if ( dbsi_has( info, DbSI_MADE_V4 ) ) {
393 // remove the newly created rpm4 database and
394 // any backup created on conversion.
395 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
397 ZYPP_RETHROW(excpt_r);
399 if ( dbsi_has( info, DbSI_HAVE_V3 ) ) {
400 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) ) {
401 // Move obsolete rpm3 database beside.
402 MIL << "Cleanup: state " << info << endl;
403 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
404 dbsi_clr( info, DbSI_HAVE_V3 );
406 // Performing an update: Keep the original rpm3 database
407 // and wait if the rpm4 database gets modified by installing
408 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
409 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
412 #warning CHECK: notify root about conversion backup.
418 #warning Add rebuild database once have the info about context
420 if ( ! ( Y2PM::runningFromSystem() ) ) {
421 if ( dbsi_has( info, DbSI_HAVE_V4 )
422 && ! dbsi_has( info, DbSI_MADE_V4 ) ) {
423 err = rebuildDatabase();
428 // Close the database in case any write acces (create/convert)
429 // happened during init. This should drop any lock acquired
430 // by librpm. On demand it will be reopened readonly and should
431 // not hold any lock.
432 librpmDb::dbRelease( true );
433 MIL << "InitDatabase: " << *this << endl;
436 ///////////////////////////////////////////////////////////////////
439 // METHOD NAME : RpmDb::internal_initDatabase
440 // METHOD TYPE : PMError
442 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
443 DbStateInfoBits & info_r )
445 info_r = DbSI_NO_INIT;
447 ///////////////////////////////////////////////////////////////////
448 // Get info about the desired database dir
449 ///////////////////////////////////////////////////////////////////
450 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
452 if ( dbInfo.illegalArgs() ) {
453 // should not happen (checked in initDatabase)
454 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
456 if ( ! dbInfo.usableArgs() ) {
457 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
458 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
461 if ( dbInfo.hasDbV4() ) {
462 dbsi_set( info_r, DbSI_HAVE_V4 );
463 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
465 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
468 if ( dbInfo.hasDbV3() ) {
469 dbsi_set( info_r, DbSI_HAVE_V3 );
471 if ( dbInfo.hasDbV3ToV4() ) {
472 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
475 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
476 librpmDb::dumpState( DBG ) << endl;
478 ///////////////////////////////////////////////////////////////////
479 // Access database, create if needed
480 ///////////////////////////////////////////////////////////////////
482 // creates dbdir and empty rpm4 database if not present
483 librpmDb::dbAccess( root_r, dbPath_r );
485 if ( ! dbInfo.hasDbV4() ) {
487 if ( dbInfo.hasDbV4() ) {
488 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
492 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
493 librpmDb::dumpState( DBG ) << endl;
495 ///////////////////////////////////////////////////////////////////
496 // Check whether to convert something. Create backup but do
497 // not remove anything here
498 ///////////////////////////////////////////////////////////////////
499 librpmDb::constPtr dbptr;
500 librpmDb::dbAccess( dbptr );
501 bool dbEmpty = dbptr->empty();
503 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
506 if ( dbInfo.hasDbV3() ) {
507 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
510 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
511 convertV3toV4( dbInfo.dbV3().path(), dbptr );
513 // create a backup copy
514 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
516 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
519 if ( dbInfo.hasDbV3ToV4() ) {
520 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
521 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
527 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
528 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
529 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
530 //report->start( dbInfo.dbV3().path() );
531 //report->stop( some error );
533 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
534 dbsi_set( info_r, DbSI_MODIFIED_V4 );
538 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
539 librpmDb::dumpState( DBG ) << endl;
542 if ( dbInfo.hasDbV3ToV4() ) {
543 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
547 ///////////////////////////////////////////////////////////////////
550 // METHOD NAME : RpmDb::removeV4
551 // METHOD TYPE : void
553 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
555 const char * v3backup = "packages.rpm3";
556 const char * master = "Packages";
557 const char * index[] = {
578 PathInfo pi( dbdir_r );
579 if ( ! pi.isDir() ) {
580 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
584 for ( const char ** f = index; *f; ++f ) {
587 filesystem::unlink( pi.path() );
591 pi( dbdir_r + master );
593 MIL << "Removing rpm4 database " << pi << endl;
594 filesystem::unlink( pi.path() );
598 pi( dbdir_r + v3backup );
600 MIL << "Removing converted rpm3 database backup " << pi << endl;
601 filesystem::unlink( pi.path() );
606 ///////////////////////////////////////////////////////////////////
609 // METHOD NAME : RpmDb::removeV3
610 // METHOD TYPE : void
612 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
614 const char * master = "packages.rpm";
615 const char * index[] = {
616 "conflictsindex.rpm",
627 PathInfo pi( dbdir_r );
628 if ( ! pi.isDir() ) {
629 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
633 for ( const char ** f = index; *f; ++f ) {
636 filesystem::unlink( pi.path() );
640 #warning CHECK: compare vs existing v3 backup. notify root
641 pi( dbdir_r + master );
643 Pathname m( pi.path() );
645 // backup was already created
646 filesystem::unlink( m );
647 Pathname b( m.extend( "3" ) );
648 pi( b ); // stat backup
650 Pathname b( m.extend( ".deleted" ) );
653 // rempve existing backup
654 filesystem::unlink( b );
656 filesystem::rename( m, b );
657 pi( b ); // stat backup
659 MIL << "(Re)moved rpm3 database to " << pi << endl;
663 ///////////////////////////////////////////////////////////////////
666 // METHOD NAME : RpmDb::modifyDatabase
667 // METHOD TYPE : void
669 void RpmDb::modifyDatabase()
671 if ( ! initialized() )
674 // tag database as modified
675 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
677 // Move outdated rpm3 database beside.
678 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
679 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
680 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
681 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
684 // invalidate Packages list
685 _packages._valid = false;
688 ///////////////////////////////////////////////////////////////////
691 // METHOD NAME : RpmDb::closeDatabase
692 // METHOD TYPE : PMError
694 void RpmDb::closeDatabase()
696 if ( ! initialized() ) {
700 MIL << "Calling closeDatabase: " << *this << endl;
702 ///////////////////////////////////////////////////////////////////
703 // Block further database access
704 ///////////////////////////////////////////////////////////////////
706 librpmDb::blockAccess();
708 ///////////////////////////////////////////////////////////////////
709 // Check fate if old version database still present
710 ///////////////////////////////////////////////////////////////////
711 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
712 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
713 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
714 // Move outdated rpm3 database beside.
715 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
717 // Remove unmodified rpm4 database
718 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
722 ///////////////////////////////////////////////////////////////////
724 ///////////////////////////////////////////////////////////////////
725 _root = _dbPath = Pathname();
726 _dbStateInfo = DbSI_NO_INIT;
728 MIL << "closeDatabase: " << *this << endl;
731 ///////////////////////////////////////////////////////////////////
734 // METHOD NAME : RpmDb::rebuildDatabase
735 // METHOD TYPE : PMError
737 void RpmDb::rebuildDatabase()
739 RebuildDbReport report;
741 doRebuildDatabase(report);
743 catch (RpmException & excpt_r)
746 ZYPP_RETHROW(excpt_r);
751 void RpmDb::doRebuildDatabase(RebuildDbReport & report)
753 FAILIFNOTINITIALIZED;
755 MIL << "RpmDb::rebuildDatabase" << *this << endl;
756 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
758 PathInfo dbMaster( root() + dbPath() + "Packages" );
759 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
763 opts.push_back("--rebuilddb");
764 opts.push_back("-vv");
766 // don't call modifyDatabase because it would remove the old
767 // rpm3 database, if the current database is a temporary one.
768 // But do invalidate packages list.
769 _packages._valid = false;
770 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
772 // progress report: watch this file growing
773 PathInfo newMaster( root()
774 + dbPath().extend( str::form( "rebuilddb.%d",
775 process?process->getpid():0) )
781 while ( systemReadLine( line ) ) {
782 if ( newMaster() ) { // file is removed at the end of rebuild.
783 // current size should be upper limit for new db
784 report.progress( (100 * newMaster.size()) / dbMaster.size());
787 if ( line.compare( 0, 2, "D:" ) ) {
788 errmsg += line + '\n';
789 // report.notify( line );
794 int rpm_status = systemStatus();
796 if ( rpm_status != 0 ) {
797 ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
799 report.progress( 100 ); // 100%
803 ///////////////////////////////////////////////////////////////////
806 // METHOD NAME : RpmDb::importPubkey
807 // METHOD TYPE : PMError
809 void RpmDb::importPubkey( const Pathname & pubkey_r )
811 FAILIFNOTINITIALIZED;
814 opts.push_back ( "--import" );
815 opts.push_back ( "--" );
816 opts.push_back ( pubkey_r.asString().c_str() );
818 // don't call modifyDatabase because it would remove the old
819 // rpm3 database, if the current database is a temporary one.
820 // But do invalidate packages list.
821 _packages._valid = false;
822 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
825 while ( systemReadLine( line ) ) {
826 if ( line.substr( 0, 6 ) == "error:" ) {
833 int rpm_status = systemStatus();
835 if ( rpm_status != 0 ) {
836 ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned ") + str::numstring(rpm_status)));
838 MIL << "Imported public key from file " << pubkey_r << endl;
842 ///////////////////////////////////////////////////////////////////
845 // METHOD NAME : RpmDb::importPubkey
846 // METHOD TYPE : PMError
848 void RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
850 FAILIFNOTINITIALIZED;
853 char tmpname[] = "/tmp/zypp.pubkey.XXXXXX";
854 int tmpfd = mkstemp( tmpname );
856 ZYPP_THROW(RpmSubprocessException("Unable to create a unique temporary file for pubkey"));
859 // export keyname from keyring
861 args.push_back( "gpg" );
862 args.push_back( "--armor" );
863 args.push_back( "--no-default-keyring" );
864 args.push_back( "--keyring" );
865 args.push_back( keyring_r.asString().c_str() );
866 args.push_back( "--export" );
867 args.push_back( keyname_r.c_str() );
869 const char * argv[args.size() + 1];
870 const char ** p = argv;
871 p = copy( args.begin(), args.end(), p );
875 ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
881 for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
882 ssize_t written = write( tmpfd, line.c_str(), line.length() );
883 if ( written == -1 || unsigned(written) != line.length() ) {
884 ZYPP_THROW(RpmSubprocessException(string("Error writing pubkey to ") + tmpname));
886 res += written; // empty file indicates key not found
889 catch (RpmException & excpt_r)
891 ZYPP_CAUGHT(excpt_r);
893 filesystem::unlink( tmpname );
894 ZYPP_RETHROW(excpt_r);
899 ZYPP_THROW(RpmSubprocessException(string("gpg: no key '") + keyname_r + string("' found in '") + keyring_r.asString() + string("'")));
902 // check gpg returncode
906 filesystem::unlink( tmpname );
907 ZYPP_THROW(RpmSubprocessException(string("gpg: export '") + keyname_r + string("' from '") + keyring_r.asString() + "' returned " + str::numstring(res)));
910 MIL << "Exported '" << keyname_r << "' from '" << keyring_r << "' to " << tmpname << endl;
912 importPubkey( tmpname );
914 catch (RpmException & excpt_r)
916 ZYPP_CAUGHT(excpt_r);
917 filesystem::unlink( tmpname );
918 ZYPP_RETHROW(excpt_r);
920 filesystem::unlink( tmpname );
923 ///////////////////////////////////////////////////////////////////
926 // METHOD NAME : RpmDb::pubkeys
927 // METHOD TYPE : set<Edition>
929 set<Edition> RpmDb::pubkeys() const
933 librpmDb::db_const_iterator it;
934 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it ) {
935 ret.insert( it->tag_edition() );
941 ///////////////////////////////////////////////////////////////////
944 // METHOD NAME : RpmDb::packagesValid
945 // METHOD TYPE : bool
947 bool RpmDb::packagesValid() const
949 return( _packages._valid || ! initialized() );
952 ///////////////////////////////////////////////////////////////////
955 // METHOD NAME : RpmDb::getPackages
956 // METHOD TYPE : const std::list<Package::Ptr> &
960 const std::list<Package::Ptr> & RpmDb::getPackages()
964 const std::list<Package::Ptr> & ret = doGetPackages(report);
968 catch (RpmException & excpt_r)
971 ZYPP_RETHROW(excpt_r);
976 const std::list<Package::Ptr> & RpmDb::doGetPackages(ScanDbReport & report)
978 if ( packagesValid() ) {
979 return _packages._list;
982 // FIXME Timecount _t( "RpmDb::getPackages" );
984 #warning how to detect corrupt db while reading.
988 ///////////////////////////////////////////////////////////////////
989 // Collect package data. A map is used to check whethere there are
990 // multiple entries for the same string. If so we consider the last
991 // one installed to be the one we're interesed in.
992 ///////////////////////////////////////////////////////////////////
994 librpmDb::db_const_iterator iter; // findAll
997 for ( ; *iter; ++iter ) {
1000 if ( iter.dbError() ) {
1001 ERR << "No database access: " << iter.dbError() << endl;
1002 ZYPP_THROW(*(iter.dbError()));
1005 unsigned current = 0;
1008 for ( iter.findAll(); *iter; ++iter, ++current, report.progress( (100*current)/expect)) {
1010 string name = iter->tag_name();
1011 if ( name == string( "gpg-pubkey" ) ) {
1012 // pseudo package filtered, as we can't handle multiple instances
1013 // of 'gpg-pubkey-VERS-REL'.
1016 Date installtime = iter->tag_installtime();
1017 Package::Ptr & nptr = _packages._index[name]; // be shure to get a reference!
1020 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
1021 if ( nptr->installtime() > installtime )
1023 // else overwrite previous entry
1026 // create dataprovider
1027 shared_ptr<RPMPackageImpl> impl(new RPMPackageImpl(*iter));
1029 // Collect basic Resolvable data
1030 NVRAD dataCollect( iter->tag_name(),
1031 iter->tag_edition(),
1034 list<string> filenames = impl->filenames();
1035 dataCollect.provides = iter->tag_provides ( & _filerequires );
1036 for (list<string>::const_iterator filename = filenames.begin();
1037 filename != filenames.end();
1040 if (filename->find("/bin/")
1041 || filename->find("/sbin/")
1042 || filename->find("/lib/")
1043 || filename->find("/lib64/")
1044 || filename->find("/etc/")
1045 || filename->find("/usr/games/")
1046 || filename->find("/usr/share/dict/words")
1047 || filename->find("/usr/share/magic.mime")
1048 || filename->find("/opt/gnome/games"))
1051 dataCollect.provides.insert(_f.parse(ResTraits<Package>::kind, *filename));
1053 catch (Exception & excpt_r)
1055 ZYPP_CAUGHT(excpt_r);
1056 WAR << "Invalid capability: " << *filename << endl;
1061 dataCollect.requires = iter->tag_requires ( &_filerequires );
1062 dataCollect.prerequires = iter->tag_prerequires ( &_filerequires );
1063 dataCollect.conflicts = iter->tag_conflicts( &_filerequires );
1064 dataCollect.obsoletes = iter->tag_obsoletes( &_filerequires );
1066 // create package from dataprovider
1067 nptr = detail::makeResolvableFromImpl( dataCollect, impl );
1071 ///////////////////////////////////////////////////////////////////
1072 // Evaluate filerequires collected so far
1073 ///////////////////////////////////////////////////////////////////
1074 for( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
1076 for ( iter.findByFile( *it ); *iter; ++iter ) {
1077 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1079 WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1082 pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
1087 ///////////////////////////////////////////////////////////////////
1088 // Build final packages list
1089 ///////////////////////////////////////////////////////////////////
1090 _packages.buildList();
1091 DBG << "Found installed packages: " << _packages._list.size() << endl;
1092 return _packages._list;
1095 #warning Uncomment this function if it is needed
1097 ///////////////////////////////////////////////////////////////////
1100 // METHOD NAME : RpmDb::traceFileRel
1101 // METHOD TYPE : void
1105 void RpmDb::traceFileRel( const PkgRelation & rel_r )
1107 if ( ! rel_r.isFileRel() )
1110 if ( ! _filerequires.insert( rel_r.name() ).second )
1111 return; // already got it in _filerequires
1113 if ( ! _packages._valid )
1114 return; // collect only. Evaluated in first call to getPackages()
1117 // packages already initialized. Must check and insert here
1119 librpmDb::db_const_iterator iter;
1120 if ( iter.dbError() ) {
1121 ERR << "No database access: " << iter.dbError() << endl;
1125 for ( iter.findByFile( rel_r.name() ); *iter; ++iter ) {
1126 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1128 WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
1131 pptr->addProvides( rel_r.name() );
1136 ///////////////////////////////////////////////////////////////////
1139 // METHOD NAME : RpmDb::hasFile
1140 // METHOD TYPE : bool
1144 bool RpmDb::hasFile( const std::string & file_r ) const
1146 librpmDb::db_const_iterator it;
1147 return it.findByFile( file_r );
1150 ///////////////////////////////////////////////////////////////////
1153 // METHOD NAME : RpmDb::hasProvides
1154 // METHOD TYPE : bool
1158 bool RpmDb::hasProvides( const std::string & tag_r ) const
1160 librpmDb::db_const_iterator it;
1161 return it.findByProvides( tag_r );
1164 ///////////////////////////////////////////////////////////////////
1167 // METHOD NAME : RpmDb::hasRequiredBy
1168 // METHOD TYPE : bool
1172 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1174 librpmDb::db_const_iterator it;
1175 return it.findByRequiredBy( tag_r );
1178 ///////////////////////////////////////////////////////////////////
1181 // METHOD NAME : RpmDb::hasConflicts
1182 // METHOD TYPE : bool
1186 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1188 librpmDb::db_const_iterator it;
1189 return it.findByConflicts( tag_r );
1192 ///////////////////////////////////////////////////////////////////
1195 // METHOD NAME : RpmDb::hasPackage
1196 // METHOD TYPE : bool
1200 bool RpmDb::hasPackage( const string & name_r ) const
1202 librpmDb::db_const_iterator it;
1203 return it.findPackage( name_r );
1206 ///////////////////////////////////////////////////////////////////
1209 // METHOD NAME : RpmDb::getData
1210 // METHOD TYPE : PMError
1214 void RpmDb::getData( const string & name_r,
1215 RpmHeader::constPtr & result_r ) const
1217 librpmDb::db_const_iterator it;
1218 it.findPackage( name_r );
1221 ZYPP_THROW(*(it.dbError()));
1224 ///////////////////////////////////////////////////////////////////
1227 // METHOD NAME : RpmDb::getData
1228 // METHOD TYPE : PMError
1232 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1233 RpmHeader::constPtr & result_r ) const
1235 librpmDb::db_const_iterator it;
1236 it.findPackage( name_r, ed_r );
1239 ZYPP_THROW(*(it.dbError()));
1242 /*--------------------------------------------------------------*/
1243 /* Checking the source rpm <rpmpath> with rpm --chcksig and */
1244 /* the version number. */
1245 /*--------------------------------------------------------------*/
1247 RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
1249 unsigned result = 0;
1251 if ( ! version.empty() ) {
1252 RpmHeader::constPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
1253 if ( ! h || Edition( version ) != h->tag_edition() ) {
1254 result |= CHK_INCORRECT_VERSION;
1260 #warning TBD MD5 check
1261 WAR << "md5sum check not yet implemented" << endl;
1262 return CHK_INCORRECT_FILEMD5;
1265 std::string path = packagePath.asString();
1266 // checking --checksig
1267 const char *const argv[] = {
1268 "rpm", "--checksig", "--", path.c_str(), 0
1275 for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
1277 output = output + " " + argv[k];
1280 DBG << "rpm command: " << output << endl;
1282 if ( process != NULL )
1287 // Launch the program
1288 process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1291 if ( process == NULL )
1293 result |= CHK_OTHER_FAILURE;
1294 DBG << "create process failed" << endl;
1298 output = process->receiveLine();
1300 while ( output.length() > 0)
1302 string::size_type ret;
1305 ret = output.find_first_of ( "\n" );
1306 if ( ret != string::npos )
1308 value.assign ( output, 0, ret );
1315 DBG << "stdout: " << value << endl;
1317 string::size_type pos;
1318 if((pos = value.find (path)) != string::npos)
1320 string rest = value.substr (pos + path.length() + 1);
1321 if (rest.find("NOT OK") == string::npos)
1323 // see what checks are ok
1324 if (rest.find("md5") == string::npos)
1326 result |= CHK_MD5SUM_MISSING;
1328 if (rest.find("gpg") == string::npos)
1330 result |= CHK_GPGSIG_MISSING;
1335 // see what checks are not ok
1336 if (rest.find("MD5") != string::npos)
1338 result |= CHK_INCORRECT_PKGMD5;
1342 result |= CHK_MD5SUM_MISSING;
1345 if (rest.find("GPG") != string::npos)
1347 result |= CHK_INCORRECT_GPGSIG;
1351 result |= CHK_GPGSIG_MISSING;
1356 output = process->receiveLine();
1359 if ( result == 0 && systemStatus() != 0 )
1362 result |= CHK_OTHER_FAILURE;
1368 // determine changed files of installed package
1370 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1376 if( ! initialized() ) return false;
1380 opts.push_back ("-V");
1381 opts.push_back ("--nodeps");
1382 opts.push_back ("--noscripts");
1383 opts.push_back ("--nomd5");
1384 opts.push_back ("--");
1385 opts.push_back (packageName.c_str());
1387 run_rpm (opts, ExternalProgram::Discard_Stderr);
1389 if ( process == NULL )
1400 M Mode (includes permissions and file type)
1404 while (systemReadLine(line))
1406 if (line.length() > 12 &&
1407 (line[0] == 'S' || line[0] == 's' ||
1408 (line[0] == '.' && line[7] == 'T')))
1410 // file has been changed
1413 filename.assign(line, 11, line.length() - 11);
1414 fileList.insert(filename);
1419 // exit code ignored, rpm returns 1 no matter if package is installed or
1427 /****************************************************************/
1428 /* private member-functions */
1429 /****************************************************************/
1431 /*--------------------------------------------------------------*/
1432 /* Run rpm with the specified arguments, handling stderr */
1433 /* as specified by disp */
1434 /*--------------------------------------------------------------*/
1436 RpmDb::run_rpm (const RpmArgVec& opts,
1437 ExternalProgram::Stderr_Disposition disp)
1445 if ( ! initialized() ) {
1446 ZYPP_THROW(RpmDbNotOpenException());
1451 // always set root and dbpath
1452 args.push_back("rpm");
1453 args.push_back("--root");
1454 args.push_back(_root.asString().c_str());
1455 args.push_back("--dbpath");
1456 args.push_back(_dbPath.asString().c_str());
1458 const char* argv[args.size() + opts.size() + 1];
1460 const char** p = argv;
1461 p = copy (args.begin (), args.end (), p);
1462 p = copy (opts.begin (), opts.end (), p);
1465 // Invalidate all outstanding database handles in case
1466 // the database gets modified.
1467 librpmDb::dbRelease( true );
1469 // Launch the program with default locale
1470 process = new ExternalProgram(argv, disp, false, -1, true);
1474 /*--------------------------------------------------------------*/
1475 /* Read a line from the rpm process */
1476 /*--------------------------------------------------------------*/
1478 RpmDb::systemReadLine(string &line)
1482 if ( process == NULL )
1485 line = process->receiveLine();
1487 if (line.length() == 0)
1490 if (line[line.length() - 1] == '\n')
1491 line.erase(line.length() - 1);
1496 /*--------------------------------------------------------------*/
1497 /* Return the exit status of the rpm process, closing the */
1498 /* connection if not already done */
1499 /*--------------------------------------------------------------*/
1501 RpmDb::systemStatus()
1503 if ( process == NULL )
1506 exit_code = process->close();
1511 // DBG << "exit code " << exit_code << endl;
1516 /*--------------------------------------------------------------*/
1517 /* Forcably kill the rpm process */
1518 /*--------------------------------------------------------------*/
1522 if (process) process->kill();
1526 // generate diff mails for config files
1527 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1529 string msg = line.substr(9);
1530 string::size_type pos1 = string::npos;
1531 string::size_type pos2 = string::npos;
1532 string file1s, file2s;
1536 pos1 = msg.find (typemsg);
1539 if( pos1 == string::npos )
1542 pos2 = pos1 + strlen (typemsg);
1544 if (pos2 >= msg.length() )
1547 file1 = msg.substr (0, pos1);
1548 file2 = msg.substr (pos2);
1550 file1s = file1.asString();
1551 file2s = file2.asString();
1553 if (!_root.empty() && _root != "/")
1555 file1 = _root + file1;
1556 file2 = _root + file2;
1560 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1563 Pathname file = _root + WARNINGMAILPATH;
1564 if (filesystem::assert_dir(file) != 0)
1566 ERR << "Could not create " << file.asString() << endl;
1569 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1570 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1573 ERR << "Could not open " << file << endl;
1577 // Translator: %s = name of an rpm package. A list of diffs follows
1579 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1582 ERR << "diff failed" << endl;
1583 notify << str::form(difffailmsg,
1584 file1s.c_str(), file2s.c_str()) << endl;
1588 notify << str::form(diffgenmsg,
1589 file1s.c_str(), file2s.c_str()) << endl;
1591 // remove root for the viewer's pleasure (#38240)
1592 if (!_root.empty() && _root != "/")
1594 if(out.substr(0,4) == "--- ")
1596 out.replace(4, file1.asString().length(), file1s);
1598 string::size_type pos = out.find("\n+++ ");
1599 if(pos != string::npos)
1601 out.replace(pos+5, file2.asString().length(), file2s);
1604 notify << out << endl;
1607 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1612 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1618 ///////////////////////////////////////////////////////////////////
1621 // METHOD NAME : RpmDb::installPackage
1622 // METHOD TYPE : PMError
1624 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1626 RpmInstallReport report;
1628 doInstallPackage(filename, flags, report);
1630 catch (RpmException & excpt_r)
1632 report.end(excpt_r);
1633 ZYPP_RETHROW(excpt_r);
1638 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, RpmInstallReport & report )
1640 FAILIFNOTINITIALIZED;
1641 Logfile progresslog;
1643 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1646 if ( _packagebackups ) {
1647 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1648 if ( ! backupPackage( filename ) ) {
1649 ERR << "backup of " << filename.asString() << " failed" << endl;
1651 // FIXME status handling
1652 report.progress( 0 ); // allow 1% for backup creation.
1654 report.progress( 100 );
1659 if (flags & RPMINST_NOUPGRADE)
1660 opts.push_back("-i");
1662 opts.push_back("-U");
1663 opts.push_back("--percent");
1665 if (flags & RPMINST_NODIGEST)
1666 opts.push_back("--nodigest");
1667 if (flags & RPMINST_NOSIGNATURE)
1668 opts.push_back("--nosignature");
1669 if (flags & RPMINST_NODOCS)
1670 opts.push_back ("--excludedocs");
1671 if (flags & RPMINST_NOSCRIPTS)
1672 opts.push_back ("--noscripts");
1673 if (flags & RPMINST_FORCE)
1674 opts.push_back ("--force");
1675 if (flags & RPMINST_NODEPS)
1676 opts.push_back ("--nodeps");
1677 if(flags & RPMINST_IGNORESIZE)
1678 opts.push_back ("--ignoresize");
1679 if(flags & RPMINST_JUSTDB)
1680 opts.push_back ("--justdb");
1682 opts.push_back("--");
1683 opts.push_back (filename.asString().c_str());
1685 modifyDatabase(); // BEFORE run_rpm
1686 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1690 vector<string> configwarnings;
1691 vector<string> errorlines;
1693 while (systemReadLine(line))
1695 if (line.substr(0,2)=="%%")
1698 sscanf (line.c_str () + 2, "%d", &percent);
1699 report.progress( percent );
1702 rpmmsg += line+'\n';
1704 if( line.substr(0,8) == "warning:" )
1706 configwarnings.push_back(line);
1709 int rpm_status = systemStatus();
1712 for(vector<string>::iterator it = configwarnings.begin();
1713 it != configwarnings.end(); ++it)
1715 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1717 _("rpm saved %s as %s but it was impossible to determine the difference"),
1719 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1720 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1722 _("rpm created %s as %s but it was impossible to determine the difference"),
1724 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1727 if ( rpm_status != 0 ) {
1728 // %s = filename of rpm package
1729 progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1730 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1731 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1733 // %s = filename of rpm package
1734 progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1735 if( ! rpmmsg.empty() ) {
1736 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1741 ///////////////////////////////////////////////////////////////////
1744 // METHOD NAME : RpmDb::removePackage
1745 // METHOD TYPE : PMError
1747 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1749 return removePackage( package->name(), flags );
1752 ///////////////////////////////////////////////////////////////////
1755 // METHOD NAME : RpmDb::removePackage
1756 // METHOD TYPE : PMError
1758 void RpmDb::removePackage( const string & name_r, unsigned flags )
1760 RpmRemoveReport report;
1762 doRemovePackage(name_r, flags, report);
1764 catch (RpmException & excpt_r)
1766 report.end(excpt_r);
1767 ZYPP_RETHROW(excpt_r);
1773 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, RpmRemoveReport & report )
1775 FAILIFNOTINITIALIZED;
1776 Logfile progresslog;
1778 MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
1781 if ( _packagebackups ) {
1782 // FIXME solve this status report somehow
1783 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1784 if ( ! backupPackage( name_r ) ) {
1785 ERR << "backup of " << name_r << " failed" << endl;
1787 report.progress( 0 );
1789 report.progress( 100 );
1794 opts.push_back("-e");
1795 opts.push_back("--allmatches");
1797 if (flags & RPMINST_NOSCRIPTS)
1798 opts.push_back("--noscripts");
1799 if (flags & RPMINST_NODEPS)
1800 opts.push_back("--nodeps");
1801 if (flags & RPMINST_JUSTDB)
1802 opts.push_back("--justdb");
1803 if (flags & RPMINST_FORCE) {
1804 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1807 opts.push_back("--");
1808 opts.push_back(name_r.c_str());
1810 modifyDatabase(); // BEFORE run_rpm
1811 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1816 // got no progress from command, so we fake it:
1817 // 5 - command started
1818 // 50 - command completed
1820 report.progress( 5 );
1821 while (systemReadLine(line))
1823 rpmmsg += line+'\n';
1825 report.progress( 50 );
1826 int rpm_status = systemStatus();
1828 if ( rpm_status != 0 ) {
1829 // %s = name of rpm package
1830 progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
1831 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1832 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1834 progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
1835 if( ! rpmmsg.empty() ) {
1836 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1842 RpmDb::checkPackageResult2string(unsigned code)
1845 // begin of line characters
1847 // end of line characters
1850 return string(_("Ok"))+eol;
1852 //translator: these are different kinds of how an rpm package can be broken
1853 msg = _("Package is not OK for the following reasons:");
1856 if(code&CHK_INCORRECT_VERSION)
1859 msg+=_("Package contains different version than expected");
1862 if(code&CHK_INCORRECT_FILEMD5)
1865 msg+=_("Package file has incorrect MD5 sum");
1868 if(code&CHK_GPGSIG_MISSING)
1871 msg+=_("Package is not signed");
1874 if(code&CHK_MD5SUM_MISSING)
1877 msg+=_("Package has no MD5 sum");
1880 if(code&CHK_INCORRECT_GPGSIG)
1883 msg+=_("Package has incorrect signature");
1886 if(code&CHK_INCORRECT_PKGMD5)
1889 msg+=_("Package archive has incorrect MD5 sum");
1892 if(code&CHK_OTHER_FAILURE)
1895 msg+=_("rpm failed for unkown reason, see log file");
1902 ///////////////////////////////////////////////////////////////////
1905 // METHOD NAME : RpmDb::backupPackage
1906 // METHOD TYPE : bool
1908 bool RpmDb::backupPackage( const Pathname & filename )
1910 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1914 return backupPackage( h->tag_name() );
1917 ///////////////////////////////////////////////////////////////////
1920 // METHOD NAME : RpmDb::backupPackage
1921 // METHOD TYPE : bool
1923 bool RpmDb::backupPackage(const string& packageName)
1925 Logfile progresslog;
1927 Pathname backupFilename;
1928 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1930 if (_backuppath.empty())
1932 INT << "_backuppath empty" << endl;
1938 if (!queryChangedFiles(fileList, packageName))
1940 ERR << "Error while getting changed files for package " <<
1941 packageName << endl;
1945 if (fileList.size() <= 0)
1947 DBG << "package " << packageName << " not changed -> no backup" << endl;
1951 if (filesystem::assert_dir(_root + _backuppath) != 0)
1957 // build up archive name
1958 time_t currentTime = time(0);
1959 struct tm *currentLocalTime = localtime(¤tTime);
1961 int date = (currentLocalTime->tm_year + 1900) * 10000
1962 + (currentLocalTime->tm_mon + 1) * 100
1963 + currentLocalTime->tm_mday;
1968 backupFilename = _root + _backuppath
1969 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1972 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1974 PathInfo pi(filestobackupfile);
1975 if(pi.isExist() && !pi.isFile())
1977 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1981 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1985 ERR << "could not open " << filestobackupfile.asString() << endl;
1989 for (FileList::const_iterator cit = fileList.begin();
1990 cit != fileList.end(); ++cit)
1993 if ( name[0] == '/' )
1995 // remove slash, file must be relative to -C parameter of tar
1996 name = name.substr( 1 );
1998 DBG << "saving file "<< name << endl;
2003 const char* const argv[] =
2008 _root.asString().c_str(),
2009 "--ignore-failed-read",
2011 backupFilename.asString().c_str(),
2013 filestobackupfile.asString().c_str(),
2017 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2018 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2022 // TODO: its probably possible to start tar with -v and watch it adding
2023 // files to report progress
2024 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2029 int ret = tar.close();
2033 ERR << "tar failed: " << tarmsg << endl;
2038 MIL << "tar backup ok" << endl;
2039 progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
2042 filesystem::unlink(filestobackupfile);
2048 void RpmDb::setBackupPath(const Pathname& path)
2054 } // namespace target