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 constlibrpmDbPtr & v4db_r );
480 #warning FIXME exception handling for following function
481 convertV3toV4( dbInfo.dbV3().path(), dbptr );
483 // create a backup copy
484 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
486 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
489 if ( dbInfo.hasDbV3ToV4() ) {
490 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
491 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
497 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
498 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
499 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
500 //report->start( dbInfo.dbV3().path() );
501 //report->stop( some error );
503 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
504 dbsi_set( info_r, DbSI_MODIFIED_V4 );
508 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
509 librpmDb::dumpState( DBG ) << endl;
512 if ( dbInfo.hasDbV3ToV4() ) {
513 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
517 ///////////////////////////////////////////////////////////////////
520 // METHOD NAME : RpmDb::removeV4
521 // METHOD TYPE : void
523 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
525 const char * v3backup = "packages.rpm3";
526 const char * master = "Packages";
527 const char * index[] = {
548 PathInfo pi( dbdir_r );
549 if ( ! pi.isDir() ) {
550 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
554 for ( const char ** f = index; *f; ++f ) {
557 filesystem::unlink( pi.path() );
561 pi( dbdir_r + master );
563 MIL << "Removing rpm4 database " << pi << endl;
564 filesystem::unlink( pi.path() );
568 pi( dbdir_r + v3backup );
570 MIL << "Removing converted rpm3 database backup " << pi << endl;
571 filesystem::unlink( pi.path() );
576 ///////////////////////////////////////////////////////////////////
579 // METHOD NAME : RpmDb::removeV3
580 // METHOD TYPE : void
582 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
584 const char * master = "packages.rpm";
585 const char * index[] = {
586 "conflictsindex.rpm",
597 PathInfo pi( dbdir_r );
598 if ( ! pi.isDir() ) {
599 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
603 for ( const char ** f = index; *f; ++f ) {
606 filesystem::unlink( pi.path() );
610 #warning CHECK: compare vs existing v3 backup. notify root
611 pi( dbdir_r + master );
613 Pathname m( pi.path() );
615 // backup was already created
616 filesystem::unlink( m );
617 Pathname b( m.extend( "3" ) );
618 pi( b ); // stat backup
620 Pathname b( m.extend( ".deleted" ) );
623 // rempve existing backup
624 filesystem::unlink( b );
626 filesystem::rename( m, b );
627 pi( b ); // stat backup
629 MIL << "(Re)moved rpm3 database to " << pi << endl;
633 ///////////////////////////////////////////////////////////////////
636 // METHOD NAME : RpmDb::modifyDatabase
637 // METHOD TYPE : void
639 void RpmDb::modifyDatabase()
641 if ( ! initialized() )
644 // tag database as modified
645 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
647 // Move outdated rpm3 database beside.
648 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
649 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
650 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
651 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
654 // invalidate Packages list
655 _packages._valid = false;
658 ///////////////////////////////////////////////////////////////////
661 // METHOD NAME : RpmDb::closeDatabase
662 // METHOD TYPE : PMError
664 void RpmDb::closeDatabase()
666 if ( ! initialized() ) {
670 MIL << "Calling closeDatabase: " << *this << endl;
672 ///////////////////////////////////////////////////////////////////
673 // Block further database access
674 ///////////////////////////////////////////////////////////////////
676 librpmDb::blockAccess();
678 ///////////////////////////////////////////////////////////////////
679 // Check fate if old version database still present
680 ///////////////////////////////////////////////////////////////////
681 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
682 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
683 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
684 // Move outdated rpm3 database beside.
685 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
687 // Remove unmodified rpm4 database
688 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
692 ///////////////////////////////////////////////////////////////////
694 ///////////////////////////////////////////////////////////////////
695 _root = _dbPath = Pathname();
696 _dbStateInfo = DbSI_NO_INIT;
698 MIL << "closeDatabase: " << *this << endl;
701 ///////////////////////////////////////////////////////////////////
704 // METHOD NAME : RpmDb::rebuildDatabase
705 // METHOD TYPE : PMError
707 void RpmDb::rebuildDatabase()
709 RebuildDbReport report;
711 doRebuildDatabase(report);
713 catch (RpmException & excpt_r)
716 ZYPP_RETHROW(excpt_r);
721 void RpmDb::doRebuildDatabase(RebuildDbReport & report)
723 FAILIFNOTINITIALIZED;
725 MIL << "RpmDb::rebuildDatabase" << *this << endl;
726 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
728 PathInfo dbMaster( root() + dbPath() + "Packages" );
729 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
733 opts.push_back("--rebuilddb");
734 opts.push_back("-vv");
736 // don't call modifyDatabase because it would remove the old
737 // rpm3 database, if the current database is a temporary one.
738 // But do invalidate packages list.
739 _packages._valid = false;
740 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
742 // progress report: watch this file growing
743 PathInfo newMaster( root()
744 + dbPath().extend( str::form( "rebuilddb.%d",
745 process?process->getpid():0) )
751 while ( systemReadLine( line ) ) {
752 if ( newMaster() ) { // file is removed at the end of rebuild.
753 // current size should be upper limit for new db
754 report.progress( (100 * newMaster.size()) / dbMaster.size());
757 if ( line.compare( 0, 2, "D:" ) ) {
758 errmsg += line + '\n';
759 // report.notify( line );
764 int rpm_status = systemStatus();
766 if ( rpm_status != 0 ) {
767 ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
769 report.progress( 100 ); // 100%
773 ///////////////////////////////////////////////////////////////////
776 // METHOD NAME : RpmDb::importPubkey
777 // METHOD TYPE : PMError
779 void RpmDb::importPubkey( const Pathname & pubkey_r )
781 FAILIFNOTINITIALIZED;
784 opts.push_back ( "--import" );
785 opts.push_back ( "--" );
786 opts.push_back ( pubkey_r.asString().c_str() );
788 // don't call modifyDatabase because it would remove the old
789 // rpm3 database, if the current database is a temporary one.
790 // But do invalidate packages list.
791 _packages._valid = false;
792 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
795 while ( systemReadLine( line ) ) {
796 if ( line.substr( 0, 6 ) == "error:" ) {
803 int rpm_status = systemStatus();
805 if ( rpm_status != 0 ) {
806 ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned ") + str::numstring(rpm_status)));
808 MIL << "Imported public key from file " << pubkey_r << endl;
812 ///////////////////////////////////////////////////////////////////
815 // METHOD NAME : RpmDb::importPubkey
816 // METHOD TYPE : PMError
818 void RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
820 FAILIFNOTINITIALIZED;
823 char tmpname[] = "/tmp/zypp.pubkey.XXXXXX";
824 int tmpfd = mkstemp( tmpname );
826 ZYPP_THROW(RpmSubprocessException("Unable to create a unique temporary file for pubkey"));
829 // export keyname from keyring
831 args.push_back( "gpg" );
832 args.push_back( "--armor" );
833 args.push_back( "--no-default-keyring" );
834 args.push_back( "--keyring" );
835 args.push_back( keyring_r.asString().c_str() );
836 args.push_back( "--export" );
837 args.push_back( keyname_r.c_str() );
839 const char * argv[args.size() + 1];
840 const char ** p = argv;
841 p = copy( args.begin(), args.end(), p );
845 ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
851 for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
852 ssize_t written = write( tmpfd, line.c_str(), line.length() );
853 if ( written == -1 || unsigned(written) != line.length() ) {
854 ZYPP_THROW(RpmSubprocessException(string("Error writing pubkey to ") + tmpname));
856 res += written; // empty file indicates key not found
859 catch (RpmException & 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;
880 #warning FIXME handle exception from line below
881 importPubkey( tmpname );
882 filesystem::unlink( tmpname );
885 ///////////////////////////////////////////////////////////////////
888 // METHOD NAME : RpmDb::pubkeys
889 // METHOD TYPE : set<Edition>
891 set<Edition> RpmDb::pubkeys() const
895 librpmDb::db_const_iterator it;
896 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it ) {
897 ret.insert( it->tag_edition() );
903 ///////////////////////////////////////////////////////////////////
906 // METHOD NAME : RpmDb::packagesValid
907 // METHOD TYPE : bool
909 bool RpmDb::packagesValid() const
911 return( _packages._valid || ! initialized() );
914 ///////////////////////////////////////////////////////////////////
917 // METHOD NAME : RpmDb::getPackages
918 // METHOD TYPE : const std::list<Package::Ptr> &
922 const std::list<Package::Ptr> & RpmDb::getPackages()
926 const std::list<Package::Ptr> & ret = doGetPackages(report);
930 catch (RpmException & excpt_r)
933 ZYPP_RETHROW(excpt_r);
938 const std::list<Package::Ptr> & RpmDb::doGetPackages(ScanDbReport & report)
940 if ( packagesValid() ) {
941 return _packages._list;
944 // FIXME Timecount _t( "RpmDb::getPackages" );
946 #warning how to detect corrupt db while reading.
950 ///////////////////////////////////////////////////////////////////
951 // Collect package data. A map is used to check whethere there are
952 // multiple entries for the same string. If so we consider the last
953 // one installed to be the one we're interesed in.
954 ///////////////////////////////////////////////////////////////////
956 librpmDb::db_const_iterator iter; // findAll
959 for ( ; *iter; ++iter ) {
962 if ( iter.dbError() ) {
963 ERR << "No database access: " << iter.dbError() << endl;
964 ZYPP_THROW(*(iter.dbError()));
967 unsigned current = 0;
969 for ( iter.findAll(); *iter; ++iter, ++current, report.progress( (100*current)/expect)) {
971 string name = iter->tag_name();
972 if ( name == string( "gpg-pubkey" ) ) {
973 // pseudo package filtered, as we can't handle multiple instances
974 // of 'gpg-pubkey-VERS-REL'.
977 Date installtime = iter->tag_installtime();
978 Package::Ptr & nptr = _packages._index[name]; // be shure to get a reference!
981 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
982 if ( nptr->installtime() > installtime )
984 // else overwrite previous entry
987 // create dataprovider and package
988 shared_ptr<RPMPackageImpl> impl(new RPMPackageImpl(*iter));
989 nptr = detail::makeResolvableFromImpl(
996 _deps.setProvides(iter->tag_provides ( & _filerequires ) );
997 _deps.setRequires ( iter->tag_requires ( &_filerequires ) );
998 _deps.setPrerequires ( iter->tag_prerequires ( &_filerequires ) );
999 _deps.setConflicts( iter->tag_conflicts( &_filerequires ) );
1000 _deps.setObsoletes( iter->tag_obsoletes( &_filerequires ) );
1001 nptr->setDeps(_deps);
1004 ///////////////////////////////////////////////////////////////////
1005 // Evaluate filerequires collected so far
1006 ///////////////////////////////////////////////////////////////////
1007 for( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
1009 for ( iter.findByFile( *it ); *iter; ++iter ) {
1010 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1012 WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1015 Dependencies _deps = pptr->deps();
1016 #warning Add FileDeps
1017 // pptr->addProvides( *it );
1018 pptr->setDeps(_deps);
1023 ///////////////////////////////////////////////////////////////////
1024 // Build final packages list
1025 ///////////////////////////////////////////////////////////////////
1026 _packages.buildList();
1027 DBG << "Found installed packages: " << _packages._list.size() << endl;
1028 return _packages._list;
1031 #warning Uncomment this function
1033 ///////////////////////////////////////////////////////////////////
1036 // METHOD NAME : RpmDb::traceFileRel
1037 // METHOD TYPE : void
1041 void RpmDb::traceFileRel( const PkgRelation & rel_r )
1043 if ( ! rel_r.isFileRel() )
1046 if ( ! _filerequires.insert( rel_r.name() ).second )
1047 return; // already got it in _filerequires
1049 if ( ! _packages._valid )
1050 return; // collect only. Evaluated in first call to getPackages()
1053 // packages already initialized. Must check and insert here
1055 librpmDb::db_const_iterator iter;
1056 if ( iter.dbError() ) {
1057 ERR << "No database access: " << iter.dbError() << endl;
1061 for ( iter.findByFile( rel_r.name() ); *iter; ++iter ) {
1062 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1064 WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
1067 pptr->addProvides( rel_r.name() );
1072 ///////////////////////////////////////////////////////////////////
1075 // METHOD NAME : RpmDb::hasFile
1076 // METHOD TYPE : bool
1080 bool RpmDb::hasFile( const std::string & file_r ) const
1082 librpmDb::db_const_iterator it;
1083 return it.findByFile( file_r );
1086 ///////////////////////////////////////////////////////////////////
1089 // METHOD NAME : RpmDb::hasProvides
1090 // METHOD TYPE : bool
1094 bool RpmDb::hasProvides( const std::string & tag_r ) const
1096 librpmDb::db_const_iterator it;
1097 return it.findByProvides( tag_r );
1100 ///////////////////////////////////////////////////////////////////
1103 // METHOD NAME : RpmDb::hasRequiredBy
1104 // METHOD TYPE : bool
1108 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1110 librpmDb::db_const_iterator it;
1111 return it.findByRequiredBy( tag_r );
1114 ///////////////////////////////////////////////////////////////////
1117 // METHOD NAME : RpmDb::hasConflicts
1118 // METHOD TYPE : bool
1122 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1124 librpmDb::db_const_iterator it;
1125 return it.findByConflicts( tag_r );
1128 ///////////////////////////////////////////////////////////////////
1131 // METHOD NAME : RpmDb::hasPackage
1132 // METHOD TYPE : bool
1136 bool RpmDb::hasPackage( const string & name_r ) const
1138 librpmDb::db_const_iterator it;
1139 return it.findPackage( name_r );
1142 ///////////////////////////////////////////////////////////////////
1145 // METHOD NAME : RpmDb::getData
1146 // METHOD TYPE : PMError
1150 void RpmDb::getData( const string & name_r,
1151 RpmHeader::constPtr & result_r ) const
1153 librpmDb::db_const_iterator it;
1154 it.findPackage( name_r );
1157 ZYPP_THROW(*(it.dbError()));
1160 ///////////////////////////////////////////////////////////////////
1163 // METHOD NAME : RpmDb::getData
1164 // METHOD TYPE : PMError
1168 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1169 RpmHeader::constPtr & result_r ) const
1171 librpmDb::db_const_iterator it;
1172 it.findPackage( name_r, ed_r );
1175 ZYPP_THROW(*(it.dbError()));
1178 /*--------------------------------------------------------------*/
1179 /* Checking the source rpm <rpmpath> with rpm --chcksig and */
1180 /* the version number. */
1181 /*--------------------------------------------------------------*/
1183 RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
1185 unsigned result = 0;
1187 if ( ! version.empty() ) {
1188 RpmHeader::constPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
1189 if ( ! h || Edition( version ) != h->tag_edition() ) {
1190 result |= CHK_INCORRECT_VERSION;
1196 #warning TBD MD5 check
1197 WAR << "md5sum check not yet implemented" << endl;
1198 return CHK_INCORRECT_FILEMD5;
1201 std::string path = packagePath.asString();
1202 // checking --checksig
1203 const char *const argv[] = {
1204 "rpm", "--checksig", "--", path.c_str(), 0
1211 for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
1213 output = output + " " + argv[k];
1216 DBG << "rpm command: " << output << endl;
1218 if ( process != NULL )
1223 // Launch the program
1224 process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1227 if ( process == NULL )
1229 result |= CHK_OTHER_FAILURE;
1230 DBG << "create process failed" << endl;
1234 output = process->receiveLine();
1236 while ( output.length() > 0)
1238 string::size_type ret;
1241 ret = output.find_first_of ( "\n" );
1242 if ( ret != string::npos )
1244 value.assign ( output, 0, ret );
1251 DBG << "stdout: " << value << endl;
1253 string::size_type pos;
1254 if((pos = value.find (path)) != string::npos)
1256 string rest = value.substr (pos + path.length() + 1);
1257 if (rest.find("NOT OK") == string::npos)
1259 // see what checks are ok
1260 if (rest.find("md5") == string::npos)
1262 result |= CHK_MD5SUM_MISSING;
1264 if (rest.find("gpg") == string::npos)
1266 result |= CHK_GPGSIG_MISSING;
1271 // see what checks are not ok
1272 if (rest.find("MD5") != string::npos)
1274 result |= CHK_INCORRECT_PKGMD5;
1278 result |= CHK_MD5SUM_MISSING;
1281 if (rest.find("GPG") != string::npos)
1283 result |= CHK_INCORRECT_GPGSIG;
1287 result |= CHK_GPGSIG_MISSING;
1292 output = process->receiveLine();
1295 if ( result == 0 && systemStatus() != 0 )
1298 result |= CHK_OTHER_FAILURE;
1304 // determine changed files of installed package
1306 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1312 if( ! initialized() ) return false;
1316 opts.push_back ("-V");
1317 opts.push_back ("--nodeps");
1318 opts.push_back ("--noscripts");
1319 opts.push_back ("--nomd5");
1320 opts.push_back ("--");
1321 opts.push_back (packageName.c_str());
1323 run_rpm (opts, ExternalProgram::Discard_Stderr);
1325 if ( process == NULL )
1336 M Mode (includes permissions and file type)
1340 while (systemReadLine(line))
1342 if (line.length() > 12 &&
1343 (line[0] == 'S' || line[0] == 's' ||
1344 (line[0] == '.' && line[7] == 'T')))
1346 // file has been changed
1349 filename.assign(line, 11, line.length() - 11);
1350 fileList.insert(filename);
1355 // exit code ignored, rpm returns 1 no matter if package is installed or
1363 /****************************************************************/
1364 /* private member-functions */
1365 /****************************************************************/
1367 /*--------------------------------------------------------------*/
1368 /* Run rpm with the specified arguments, handling stderr */
1369 /* as specified by disp */
1370 /*--------------------------------------------------------------*/
1372 RpmDb::run_rpm (const RpmArgVec& opts,
1373 ExternalProgram::Stderr_Disposition disp)
1381 if ( ! initialized() ) {
1382 ZYPP_THROW(RpmDbNotOpenException());
1387 // always set root and dbpath
1388 args.push_back("rpm");
1389 args.push_back("--root");
1390 args.push_back(_root.asString().c_str());
1391 args.push_back("--dbpath");
1392 args.push_back(_dbPath.asString().c_str());
1394 const char* argv[args.size() + opts.size() + 1];
1396 const char** p = argv;
1397 p = copy (args.begin (), args.end (), p);
1398 p = copy (opts.begin (), opts.end (), p);
1401 // Invalidate all outstanding database handles in case
1402 // the database gets modified.
1403 librpmDb::dbRelease( true );
1405 // Launch the program with default locale
1406 process = new ExternalProgram(argv, disp, false, -1, true);
1410 /*--------------------------------------------------------------*/
1411 /* Read a line from the rpm process */
1412 /*--------------------------------------------------------------*/
1414 RpmDb::systemReadLine(string &line)
1418 if ( process == NULL )
1421 line = process->receiveLine();
1423 if (line.length() == 0)
1426 if (line[line.length() - 1] == '\n')
1427 line.erase(line.length() - 1);
1432 /*--------------------------------------------------------------*/
1433 /* Return the exit status of the rpm process, closing the */
1434 /* connection if not already done */
1435 /*--------------------------------------------------------------*/
1437 RpmDb::systemStatus()
1439 if ( process == NULL )
1442 exit_code = process->close();
1447 // DBG << "exit code " << exit_code << endl;
1452 /*--------------------------------------------------------------*/
1453 /* Forcably kill the rpm process */
1454 /*--------------------------------------------------------------*/
1458 if (process) process->kill();
1462 // generate diff mails for config files
1463 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1465 string msg = line.substr(9);
1466 string::size_type pos1 = string::npos;
1467 string::size_type pos2 = string::npos;
1468 string file1s, file2s;
1472 pos1 = msg.find (typemsg);
1475 if( pos1 == string::npos )
1478 pos2 = pos1 + strlen (typemsg);
1480 if (pos2 >= msg.length() )
1483 file1 = msg.substr (0, pos1);
1484 file2 = msg.substr (pos2);
1486 file1s = file1.asString();
1487 file2s = file2.asString();
1489 if (!_root.empty() && _root != "/")
1491 file1 = _root + file1;
1492 file2 = _root + file2;
1496 #warning FIXME the diffing functionality
1498 int ret = Diff::differ (file1.asString(), file2.asString(), out, 25);
1501 Pathname file = _root + WARNINGMAILPATH;
1502 if (filesystem::assert_dir(file) != 0)
1504 ERR << "Could not create " << file.asString() << endl;
1507 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1508 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1511 ERR << "Could not open " << file << endl;
1515 // Translator: %s = name of an rpm package. A list of diffs follows
1517 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1520 ERR << "diff failed" << endl;
1521 notify << str::form(difffailmsg,
1522 file1s.c_str(), file2s.c_str()) << endl;
1526 notify << str::form(diffgenmsg,
1527 file1s.c_str(), file2s.c_str()) << endl;
1529 // remove root for the viewer's pleasure (#38240)
1530 if (!_root.empty() && _root != "/")
1532 if(out.substr(0,4) == "--- ")
1534 out.replace(4, file1.asString().length(), file1s);
1536 string::size_type pos = out.find("\n+++ ");
1537 if(pos != string::npos)
1539 out.replace(pos+5, file2.asString().length(), file2s);
1542 notify << out << endl;
1545 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1550 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1557 ///////////////////////////////////////////////////////////////////
1560 // METHOD NAME : RpmDb::installPackage
1561 // METHOD TYPE : PMError
1563 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1565 RpmInstallReport report;
1567 doInstallPackage(filename, flags, report);
1569 catch (RpmException & excpt_r)
1571 report.end(excpt_r);
1572 ZYPP_RETHROW(excpt_r);
1577 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, RpmInstallReport & report )
1579 FAILIFNOTINITIALIZED;
1580 Logfile progresslog;
1582 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1585 if ( _packagebackups ) {
1586 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1587 if ( ! backupPackage( filename ) ) {
1588 ERR << "backup of " << filename.asString() << " failed" << endl;
1590 // FIXME status handling
1591 report.progress( 0 ); // allow 1% for backup creation.
1593 report.progress( 100 );
1598 if (flags & RPMINST_NOUPGRADE)
1599 opts.push_back("-i");
1601 opts.push_back("-U");
1602 opts.push_back("--percent");
1604 if (flags & RPMINST_NODIGEST)
1605 opts.push_back("--nodigest");
1606 if (flags & RPMINST_NOSIGNATURE)
1607 opts.push_back("--nosignature");
1608 if (flags & RPMINST_NODOCS)
1609 opts.push_back ("--excludedocs");
1610 if (flags & RPMINST_NOSCRIPTS)
1611 opts.push_back ("--noscripts");
1612 if (flags & RPMINST_FORCE)
1613 opts.push_back ("--force");
1614 if (flags & RPMINST_NODEPS)
1615 opts.push_back ("--nodeps");
1616 if(flags & RPMINST_IGNORESIZE)
1617 opts.push_back ("--ignoresize");
1618 if(flags & RPMINST_JUSTDB)
1619 opts.push_back ("--justdb");
1621 opts.push_back("--");
1622 opts.push_back (filename.asString().c_str());
1624 modifyDatabase(); // BEFORE run_rpm
1625 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1629 vector<string> configwarnings;
1630 vector<string> errorlines;
1632 while (systemReadLine(line))
1634 if (line.substr(0,2)=="%%")
1637 sscanf (line.c_str () + 2, "%d", &percent);
1638 report.progress( percent );
1641 rpmmsg += line+'\n';
1643 if( line.substr(0,8) == "warning:" )
1645 configwarnings.push_back(line);
1648 int rpm_status = systemStatus();
1651 for(vector<string>::iterator it = configwarnings.begin();
1652 it != configwarnings.end(); ++it)
1654 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1656 _("rpm saved %s as %s but it was impossible to determine the difference"),
1658 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1659 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1661 _("rpm created %s as %s but it was impossible to determine the difference"),
1663 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1666 if ( rpm_status != 0 ) {
1667 // %s = filename of rpm package
1668 progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1669 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1670 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1672 // %s = filename of rpm package
1673 progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1674 if( ! rpmmsg.empty() ) {
1675 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1680 ///////////////////////////////////////////////////////////////////
1683 // METHOD NAME : RpmDb::removePackage
1684 // METHOD TYPE : PMError
1686 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1688 return removePackage( package->name(), flags );
1691 ///////////////////////////////////////////////////////////////////
1694 // METHOD NAME : RpmDb::removePackage
1695 // METHOD TYPE : PMError
1697 void RpmDb::removePackage( const string & name_r, unsigned flags )
1699 RpmRemoveReport report;
1701 doRemovePackage(name_r, flags, report);
1703 catch (RpmException & excpt_r)
1705 report.end(excpt_r);
1706 ZYPP_RETHROW(excpt_r);
1712 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, RpmRemoveReport & report )
1714 FAILIFNOTINITIALIZED;
1715 Logfile progresslog;
1717 MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
1720 if ( _packagebackups ) {
1721 // FIXME solve this status report somehow
1722 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1723 if ( ! backupPackage( name_r ) ) {
1724 ERR << "backup of " << name_r << " failed" << endl;
1726 report.progress( 0 );
1728 report.progress( 100 );
1733 opts.push_back("-e");
1734 opts.push_back("--allmatches");
1736 if (flags & RPMINST_NOSCRIPTS)
1737 opts.push_back("--noscripts");
1738 if (flags & RPMINST_NODEPS)
1739 opts.push_back("--nodeps");
1740 if (flags & RPMINST_JUSTDB)
1741 opts.push_back("--justdb");
1742 if (flags & RPMINST_FORCE) {
1743 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1746 opts.push_back("--");
1747 opts.push_back(name_r.c_str());
1749 modifyDatabase(); // BEFORE run_rpm
1750 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1755 // got no progress from command, so we fake it:
1756 // 5 - command started
1757 // 50 - command completed
1759 report.progress( 5 );
1760 while (systemReadLine(line))
1762 rpmmsg += line+'\n';
1764 report.progress( 50 );
1765 int rpm_status = systemStatus();
1767 if ( rpm_status != 0 ) {
1768 // %s = name of rpm package
1769 progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
1770 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1771 ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1773 progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
1774 if( ! rpmmsg.empty() ) {
1775 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1781 RpmDb::checkPackageResult2string(unsigned code)
1784 // begin of line characters
1786 // end of line characters
1789 return string(_("Ok"))+eol;
1791 //translator: these are different kinds of how an rpm package can be broken
1792 msg = _("Package is not OK for the following reasons:");
1795 if(code&CHK_INCORRECT_VERSION)
1798 msg+=_("Package contains different version than expected");
1801 if(code&CHK_INCORRECT_FILEMD5)
1804 msg+=_("Package file has incorrect MD5 sum");
1807 if(code&CHK_GPGSIG_MISSING)
1810 msg+=_("Package is not signed");
1813 if(code&CHK_MD5SUM_MISSING)
1816 msg+=_("Package has no MD5 sum");
1819 if(code&CHK_INCORRECT_GPGSIG)
1822 msg+=_("Package has incorrect signature");
1825 if(code&CHK_INCORRECT_PKGMD5)
1828 msg+=_("Package archive has incorrect MD5 sum");
1831 if(code&CHK_OTHER_FAILURE)
1834 msg+=_("rpm failed for unkown reason, see log file");
1841 ///////////////////////////////////////////////////////////////////
1844 // METHOD NAME : RpmDb::backupPackage
1845 // METHOD TYPE : bool
1847 bool RpmDb::backupPackage( const Pathname & filename )
1849 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1853 return backupPackage( h->tag_name() );
1856 ///////////////////////////////////////////////////////////////////
1859 // METHOD NAME : RpmDb::backupPackage
1860 // METHOD TYPE : bool
1862 bool RpmDb::backupPackage(const string& packageName)
1864 Logfile progresslog;
1866 Pathname backupFilename;
1867 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1869 if (_backuppath.empty())
1871 INT << "_backuppath empty" << endl;
1877 if (!queryChangedFiles(fileList, packageName))
1879 ERR << "Error while getting changed files for package " <<
1880 packageName << endl;
1884 if (fileList.size() <= 0)
1886 DBG << "package " << packageName << " not changed -> no backup" << endl;
1890 if (filesystem::assert_dir(_root + _backuppath) != 0)
1896 // build up archive name
1897 time_t currentTime = time(0);
1898 struct tm *currentLocalTime = localtime(¤tTime);
1900 int date = (currentLocalTime->tm_year + 1900) * 10000
1901 + (currentLocalTime->tm_mon + 1) * 100
1902 + currentLocalTime->tm_mday;
1907 backupFilename = _root + _backuppath
1908 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1911 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1913 PathInfo pi(filestobackupfile);
1914 if(pi.isExist() && !pi.isFile())
1916 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1920 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1924 ERR << "could not open " << filestobackupfile.asString() << endl;
1928 for (FileList::const_iterator cit = fileList.begin();
1929 cit != fileList.end(); ++cit)
1932 if ( name[0] == '/' )
1934 // remove slash, file must be relative to -C parameter of tar
1935 name = name.substr( 1 );
1937 DBG << "saving file "<< name << endl;
1942 const char* const argv[] =
1947 _root.asString().c_str(),
1948 "--ignore-failed-read",
1950 backupFilename.asString().c_str(),
1952 filestobackupfile.asString().c_str(),
1956 // execute tar in inst-sys (we dont know if there is a tar below _root !)
1957 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1961 // TODO: its probably possible to start tar with -v and watch it adding
1962 // files to report progress
1963 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
1968 int ret = tar.close();
1972 ERR << "tar failed: " << tarmsg << endl;
1977 MIL << "tar backup ok" << endl;
1978 progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
1981 filesystem::unlink(filestobackupfile);
1987 void RpmDb::setBackupPath(const Pathname& path)
1993 } // namespace target