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"
50 /******************************************************************
53 ** FUNCTION NAME : stringPath
54 ** FUNCTION TYPE : inline string
56 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
58 return librpmDb::stringPath( root_r, sub_r );
61 /******************************************************************
64 ** FUNCTION NAME : operator<<
65 ** FUNCTION TYPE : ostream &
67 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
69 if ( obj == RpmDb::DbSI_NO_INIT ) {
72 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
74 ENUM_OUT( DbSI_HAVE_V4, 'X' );
75 ENUM_OUT( DbSI_MADE_V4, 'c' );
76 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
78 ENUM_OUT( DbSI_HAVE_V3, 'X' );
79 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
80 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
87 ///////////////////////////////////////////////////////////////////
88 // CLASS NAME : RpmDbPtr
89 // CLASS NAME : RpmDbconstPtr
90 ///////////////////////////////////////////////////////////////////
92 #define WARNINGMAILPATH "/var/log/YaST2/"
93 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
95 ///////////////////////////////////////////////////////////////////
97 // CLASS NAME : RpmDb::Logfile
99 * Simple wrapper for progress log. Refcnt, filename and corresponding
100 * ofstream are static members. Logfile constructor raises, destructor
101 * lowers refcounter. On refcounter changing from 0->1, file is opened.
102 * Changing from 1->0 the file is closed. Thus Logfile objects should be
103 * local to those functions, writing the log, and must not be stored
109 * Logfile progresslog;
111 * progresslog() << "some message" << endl;
115 class RpmDb::Logfile {
116 Logfile( const Logfile & );
117 Logfile & operator=( const Logfile & );
119 static ofstream _log;
120 static unsigned _refcnt;
121 static Pathname _fname;
122 static void openLog() {
123 if ( !_fname.empty() ) {
125 _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
127 ERR << "Could not open logfile '" << _fname << "'" << endl;
130 static void closeLog() {
134 static void refUp() {
139 static void refDown() {
145 Logfile() { refUp(); }
146 ~Logfile() { refDown(); }
147 ostream & operator()( bool timestamp = false ) {
149 _log << Date(Date::now()).form( "%Y-%m-%d %H:%M:%S ");
153 static void setFname( const Pathname & fname_r ) {
154 MIL << "installation log file " << fname_r << endl;
163 ///////////////////////////////////////////////////////////////////
165 Pathname RpmDb::Logfile::_fname;
166 ofstream RpmDb::Logfile::_log;
167 unsigned RpmDb::Logfile::_refcnt = 0;
169 ///////////////////////////////////////////////////////////////////
171 ///////////////////////////////////////////////////////////////////
174 // METHOD NAME : RpmDb::setInstallationLogfile
175 // METHOD TYPE : bool
177 bool RpmDb::setInstallationLogfile( const Pathname & filename )
179 Logfile::setFname( filename );
183 ///////////////////////////////////////////////////////////////////
185 // CLASS NAME : RpmDb::Packages
187 * Helper class for RpmDb::getPackages() to build the
188 * list<Package::Ptr> returned. We have to assert, that there
189 * is a unique entry for every string.
191 * In the first step we build the _index map which helps to catch
192 * multiple occurances of a string in the rpmdb. That's not desired,
193 * but possible. Usg. the last package instance installed is strored
196 * At the end buildList() is called to build the list<Package::Ptr>
197 * from the _index map. _valid is set true to assign that the list
198 * is in sync with the rpmdb content. Operations changing the rpmdb
199 * content (install/remove package) should set _valid to false. The
200 * next call to RpmDb::getPackages() will then reread the the rpmdb.
202 * Note that outside RpmDb::getPackages() _list and _index are always
203 * in sync. So you may use lookup(PkgName) to retrieve a specific
206 class RpmDb::Packages {
208 list<Package::Ptr> _list;
209 map<std::string,Package::Ptr> _index;
211 Packages() : _valid( false ) {}
217 Package::Ptr lookup( const string & name_r ) const {
218 map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
219 if ( got != _index.end() )
221 return Package::Ptr();
225 for ( map<string,Package::Ptr>::iterator iter = _index.begin();
226 iter != _index.end(); ++iter ) {
228 _list.push_back( iter->second );
234 ///////////////////////////////////////////////////////////////////
236 ///////////////////////////////////////////////////////////////////
238 // CLASS NAME : RpmDb
240 ///////////////////////////////////////////////////////////////////
242 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
244 ///////////////////////////////////////////////////////////////////
246 ///////////////////////////////////////////////////////////////////
249 // METHOD NAME : RpmDb::RpmDb
250 // METHOD TYPE : Constructor
253 : _dbStateInfo( DbSI_NO_INIT )
254 , _packages( * new Packages ) // delete in destructor
255 #warning Check for obsolete memebers
256 , _backuppath ("/var/adm/backup")
257 , _packagebackups(false)
258 , _warndirexists(false)
263 // Some rpm versions are patched not to abort installation if
264 // symlink creation failed.
265 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
268 ///////////////////////////////////////////////////////////////////
271 // METHOD NAME : RpmDb::~RpmDb
272 // METHOD TYPE : Destructor
276 MIL << "~RpmDb()" << endl;
281 MIL << "~RpmDb() end" << endl;
284 ///////////////////////////////////////////////////////////////////
287 // METHOD NAME : RpmDb::dumpOn
288 // METHOD TYPE : std::ostream &
290 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
294 if ( _dbStateInfo == DbSI_NO_INIT ) {
297 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
299 ENUM_OUT( DbSI_HAVE_V4, 'X' );
300 ENUM_OUT( DbSI_MADE_V4, 'c' );
301 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
303 ENUM_OUT( DbSI_HAVE_V3, 'X' );
304 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
305 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
306 str << "): " << stringPath( _root, _dbPath );
312 ///////////////////////////////////////////////////////////////////
315 // METHOD NAME : RpmDb::initDatabase
316 // METHOD TYPE : PMError
318 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
320 ///////////////////////////////////////////////////////////////////
322 ///////////////////////////////////////////////////////////////////
323 if ( root_r.empty() )
326 if ( dbPath_r.empty() )
327 dbPath_r = "/var/lib/rpm";
329 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
330 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
331 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
334 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
336 ///////////////////////////////////////////////////////////////////
337 // Check whether already initialized
338 ///////////////////////////////////////////////////////////////////
339 if ( initialized() ) {
340 if ( root_r == _root && dbPath_r == _dbPath ) {
343 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
347 ///////////////////////////////////////////////////////////////////
349 ///////////////////////////////////////////////////////////////////
350 librpmDb::unblockAccess();
351 DbStateInfoBits info = DbSI_NO_INIT;
353 internal_initDatabase( root_r, dbPath_r, info );
355 catch (const RpmException & excpt_r)
357 ZYPP_CAUGHT(excpt_r);
358 librpmDb::blockAccess();
359 ERR << "Cleanup on error: state " << info << endl;
361 if ( dbsi_has( info, DbSI_MADE_V4 ) ) {
362 // remove the newly created rpm4 database and
363 // any backup created on conversion.
364 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
366 ZYPP_RETHROW(excpt_r);
368 if ( dbsi_has( info, DbSI_HAVE_V3 ) ) {
369 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) ) {
370 // Move obsolete rpm3 database beside.
371 MIL << "Cleanup: state " << info << endl;
372 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
373 dbsi_clr( info, DbSI_HAVE_V3 );
375 // Performing an update: Keep the original rpm3 database
376 // and wait if the rpm4 database gets modified by installing
377 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
378 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
381 #warning CHECK: notify root about conversion backup.
387 #warning Add rebuild database once have the info about context
389 if ( ! ( Y2PM::runningFromSystem() ) ) {
390 if ( dbsi_has( info, DbSI_HAVE_V4 )
391 && ! dbsi_has( info, DbSI_MADE_V4 ) ) {
392 err = rebuildDatabase();
397 // Close the database in case any write acces (create/convert)
398 // happened during init. This should drop any lock acquired
399 // by librpm. On demand it will be reopened readonly and should
400 // not hold any lock.
401 librpmDb::dbRelease( true );
402 MIL << "InitDatabase: " << *this << endl;
405 ///////////////////////////////////////////////////////////////////
408 // METHOD NAME : RpmDb::internal_initDatabase
409 // METHOD TYPE : PMError
411 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
412 DbStateInfoBits & info_r )
414 info_r = DbSI_NO_INIT;
416 ///////////////////////////////////////////////////////////////////
417 // Get info about the desired database dir
418 ///////////////////////////////////////////////////////////////////
419 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
421 if ( dbInfo.illegalArgs() ) {
422 // should not happen (checked in initDatabase)
423 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
425 if ( ! dbInfo.usableArgs() ) {
426 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
427 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
430 if ( dbInfo.hasDbV4() ) {
431 dbsi_set( info_r, DbSI_HAVE_V4 );
432 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
434 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
437 if ( dbInfo.hasDbV3() ) {
438 dbsi_set( info_r, DbSI_HAVE_V3 );
440 if ( dbInfo.hasDbV3ToV4() ) {
441 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
444 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
445 librpmDb::dumpState( DBG ) << endl;
447 ///////////////////////////////////////////////////////////////////
448 // Access database, create if needed
449 ///////////////////////////////////////////////////////////////////
451 // creates dbdir and empty rpm4 database if not present
452 librpmDb::dbAccess( root_r, dbPath_r );
454 if ( ! dbInfo.hasDbV4() ) {
456 if ( dbInfo.hasDbV4() ) {
457 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
461 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
462 librpmDb::dumpState( DBG ) << endl;
464 ///////////////////////////////////////////////////////////////////
465 // Check whether to convert something. Create backup but do
466 // not remove anything here
467 ///////////////////////////////////////////////////////////////////
468 librpmDb::constPtr dbptr;
469 librpmDb::dbAccess( dbptr );
470 bool dbEmpty = dbptr->empty();
472 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
475 if ( dbInfo.hasDbV3() ) {
476 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
479 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
480 convertV3toV4( dbInfo.dbV3().path(), dbptr );
482 // create a backup copy
483 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
485 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
488 if ( dbInfo.hasDbV3ToV4() ) {
489 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
490 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
496 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
497 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
498 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
499 //report->start( dbInfo.dbV3().path() );
500 //report->stop( some error );
502 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
503 dbsi_set( info_r, DbSI_MODIFIED_V4 );
507 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
508 librpmDb::dumpState( DBG ) << endl;
511 if ( dbInfo.hasDbV3ToV4() ) {
512 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
516 ///////////////////////////////////////////////////////////////////
519 // METHOD NAME : RpmDb::removeV4
520 // METHOD TYPE : void
522 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
524 const char * v3backup = "packages.rpm3";
525 const char * master = "Packages";
526 const char * index[] = {
547 PathInfo pi( dbdir_r );
548 if ( ! pi.isDir() ) {
549 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
553 for ( const char ** f = index; *f; ++f ) {
556 filesystem::unlink( pi.path() );
560 pi( dbdir_r + master );
562 MIL << "Removing rpm4 database " << pi << endl;
563 filesystem::unlink( pi.path() );
567 pi( dbdir_r + v3backup );
569 MIL << "Removing converted rpm3 database backup " << pi << endl;
570 filesystem::unlink( pi.path() );
575 ///////////////////////////////////////////////////////////////////
578 // METHOD NAME : RpmDb::removeV3
579 // METHOD TYPE : void
581 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
583 const char * master = "packages.rpm";
584 const char * index[] = {
585 "conflictsindex.rpm",
596 PathInfo pi( dbdir_r );
597 if ( ! pi.isDir() ) {
598 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
602 for ( const char ** f = index; *f; ++f ) {
605 filesystem::unlink( pi.path() );
609 #warning CHECK: compare vs existing v3 backup. notify root
610 pi( dbdir_r + master );
612 Pathname m( pi.path() );
614 // backup was already created
615 filesystem::unlink( m );
616 Pathname b( m.extend( "3" ) );
617 pi( b ); // stat backup
619 Pathname b( m.extend( ".deleted" ) );
622 // rempve existing backup
623 filesystem::unlink( b );
625 filesystem::rename( m, b );
626 pi( b ); // stat backup
628 MIL << "(Re)moved rpm3 database to " << pi << endl;
632 ///////////////////////////////////////////////////////////////////
635 // METHOD NAME : RpmDb::modifyDatabase
636 // METHOD TYPE : void
638 void RpmDb::modifyDatabase()
640 if ( ! initialized() )
643 // tag database as modified
644 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
646 // Move outdated rpm3 database beside.
647 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
648 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
649 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
650 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
653 // invalidate Packages list
654 _packages._valid = false;
657 ///////////////////////////////////////////////////////////////////
660 // METHOD NAME : RpmDb::closeDatabase
661 // METHOD TYPE : PMError
663 void RpmDb::closeDatabase()
665 if ( ! initialized() ) {
669 MIL << "Calling closeDatabase: " << *this << endl;
671 ///////////////////////////////////////////////////////////////////
672 // Block further database access
673 ///////////////////////////////////////////////////////////////////
675 librpmDb::blockAccess();
677 ///////////////////////////////////////////////////////////////////
678 // Check fate if old version database still present
679 ///////////////////////////////////////////////////////////////////
680 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
681 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
682 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
683 // Move outdated rpm3 database beside.
684 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
686 // Remove unmodified rpm4 database
687 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
691 ///////////////////////////////////////////////////////////////////
693 ///////////////////////////////////////////////////////////////////
694 _root = _dbPath = Pathname();
695 _dbStateInfo = DbSI_NO_INIT;
697 MIL << "closeDatabase: " << *this << endl;
700 ///////////////////////////////////////////////////////////////////
703 // METHOD NAME : RpmDb::rebuildDatabase
704 // METHOD TYPE : PMError
706 void RpmDb::rebuildDatabase()
708 RebuildDbReport report;
710 doRebuildDatabase(report);
712 catch (RpmException & excpt_r)
715 ZYPP_RETHROW(excpt_r);
720 void RpmDb::doRebuildDatabase(RebuildDbReport & report)
722 FAILIFNOTINITIALIZED;
724 MIL << "RpmDb::rebuildDatabase" << *this << endl;
725 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
727 PathInfo dbMaster( root() + dbPath() + "Packages" );
728 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
732 opts.push_back("--rebuilddb");
733 opts.push_back("-vv");
735 // don't call modifyDatabase because it would remove the old
736 // rpm3 database, if the current database is a temporary one.
737 // But do invalidate packages list.
738 _packages._valid = false;
739 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
741 // progress report: watch this file growing
742 PathInfo newMaster( root()
743 + dbPath().extend( str::form( "rebuilddb.%d",
744 process?process->getpid():0) )
750 while ( systemReadLine( line ) ) {
751 if ( newMaster() ) { // file is removed at the end of rebuild.
752 // current size should be upper limit for new db
753 report.progress( (100 * newMaster.size()) / dbMaster.size());
756 if ( line.compare( 0, 2, "D:" ) ) {
757 errmsg += line + '\n';
758 // report.notify( line );
763 int rpm_status = systemStatus();
765 if ( rpm_status != 0 ) {
766 ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
768 report.progress( 100 ); // 100%
772 ///////////////////////////////////////////////////////////////////
775 // METHOD NAME : RpmDb::importPubkey
776 // METHOD TYPE : PMError
778 void RpmDb::importPubkey( const Pathname & pubkey_r )
780 FAILIFNOTINITIALIZED;
783 opts.push_back ( "--import" );
784 opts.push_back ( "--" );
785 opts.push_back ( pubkey_r.asString().c_str() );
787 // don't call modifyDatabase because it would remove the old
788 // rpm3 database, if the current database is a temporary one.
789 // But do invalidate packages list.
790 _packages._valid = false;
791 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
794 while ( systemReadLine( line ) ) {
795 if ( line.substr( 0, 6 ) == "error:" ) {
802 int rpm_status = systemStatus();
804 if ( rpm_status != 0 ) {
805 ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned ") + str::numstring(rpm_status)));
807 MIL << "Imported public key from file " << pubkey_r << endl;
811 ///////////////////////////////////////////////////////////////////
814 // METHOD NAME : RpmDb::importPubkey
815 // METHOD TYPE : PMError
817 void RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
819 FAILIFNOTINITIALIZED;
822 char tmpname[] = "/tmp/zypp.pubkey.XXXXXX";
823 int tmpfd = mkstemp( tmpname );
825 ZYPP_THROW(RpmSubprocessException("Unable to create a unique temporary file for pubkey"));
828 // export keyname from keyring
830 args.push_back( "gpg" );
831 args.push_back( "--armor" );
832 args.push_back( "--no-default-keyring" );
833 args.push_back( "--keyring" );
834 args.push_back( keyring_r.asString().c_str() );
835 args.push_back( "--export" );
836 args.push_back( keyname_r.c_str() );
838 const char * argv[args.size() + 1];
839 const char ** p = argv;
840 p = copy( args.begin(), args.end(), p );
844 ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
850 for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
851 ssize_t written = write( tmpfd, line.c_str(), line.length() );
852 if ( written == -1 || unsigned(written) != line.length() ) {
853 ZYPP_THROW(RpmSubprocessException(string("Error writing pubkey to ") + tmpname));
855 res += written; // empty file indicates key not found
858 catch (RpmException & excpt_r)
860 ZYPP_CAUGHT(excpt_r);
862 filesystem::unlink( tmpname );
863 ZYPP_RETHROW(excpt_r);
868 ZYPP_THROW(RpmSubprocessException(string("gpg: no key '") + keyname_r + string("' found in '") + keyring_r.asString() + string("'")));
871 // check gpg returncode
875 filesystem::unlink( tmpname );
876 ZYPP_THROW(RpmSubprocessException(string("gpg: export '") + keyname_r + string("' from '") + keyring_r.asString() + "' returned " + str::numstring(res)));
879 MIL << "Exported '" << keyname_r << "' from '" << keyring_r << "' to " << tmpname << endl;
881 importPubkey( tmpname );
883 catch (RpmException & excpt_r)
885 ZYPP_CAUGHT(excpt_r);
886 filesystem::unlink( tmpname );
887 ZYPP_RETHROW(excpt_r);
889 filesystem::unlink( tmpname );
892 ///////////////////////////////////////////////////////////////////
895 // METHOD NAME : RpmDb::pubkeys
896 // METHOD TYPE : set<Edition>
898 set<Edition> RpmDb::pubkeys() const
902 librpmDb::db_const_iterator it;
903 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it ) {
904 ret.insert( it->tag_edition() );
910 ///////////////////////////////////////////////////////////////////
913 // METHOD NAME : RpmDb::packagesValid
914 // METHOD TYPE : bool
916 bool RpmDb::packagesValid() const
918 return( _packages._valid || ! initialized() );
921 ///////////////////////////////////////////////////////////////////
924 // METHOD NAME : RpmDb::getPackages
925 // METHOD TYPE : const std::list<Package::Ptr> &
929 const std::list<Package::Ptr> & RpmDb::getPackages()
933 const std::list<Package::Ptr> & ret = doGetPackages(report);
937 catch (RpmException & excpt_r)
940 ZYPP_RETHROW(excpt_r);
945 const std::list<Package::Ptr> & RpmDb::doGetPackages(ScanDbReport & report)
947 if ( packagesValid() ) {
948 return _packages._list;
951 // FIXME Timecount _t( "RpmDb::getPackages" );
953 #warning how to detect corrupt db while reading.
957 ///////////////////////////////////////////////////////////////////
958 // Collect package data. A map is used to check whethere there are
959 // multiple entries for the same string. If so we consider the last
960 // one installed to be the one we're interesed in.
961 ///////////////////////////////////////////////////////////////////
963 librpmDb::db_const_iterator iter; // findAll
966 for ( ; *iter; ++iter ) {
969 if ( iter.dbError() ) {
970 ERR << "No database access: " << iter.dbError() << endl;
971 ZYPP_THROW(*(iter.dbError()));
974 unsigned current = 0;
976 for ( iter.findAll(); *iter; ++iter, ++current, report.progress( (100*current)/expect)) {
978 string name = iter->tag_name();
979 if ( name == string( "gpg-pubkey" ) ) {
980 // pseudo package filtered, as we can't handle multiple instances
981 // of 'gpg-pubkey-VERS-REL'.
984 Date installtime = iter->tag_installtime();
985 Package::Ptr & nptr = _packages._index[name]; // be shure to get a reference!
988 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
989 if ( nptr->installtime() > installtime )
991 // else overwrite previous entry
994 // create dataprovider and package
995 shared_ptr<RPMPackageImpl> impl(new RPMPackageImpl(*iter));
996 nptr = detail::makeResolvableFromImpl(
1003 _deps.setProvides(iter->tag_provides ( & _filerequires ) );
1004 _deps.setRequires ( iter->tag_requires ( &_filerequires ) );
1005 _deps.setPrerequires ( iter->tag_prerequires ( &_filerequires ) );
1006 _deps.setConflicts( iter->tag_conflicts( &_filerequires ) );
1007 _deps.setObsoletes( iter->tag_obsoletes( &_filerequires ) );
1008 nptr->setDeps(_deps);
1011 ///////////////////////////////////////////////////////////////////
1012 // Evaluate filerequires collected so far
1013 ///////////////////////////////////////////////////////////////////
1014 for( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
1016 for ( iter.findByFile( *it ); *iter; ++iter ) {
1017 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1019 WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1022 Dependencies _deps = pptr->deps();
1023 #warning Add FileDeps
1024 // pptr->addProvides( *it );
1025 pptr->setDeps(_deps);
1030 ///////////////////////////////////////////////////////////////////
1031 // Build final packages list
1032 ///////////////////////////////////////////////////////////////////
1033 _packages.buildList();
1034 DBG << "Found installed packages: " << _packages._list.size() << endl;
1035 return _packages._list;
1038 #warning Uncomment this function
1040 ///////////////////////////////////////////////////////////////////
1043 // METHOD NAME : RpmDb::traceFileRel
1044 // METHOD TYPE : void
1048 void RpmDb::traceFileRel( const PkgRelation & rel_r )
1050 if ( ! rel_r.isFileRel() )
1053 if ( ! _filerequires.insert( rel_r.name() ).second )
1054 return; // already got it in _filerequires
1056 if ( ! _packages._valid )
1057 return; // collect only. Evaluated in first call to getPackages()
1060 // packages already initialized. Must check and insert here
1062 librpmDb::db_const_iterator iter;
1063 if ( iter.dbError() ) {
1064 ERR << "No database access: " << iter.dbError() << endl;
1068 for ( iter.findByFile( rel_r.name() ); *iter; ++iter ) {
1069 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1071 WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
1074 pptr->addProvides( rel_r.name() );
1079 ///////////////////////////////////////////////////////////////////
1082 // METHOD NAME : RpmDb::hasFile
1083 // METHOD TYPE : bool
1087 bool RpmDb::hasFile( const std::string & file_r ) const
1089 librpmDb::db_const_iterator it;
1090 return it.findByFile( file_r );
1093 ///////////////////////////////////////////////////////////////////
1096 // METHOD NAME : RpmDb::hasProvides
1097 // METHOD TYPE : bool
1101 bool RpmDb::hasProvides( const std::string & tag_r ) const
1103 librpmDb::db_const_iterator it;
1104 return it.findByProvides( tag_r );
1107 ///////////////////////////////////////////////////////////////////
1110 // METHOD NAME : RpmDb::hasRequiredBy
1111 // METHOD TYPE : bool
1115 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1117 librpmDb::db_const_iterator it;
1118 return it.findByRequiredBy( tag_r );
1121 ///////////////////////////////////////////////////////////////////
1124 // METHOD NAME : RpmDb::hasConflicts
1125 // METHOD TYPE : bool
1129 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1131 librpmDb::db_const_iterator it;
1132 return it.findByConflicts( tag_r );
1135 ///////////////////////////////////////////////////////////////////
1138 // METHOD NAME : RpmDb::hasPackage
1139 // METHOD TYPE : bool
1143 bool RpmDb::hasPackage( const string & name_r ) const
1145 librpmDb::db_const_iterator it;
1146 return it.findPackage( name_r );
1149 ///////////////////////////////////////////////////////////////////
1152 // METHOD NAME : RpmDb::getData
1153 // METHOD TYPE : PMError
1157 void RpmDb::getData( const string & name_r,
1158 RpmHeader::constPtr & result_r ) const
1160 librpmDb::db_const_iterator it;
1161 it.findPackage( name_r );
1164 ZYPP_THROW(*(it.dbError()));
1167 ///////////////////////////////////////////////////////////////////
1170 // METHOD NAME : RpmDb::getData
1171 // METHOD TYPE : PMError
1175 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1176 RpmHeader::constPtr & result_r ) const
1178 librpmDb::db_const_iterator it;
1179 it.findPackage( name_r, ed_r );
1182 ZYPP_THROW(*(it.dbError()));
1185 /*--------------------------------------------------------------*/
1186 /* Checking the source rpm <rpmpath> with rpm --chcksig and */
1187 /* the version number. */
1188 /*--------------------------------------------------------------*/
1190 RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
1192 unsigned result = 0;
1194 if ( ! version.empty() ) {
1195 RpmHeader::constPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
1196 if ( ! h || Edition( version ) != h->tag_edition() ) {
1197 result |= CHK_INCORRECT_VERSION;
1203 #warning TBD MD5 check
1204 WAR << "md5sum check not yet implemented" << endl;
1205 return CHK_INCORRECT_FILEMD5;
1208 std::string path = packagePath.asString();
1209 // checking --checksig
1210 const char *const argv[] = {
1211 "rpm", "--checksig", "--", path.c_str(), 0
1218 for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
1220 output = output + " " + argv[k];
1223 DBG << "rpm command: " << output << endl;
1225 if ( process != NULL )
1230 // Launch the program
1231 process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1234 if ( process == NULL )
1236 result |= CHK_OTHER_FAILURE;
1237 DBG << "create process failed" << endl;
1241 output = process->receiveLine();
1243 while ( output.length() > 0)
1245 string::size_type ret;
1248 ret = output.find_first_of ( "\n" );
1249 if ( ret != string::npos )
1251 value.assign ( output, 0, ret );
1258 DBG << "stdout: " << value << endl;
1260 string::size_type pos;
1261 if((pos = value.find (path)) != string::npos)
1263 string rest = value.substr (pos + path.length() + 1);
1264 if (rest.find("NOT OK") == string::npos)
1266 // see what checks are ok
1267 if (rest.find("md5") == string::npos)
1269 result |= CHK_MD5SUM_MISSING;
1271 if (rest.find("gpg") == string::npos)
1273 result |= CHK_GPGSIG_MISSING;
1278 // see what checks are not ok
1279 if (rest.find("MD5") != string::npos)
1281 result |= CHK_INCORRECT_PKGMD5;
1285 result |= CHK_MD5SUM_MISSING;
1288 if (rest.find("GPG") != string::npos)
1290 result |= CHK_INCORRECT_GPGSIG;
1294 result |= CHK_GPGSIG_MISSING;
1299 output = process->receiveLine();
1302 if ( result == 0 && systemStatus() != 0 )
1305 result |= CHK_OTHER_FAILURE;
1311 // determine changed files of installed package
1313 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1319 if( ! initialized() ) return false;
1323 opts.push_back ("-V");
1324 opts.push_back ("--nodeps");
1325 opts.push_back ("--noscripts");
1326 opts.push_back ("--nomd5");
1327 opts.push_back ("--");
1328 opts.push_back (packageName.c_str());
1330 run_rpm (opts, ExternalProgram::Discard_Stderr);
1332 if ( process == NULL )
1343 M Mode (includes permissions and file type)
1347 while (systemReadLine(line))
1349 if (line.length() > 12 &&
1350 (line[0] == 'S' || line[0] == 's' ||
1351 (line[0] == '.' && line[7] == 'T')))
1353 // file has been changed
1356 filename.assign(line, 11, line.length() - 11);
1357 fileList.insert(filename);
1362 // exit code ignored, rpm returns 1 no matter if package is installed or
1370 /****************************************************************/
1371 /* private member-functions */
1372 /****************************************************************/
1374 /*--------------------------------------------------------------*/
1375 /* Run rpm with the specified arguments, handling stderr */
1376 /* as specified by disp */
1377 /*--------------------------------------------------------------*/
1379 RpmDb::run_rpm (const RpmArgVec& opts,
1380 ExternalProgram::Stderr_Disposition disp)
1388 if ( ! initialized() ) {
1389 ZYPP_THROW(RpmDbNotOpenException());
1394 // always set root and dbpath
1395 args.push_back("rpm");
1396 args.push_back("--root");
1397 args.push_back(_root.asString().c_str());
1398 args.push_back("--dbpath");
1399 args.push_back(_dbPath.asString().c_str());
1401 const char* argv[args.size() + opts.size() + 1];
1403 const char** p = argv;
1404 p = copy (args.begin (), args.end (), p);
1405 p = copy (opts.begin (), opts.end (), p);
1408 // Invalidate all outstanding database handles in case
1409 // the database gets modified.
1410 librpmDb::dbRelease( true );
1412 // Launch the program with default locale
1413 process = new ExternalProgram(argv, disp, false, -1, true);
1417 /*--------------------------------------------------------------*/
1418 /* Read a line from the rpm process */
1419 /*--------------------------------------------------------------*/
1421 RpmDb::systemReadLine(string &line)
1425 if ( process == NULL )
1428 line = process->receiveLine();
1430 if (line.length() == 0)
1433 if (line[line.length() - 1] == '\n')
1434 line.erase(line.length() - 1);
1439 /*--------------------------------------------------------------*/
1440 /* Return the exit status of the rpm process, closing the */
1441 /* connection if not already done */
1442 /*--------------------------------------------------------------*/
1444 RpmDb::systemStatus()
1446 if ( process == NULL )
1449 exit_code = process->close();
1454 // DBG << "exit code " << exit_code << endl;
1459 /*--------------------------------------------------------------*/
1460 /* Forcably kill the rpm process */
1461 /*--------------------------------------------------------------*/
1465 if (process) process->kill();
1469 // generate diff mails for config files
1470 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1472 string msg = line.substr(9);
1473 string::size_type pos1 = string::npos;
1474 string::size_type pos2 = string::npos;
1475 string file1s, file2s;
1479 pos1 = msg.find (typemsg);
1482 if( pos1 == string::npos )
1485 pos2 = pos1 + strlen (typemsg);
1487 if (pos2 >= msg.length() )
1490 file1 = msg.substr (0, pos1);
1491 file2 = msg.substr (pos2);
1493 file1s = file1.asString();
1494 file2s = file2.asString();
1496 if (!_root.empty() && _root != "/")
1498 file1 = _root + file1;
1499 file2 = _root + file2;
1503 #warning FIXME the diffing functionality
1505 int ret = Diff::differ (file1.asString(), file2.asString(), out, 25);
1508 Pathname file = _root + WARNINGMAILPATH;
1509 if (filesystem::assert_dir(file) != 0)
1511 ERR << "Could not create " << file.asString() << endl;
1514 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1515 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1518 ERR << "Could not open " << file << endl;
1522 // Translator: %s = name of an rpm package. A list of diffs follows
1524 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1527 ERR << "diff failed" << endl;
1528 notify << str::form(difffailmsg,
1529 file1s.c_str(), file2s.c_str()) << endl;
1533 notify << str::form(diffgenmsg,
1534 file1s.c_str(), file2s.c_str()) << endl;
1536 // remove root for the viewer's pleasure (#38240)
1537 if (!_root.empty() && _root != "/")
1539 if(out.substr(0,4) == "--- ")
1541 out.replace(4, file1.asString().length(), file1s);
1543 string::size_type pos = out.find("\n+++ ");
1544 if(pos != string::npos)
1546 out.replace(pos+5, file2.asString().length(), file2s);
1549 notify << out << endl;
1552 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1557 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1564 ///////////////////////////////////////////////////////////////////
1567 // METHOD NAME : RpmDb::installPackage
1568 // METHOD TYPE : PMError
1570 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1572 RpmInstallReport report;
1574 doInstallPackage(filename, flags, report);
1576 catch (RpmException & excpt_r)
1578 report.end(excpt_r);
1579 ZYPP_RETHROW(excpt_r);
1584 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, RpmInstallReport & report )
1586 FAILIFNOTINITIALIZED;
1587 Logfile progresslog;
1589 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1592 if ( _packagebackups ) {
1593 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1594 if ( ! backupPackage( filename ) ) {
1595 ERR << "backup of " << filename.asString() << " failed" << endl;
1597 // FIXME status handling
1598 report.progress( 0 ); // allow 1% for backup creation.
1600 report.progress( 100 );
1605 if (flags & RPMINST_NOUPGRADE)
1606 opts.push_back("-i");
1608 opts.push_back("-U");
1609 opts.push_back("--percent");
1611 if (flags & RPMINST_NODIGEST)
1612 opts.push_back("--nodigest");
1613 if (flags & RPMINST_NOSIGNATURE)
1614 opts.push_back("--nosignature");
1615 if (flags & RPMINST_NODOCS)
1616 opts.push_back ("--excludedocs");
1617 if (flags & RPMINST_NOSCRIPTS)
1618 opts.push_back ("--noscripts");
1619 if (flags & RPMINST_FORCE)
1620 opts.push_back ("--force");
1621 if (flags & RPMINST_NODEPS)
1622 opts.push_back ("--nodeps");
1623 if(flags & RPMINST_IGNORESIZE)
1624 opts.push_back ("--ignoresize");
1625 if(flags & RPMINST_JUSTDB)
1626 opts.push_back ("--justdb");
1628 opts.push_back("--");
1629 opts.push_back (filename.asString().c_str());
1631 modifyDatabase(); // BEFORE run_rpm
1632 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1636 vector<string> configwarnings;
1637 vector<string> errorlines;
1639 while (systemReadLine(line))
1641 if (line.substr(0,2)=="%%")
1644 sscanf (line.c_str () + 2, "%d", &percent);
1645 report.progress( percent );
1648 rpmmsg += line+'\n';
1650 if( line.substr(0,8) == "warning:" )
1652 configwarnings.push_back(line);
1655 int rpm_status = systemStatus();
1658 for(vector<string>::iterator it = configwarnings.begin();
1659 it != configwarnings.end(); ++it)
1661 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1663 _("rpm saved %s as %s but it was impossible to determine the difference"),
1665 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1666 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1668 _("rpm created %s as %s but it was impossible to determine the difference"),
1670 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1673 if ( rpm_status != 0 ) {
1674 // %s = filename of rpm package
1675 progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1676 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1677 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1679 // %s = filename of rpm package
1680 progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1681 if( ! rpmmsg.empty() ) {
1682 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1687 ///////////////////////////////////////////////////////////////////
1690 // METHOD NAME : RpmDb::removePackage
1691 // METHOD TYPE : PMError
1693 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1695 return removePackage( package->name(), flags );
1698 ///////////////////////////////////////////////////////////////////
1701 // METHOD NAME : RpmDb::removePackage
1702 // METHOD TYPE : PMError
1704 void RpmDb::removePackage( const string & name_r, unsigned flags )
1706 RpmRemoveReport report;
1708 doRemovePackage(name_r, flags, report);
1710 catch (RpmException & excpt_r)
1712 report.end(excpt_r);
1713 ZYPP_RETHROW(excpt_r);
1719 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, RpmRemoveReport & report )
1721 FAILIFNOTINITIALIZED;
1722 Logfile progresslog;
1724 MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
1727 if ( _packagebackups ) {
1728 // FIXME solve this status report somehow
1729 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1730 if ( ! backupPackage( name_r ) ) {
1731 ERR << "backup of " << name_r << " failed" << endl;
1733 report.progress( 0 );
1735 report.progress( 100 );
1740 opts.push_back("-e");
1741 opts.push_back("--allmatches");
1743 if (flags & RPMINST_NOSCRIPTS)
1744 opts.push_back("--noscripts");
1745 if (flags & RPMINST_NODEPS)
1746 opts.push_back("--nodeps");
1747 if (flags & RPMINST_JUSTDB)
1748 opts.push_back("--justdb");
1749 if (flags & RPMINST_FORCE) {
1750 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1753 opts.push_back("--");
1754 opts.push_back(name_r.c_str());
1756 modifyDatabase(); // BEFORE run_rpm
1757 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1762 // got no progress from command, so we fake it:
1763 // 5 - command started
1764 // 50 - command completed
1766 report.progress( 5 );
1767 while (systemReadLine(line))
1769 rpmmsg += line+'\n';
1771 report.progress( 50 );
1772 int rpm_status = systemStatus();
1774 if ( rpm_status != 0 ) {
1775 // %s = name of rpm package
1776 progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
1777 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1778 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1780 progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
1781 if( ! rpmmsg.empty() ) {
1782 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1788 RpmDb::checkPackageResult2string(unsigned code)
1791 // begin of line characters
1793 // end of line characters
1796 return string(_("Ok"))+eol;
1798 //translator: these are different kinds of how an rpm package can be broken
1799 msg = _("Package is not OK for the following reasons:");
1802 if(code&CHK_INCORRECT_VERSION)
1805 msg+=_("Package contains different version than expected");
1808 if(code&CHK_INCORRECT_FILEMD5)
1811 msg+=_("Package file has incorrect MD5 sum");
1814 if(code&CHK_GPGSIG_MISSING)
1817 msg+=_("Package is not signed");
1820 if(code&CHK_MD5SUM_MISSING)
1823 msg+=_("Package has no MD5 sum");
1826 if(code&CHK_INCORRECT_GPGSIG)
1829 msg+=_("Package has incorrect signature");
1832 if(code&CHK_INCORRECT_PKGMD5)
1835 msg+=_("Package archive has incorrect MD5 sum");
1838 if(code&CHK_OTHER_FAILURE)
1841 msg+=_("rpm failed for unkown reason, see log file");
1848 ///////////////////////////////////////////////////////////////////
1851 // METHOD NAME : RpmDb::backupPackage
1852 // METHOD TYPE : bool
1854 bool RpmDb::backupPackage( const Pathname & filename )
1856 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1860 return backupPackage( h->tag_name() );
1863 ///////////////////////////////////////////////////////////////////
1866 // METHOD NAME : RpmDb::backupPackage
1867 // METHOD TYPE : bool
1869 bool RpmDb::backupPackage(const string& packageName)
1871 Logfile progresslog;
1873 Pathname backupFilename;
1874 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1876 if (_backuppath.empty())
1878 INT << "_backuppath empty" << endl;
1884 if (!queryChangedFiles(fileList, packageName))
1886 ERR << "Error while getting changed files for package " <<
1887 packageName << endl;
1891 if (fileList.size() <= 0)
1893 DBG << "package " << packageName << " not changed -> no backup" << endl;
1897 if (filesystem::assert_dir(_root + _backuppath) != 0)
1903 // build up archive name
1904 time_t currentTime = time(0);
1905 struct tm *currentLocalTime = localtime(¤tTime);
1907 int date = (currentLocalTime->tm_year + 1900) * 10000
1908 + (currentLocalTime->tm_mon + 1) * 100
1909 + currentLocalTime->tm_mday;
1914 backupFilename = _root + _backuppath
1915 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1918 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1920 PathInfo pi(filestobackupfile);
1921 if(pi.isExist() && !pi.isFile())
1923 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1927 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1931 ERR << "could not open " << filestobackupfile.asString() << endl;
1935 for (FileList::const_iterator cit = fileList.begin();
1936 cit != fileList.end(); ++cit)
1939 if ( name[0] == '/' )
1941 // remove slash, file must be relative to -C parameter of tar
1942 name = name.substr( 1 );
1944 DBG << "saving file "<< name << endl;
1949 const char* const argv[] =
1954 _root.asString().c_str(),
1955 "--ignore-failed-read",
1957 backupFilename.asString().c_str(),
1959 filestobackupfile.asString().c_str(),
1963 // execute tar in inst-sys (we dont know if there is a tar below _root !)
1964 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1968 // TODO: its probably possible to start tar with -v and watch it adding
1969 // files to report progress
1970 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
1975 int ret = tar.close();
1979 ERR << "tar failed: " << tarmsg << endl;
1984 MIL << "tar backup ok" << endl;
1985 progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
1988 filesystem::unlink(filestobackupfile);
1994 void RpmDb::setBackupPath(const Pathname& path)
2000 } // namespace target