1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
14 Purpose: Interface to installed RPM system
15 Author: Stefan Schubert <schubi@suse.de>
16 Maintainer: Ludwig Nussel <lnussel@suse.de>
18 Copied and adapted from agent-targetpkg
35 #include "zypp/base/Logger.h"
37 #include "zypp/Date.h"
38 #include "zypp/Pathname.h"
39 #include "zypp/PathInfo.h"
41 #include "zypp/target/rpm/RpmDb.h"
42 #include "zypp/target/rpm/RpmCallbacks.h"
44 #include "zypp/target/rpm/librpmDb.h"
45 #include "zypp/target/rpm/RpmPackageImpl.h"
46 #include "zypp/CapSet.h"
58 /******************************************************************
61 ** FUNCTION NAME : stringPath
62 ** FUNCTION TYPE : inline string
64 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
66 return librpmDb::stringPath( root_r, sub_r );
69 /******************************************************************
72 ** FUNCTION NAME : operator<<
73 ** FUNCTION TYPE : ostream &
75 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
77 if ( obj == RpmDb::DbSI_NO_INIT ) {
80 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
82 ENUM_OUT( DbSI_HAVE_V4, 'X' );
83 ENUM_OUT( DbSI_MADE_V4, 'c' );
84 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
86 ENUM_OUT( DbSI_HAVE_V3, 'X' );
87 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
88 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
95 ///////////////////////////////////////////////////////////////////
96 // CLASS NAME : RpmDbPtr
97 // CLASS NAME : RpmDbconstPtr
98 ///////////////////////////////////////////////////////////////////
100 #define WARNINGMAILPATH "/var/log/YaST2/"
101 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
103 ///////////////////////////////////////////////////////////////////
105 // CLASS NAME : RpmDb::Logfile
107 * Simple wrapper for progress log. Refcnt, filename and corresponding
108 * ofstream are static members. Logfile constructor raises, destructor
109 * lowers refcounter. On refcounter changing from 0->1, file is opened.
110 * Changing from 1->0 the file is closed. Thus Logfile objects should be
111 * local to those functions, writing the log, and must not be stored
117 * Logfile progresslog;
119 * progresslog() << "some message" << endl;
123 class RpmDb::Logfile {
124 Logfile( const Logfile & );
125 Logfile & operator=( const Logfile & );
127 static ofstream _log;
128 static unsigned _refcnt;
129 static Pathname _fname;
130 static void openLog() {
131 if ( !_fname.empty() ) {
133 _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
135 ERR << "Could not open logfile '" << _fname << "'" << endl;
138 static void closeLog() {
142 static void refUp() {
147 static void refDown() {
153 Logfile() { refUp(); }
154 ~Logfile() { refDown(); }
155 ostream & operator()( bool timestamp = false ) {
157 _log << Date(Date::now()).form( "%Y-%m-%d %H:%M:%S ");
161 static void setFname( const Pathname & fname_r ) {
162 MIL << "installation log file " << fname_r << endl;
171 ///////////////////////////////////////////////////////////////////
173 Pathname RpmDb::Logfile::_fname;
174 ofstream RpmDb::Logfile::_log;
175 unsigned RpmDb::Logfile::_refcnt = 0;
177 ///////////////////////////////////////////////////////////////////
179 ///////////////////////////////////////////////////////////////////
182 // METHOD NAME : RpmDb::setInstallationLogfile
183 // METHOD TYPE : bool
185 bool RpmDb::setInstallationLogfile( const Pathname & filename )
187 Logfile::setFname( filename );
191 ///////////////////////////////////////////////////////////////////
193 // CLASS NAME : RpmDb::Packages
195 * Helper class for RpmDb::getPackages() to build the
196 * list<Package::Ptr> returned. We have to assert, that there
197 * is a unique entry for every string.
199 * In the first step we build the _index map which helps to catch
200 * multiple occurances of a string in the rpmdb. That's not desired,
201 * but possible. Usg. the last package instance installed is strored
204 * At the end buildList() is called to build the list<Package::Ptr>
205 * from the _index map. _valid is set true to assign that the list
206 * is in sync with the rpmdb content. Operations changing the rpmdb
207 * content (install/remove package) should set _valid to false. The
208 * next call to RpmDb::getPackages() will then reread the the rpmdb.
210 * Note that outside RpmDb::getPackages() _list and _index are always
211 * in sync. So you may use lookup(PkgName) to retrieve a specific
214 class RpmDb::Packages {
216 list<Package::Ptr> _list;
217 map<std::string,Package::Ptr> _index;
219 Packages() : _valid( false ) {}
225 Package::Ptr lookup( const string & name_r ) const {
226 map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
227 if ( got != _index.end() )
229 return Package::Ptr();
233 for ( map<string,Package::Ptr>::iterator iter = _index.begin();
234 iter != _index.end(); ++iter ) {
236 _list.push_back( iter->second );
242 ///////////////////////////////////////////////////////////////////
244 ///////////////////////////////////////////////////////////////////
246 // CLASS NAME : RpmDb
248 ///////////////////////////////////////////////////////////////////
250 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(Exception("Error::E_RpmDB_not_open")); }
252 ///////////////////////////////////////////////////////////////////
254 ///////////////////////////////////////////////////////////////////
257 // METHOD NAME : RpmDb::RpmDb
258 // METHOD TYPE : Constructor
261 : _dbStateInfo( DbSI_NO_INIT )
262 , _packages( * new Packages ) // delete in destructor
263 #warning Check for obsolete memebers
264 , _backuppath ("/var/adm/backup")
265 , _packagebackups(false)
266 , _warndirexists(false)
271 // Some rpm versions are patched not to abort installation if
272 // symlink creation failed.
273 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
276 ///////////////////////////////////////////////////////////////////
279 // METHOD NAME : RpmDb::~RpmDb
280 // METHOD TYPE : Destructor
284 MIL << "~RpmDb()" << endl;
289 MIL << "~RpmDb() end" << endl;
292 ///////////////////////////////////////////////////////////////////
295 // METHOD NAME : RpmDb::dumpOn
296 // METHOD TYPE : std::ostream &
298 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
302 if ( _dbStateInfo == DbSI_NO_INIT ) {
305 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
307 ENUM_OUT( DbSI_HAVE_V4, 'X' );
308 ENUM_OUT( DbSI_MADE_V4, 'c' );
309 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
311 ENUM_OUT( DbSI_HAVE_V3, 'X' );
312 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
313 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
314 str << "): " << stringPath( _root, _dbPath );
320 ///////////////////////////////////////////////////////////////////
323 // METHOD NAME : RpmDb::initDatabase
324 // METHOD TYPE : PMError
326 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
328 ///////////////////////////////////////////////////////////////////
330 ///////////////////////////////////////////////////////////////////
331 if ( root_r.empty() )
334 if ( dbPath_r.empty() )
335 dbPath_r = "/var/lib/rpm";
337 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
338 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
339 ZYPP_THROW(Exception("Error::E_invalid_argument"));
342 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
344 ///////////////////////////////////////////////////////////////////
345 // Check whether already initialized
346 ///////////////////////////////////////////////////////////////////
347 if ( initialized() ) {
348 if ( root_r == _root && dbPath_r == _dbPath ) {
351 ERR << "Can't switch to " << stringPath( root_r, dbPath_r )
352 << " while accessing " << stringPath( _root, _dbPath ) << endl;
353 ZYPP_THROW(Exception("Error::E_RpmDB_already_open"));
357 ///////////////////////////////////////////////////////////////////
359 ///////////////////////////////////////////////////////////////////
360 librpmDb::unblockAccess();
361 DbStateInfoBits info = DbSI_NO_INIT;
363 internal_initDatabase( root_r, dbPath_r, info );
367 librpmDb::blockAccess();
368 ERR << "Cleanup on error: state " << info << endl;
370 if ( dbsi_has( info, DbSI_MADE_V4 ) ) {
371 // remove the newly created rpm4 database and
372 // any backup created on conversion.
373 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
375 #warning FIXME rethrow
378 if ( dbsi_has( info, DbSI_HAVE_V3 ) ) {
379 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) ) {
380 // Move obsolete rpm3 database beside.
381 MIL << "Cleanup: state " << info << endl;
382 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
383 dbsi_clr( info, DbSI_HAVE_V3 );
385 // Performing an update: Keep the original rpm3 database
386 // and wait if the rpm4 database gets modified by installing
387 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
388 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
391 #warning CHECK: notify root about conversion backup.
397 #warning FIXME uncomment code below
399 if ( ! ( err || Y2PM::runningFromSystem() ) ) {
400 if ( dbsi_has( info, DbSI_HAVE_V4 )
401 && ! dbsi_has( info, DbSI_MADE_V4 ) ) {
402 err = rebuildDatabase();
407 // Close the database in case any write acces (create/convert)
408 // happened during init. This should drop any lock acquired
409 // by librpm. On demand it will be reopened readonly and should
410 // not hold any lock.
411 librpmDb::dbRelease( true );
412 MIL << "InitDatabase: " << *this << endl;
415 ///////////////////////////////////////////////////////////////////
418 // METHOD NAME : RpmDb::internal_initDatabase
419 // METHOD TYPE : PMError
421 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
422 DbStateInfoBits & info_r )
424 info_r = DbSI_NO_INIT;
426 ///////////////////////////////////////////////////////////////////
427 // Get info about the desired database dir
428 ///////////////////////////////////////////////////////////////////
429 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
431 if ( dbInfo.illegalArgs() ) {
432 ZYPP_THROW(Exception("Error::E_invalid_argument")); // should not happen (checked in initDatabase)
434 if ( ! dbInfo.usableArgs() ) {
435 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
436 ZYPP_THROW(Exception("Error::E_invalid_argument"));
439 if ( dbInfo.hasDbV4() ) {
440 dbsi_set( info_r, DbSI_HAVE_V4 );
441 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
443 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
446 if ( dbInfo.hasDbV3() ) {
447 dbsi_set( info_r, DbSI_HAVE_V3 );
449 if ( dbInfo.hasDbV3ToV4() ) {
450 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
453 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
454 librpmDb::dumpState( DBG ) << endl;
456 ///////////////////////////////////////////////////////////////////
457 // Access database, create if needed
458 ///////////////////////////////////////////////////////////////////
460 // creates dbdir and empty rpm4 database if not present
461 librpmDb::dbAccess( root_r, dbPath_r );
463 if ( ! dbInfo.hasDbV4() ) {
465 if ( dbInfo.hasDbV4() ) {
466 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
470 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
471 librpmDb::dumpState( DBG ) << endl;
473 ///////////////////////////////////////////////////////////////////
474 // Check whether to convert something. Create backup but do
475 // not remove anything here
476 ///////////////////////////////////////////////////////////////////
477 librpmDb::constPtr dbptr;
478 librpmDb::dbAccess( dbptr );
479 bool dbEmpty = dbptr->empty();
481 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
484 if ( dbInfo.hasDbV3() ) {
485 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
488 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
490 convertV3toV4( dbInfo.dbV3().path(), dbptr );
492 // create a backup copy
493 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
495 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
498 if ( dbInfo.hasDbV3ToV4() ) {
499 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
500 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
506 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
507 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
508 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
509 //report->start( dbInfo.dbV3().path() );
510 //report->stop( some error );
512 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
513 dbsi_set( info_r, DbSI_MODIFIED_V4 );
517 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
518 librpmDb::dumpState( DBG ) << endl;
521 if ( dbInfo.hasDbV3ToV4() ) {
522 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
526 ///////////////////////////////////////////////////////////////////
529 // METHOD NAME : RpmDb::removeV4
530 // METHOD TYPE : void
532 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
534 const char * v3backup = "packages.rpm3";
535 const char * master = "Packages";
536 const char * index[] = {
557 PathInfo pi( dbdir_r );
558 if ( ! pi.isDir() ) {
559 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
563 for ( const char ** f = index; *f; ++f ) {
566 filesystem::unlink( pi.path() );
570 pi( dbdir_r + master );
572 MIL << "Removing rpm4 database " << pi << endl;
573 filesystem::unlink( pi.path() );
577 pi( dbdir_r + v3backup );
579 MIL << "Removing converted rpm3 database backup " << pi << endl;
580 filesystem::unlink( pi.path() );
585 ///////////////////////////////////////////////////////////////////
588 // METHOD NAME : RpmDb::removeV3
589 // METHOD TYPE : void
591 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
593 const char * master = "packages.rpm";
594 const char * index[] = {
595 "conflictsindex.rpm",
606 PathInfo pi( dbdir_r );
607 if ( ! pi.isDir() ) {
608 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
612 for ( const char ** f = index; *f; ++f ) {
615 filesystem::unlink( pi.path() );
619 #warning CHECK: compare vs existing v3 backup. notify root
620 pi( dbdir_r + master );
622 Pathname m( pi.path() );
624 // backup was already created
625 filesystem::unlink( m );
626 Pathname b( m.extend( "3" ) );
627 pi( b ); // stat backup
629 Pathname b( m.extend( ".deleted" ) );
632 // rempve existing backup
633 filesystem::unlink( b );
635 filesystem::rename( m, b );
636 pi( b ); // stat backup
638 MIL << "(Re)moved rpm3 database to " << pi << endl;
642 ///////////////////////////////////////////////////////////////////
645 // METHOD NAME : RpmDb::modifyDatabase
646 // METHOD TYPE : void
648 void RpmDb::modifyDatabase()
650 if ( ! initialized() )
653 // tag database as modified
654 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
656 // Move outdated rpm3 database beside.
657 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
658 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
659 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
660 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
663 // invalidate Packages list
664 _packages._valid = false;
667 ///////////////////////////////////////////////////////////////////
670 // METHOD NAME : RpmDb::closeDatabase
671 // METHOD TYPE : PMError
673 void RpmDb::closeDatabase()
675 if ( ! initialized() ) {
679 MIL << "Calling closeDatabase: " << *this << endl;
681 ///////////////////////////////////////////////////////////////////
682 // Block further database access
683 ///////////////////////////////////////////////////////////////////
685 librpmDb::blockAccess();
687 ///////////////////////////////////////////////////////////////////
688 // Check fate if old version database still present
689 ///////////////////////////////////////////////////////////////////
690 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
691 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
692 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
693 // Move outdated rpm3 database beside.
694 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
696 // Remove unmodified rpm4 database
697 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
701 ///////////////////////////////////////////////////////////////////
703 ///////////////////////////////////////////////////////////////////
704 _root = _dbPath = Pathname();
705 _dbStateInfo = DbSI_NO_INIT;
707 MIL << "closeDatabase: " << *this << endl;
710 ///////////////////////////////////////////////////////////////////
713 // METHOD NAME : RpmDb::rebuildDatabase
714 // METHOD TYPE : PMError
716 void RpmDb::rebuildDatabase()
718 FAILIFNOTINITIALIZED;
720 MIL << "RpmDb::rebuildDatabase" << *this << endl;
721 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
723 PathInfo dbMaster( root() + dbPath() + "Packages" );
724 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
727 RebuildDbReport report;
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 ERR << "rpm failed, message was:" << endl << errmsg; // has trailing NL
767 #warning FIXME error report
768 // err.setDetails(errmsg);
770 ZYPP_THROW(Exception("Error::E_RpmDB_subprocess_failed"));
772 report.progress( 100 ); // 100%
777 ///////////////////////////////////////////////////////////////////
780 // METHOD NAME : RpmDb::importPubkey
781 // METHOD TYPE : PMError
783 void RpmDb::importPubkey( const Pathname & pubkey_r )
785 FAILIFNOTINITIALIZED;
788 opts.push_back ( "--import" );
789 opts.push_back ( "--" );
790 opts.push_back ( pubkey_r.asString().c_str() );
792 // don't call modifyDatabase because it would remove the old
793 // rpm3 database, if the current database is a temporary one.
794 // But do invalidate packages list.
795 _packages._valid = false;
796 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
799 while ( systemReadLine( line ) ) {
800 if ( line.substr( 0, 6 ) == "error:" ) {
807 int rpm_status = systemStatus();
809 if ( rpm_status != 0 ) {
810 ERR << "Failed to import public key from file " << pubkey_r << ": rpm returned " << rpm_status << endl;
811 ZYPP_THROW(Exception("Error::E_RpmDB_subprocess_failed"));
813 MIL << "Imported public key from file " << pubkey_r << endl;
817 ///////////////////////////////////////////////////////////////////
820 // METHOD NAME : RpmDb::importPubkey
821 // METHOD TYPE : PMError
823 void RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
825 FAILIFNOTINITIALIZED;
828 char tmpname[] = "/tmp/y2.pubkey.XXXXXX";
829 int tmpfd = mkstemp( tmpname );
831 ERR << "Unable to create a unique temporary file for pubkey" << endl;
832 ZYPP_THROW(Exception("Error::E_RpmDB_subprocess_failed"));
835 // export keyname from keyring
837 args.push_back( "gpg" );
838 args.push_back( "--armor" );
839 args.push_back( "--no-default-keyring" );
840 args.push_back( "--keyring" );
841 args.push_back( keyring_r.asString().c_str() );
842 args.push_back( "--export" );
843 args.push_back( keyname_r.c_str() );
845 const char * argv[args.size() + 1];
846 const char ** p = argv;
847 p = copy( args.begin(), args.end(), p );
851 ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
856 for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
857 ssize_t written = write( tmpfd, line.c_str(), line.length() );
858 if ( written == -1 || unsigned(written) != line.length() ) {
859 ERR << "Error writing pubkey to " << tmpname << endl;
860 err = "Error::E_RpmDB_subprocess_failed";
863 res += written; // empty file indicates key not found
868 WAR << "gpg: no key '" << keyname_r << "' found in '" << keyring_r << "'" << endl;
869 err = "Error::E_RpmDB_subprocess_failed";
872 // check gpg returncode
875 ERR << "gpg: export '" << keyname_r << "' from '" << keyring_r << "' returned " << res << endl;
876 err = "Error::E_RpmDB_subprocess_failed";
880 MIL << "Exported '" << keyname_r << "' from '" << keyring_r << "' to " << tmpname << endl;
881 #warning FIXME handle exception from line below
882 importPubkey( tmpname );
886 filesystem::unlink( tmpname );
888 ZYPP_THROW(Exception(err));
891 ///////////////////////////////////////////////////////////////////
894 // METHOD NAME : RpmDb::pubkeys
895 // METHOD TYPE : set<Edition>
897 set<Edition> RpmDb::pubkeys() const
901 librpmDb::db_const_iterator it;
902 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it ) {
903 ret.insert( it->tag_edition() );
909 ///////////////////////////////////////////////////////////////////
912 // METHOD NAME : RpmDb::packagesValid
913 // METHOD TYPE : bool
915 bool RpmDb::packagesValid() const
917 return( _packages._valid || ! initialized() );
920 ///////////////////////////////////////////////////////////////////
923 // METHOD NAME : RpmDb::getPackages
924 // METHOD TYPE : const std::list<Package::Ptr> &
928 const std::list<Package::Ptr> & RpmDb::getPackages()
930 if ( packagesValid() ) {
931 return _packages._list;
935 // FIXME Timecount _t( "RpmDb::getPackages" );
939 #warning how to detect corrupt db while reading.
943 ///////////////////////////////////////////////////////////////////
944 // Collect package data. A map is used to check whethere there are
945 // multiple entries for the same string. If so we consider the last
946 // one installed to be the one we're interesed in.
947 ///////////////////////////////////////////////////////////////////
948 librpmDb::db_const_iterator iter; // findAll
952 for ( ; *iter; ++iter ) {
955 #warning report error
957 if ( iter.dbError() ) {
958 ERR << "No database access: " << iter.dbError() << endl;
959 report.end( iter.dbError() );
960 return _packages._list;
963 #warning progress update
964 // report->progress( pd.init( expect ) );
967 #warning progress update
968 for ( iter.findAll(); *iter; ++iter/*, report.progress( pd.incr() )*/ ) {
970 string name = iter->tag_name();
971 if ( name == string( "gpg-pubkey" ) ) {
972 // pseudo package filtered, as we can't handle multiple instances
973 // of 'gpg-pubkey-VERS-REL'.
976 Date installtime = iter->tag_installtime();
977 Package::Ptr & nptr = _packages._index[name]; // be shure to get a reference!
980 WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
981 if ( nptr->installtime() > installtime )
983 // else overwrite previous entry
986 // create dataprovider and package
987 shared_ptr<RPMPackageImpl> impl(new RPMPackageImpl(*iter));
988 nptr = detail::makeResolvableFromImpl(
995 _deps.setProvides(iter->tag_provides ( & _filerequires ) );
996 _deps.setRequires ( iter->tag_requires ( &_filerequires ) );
997 _deps.setPrerequires ( iter->tag_prerequires ( &_filerequires ) );
998 _deps.setConflicts( iter->tag_conflicts( &_filerequires ) );
999 _deps.setObsoletes( iter->tag_obsoletes( &_filerequires ) );
1000 nptr->setDeps(_deps);
1003 ///////////////////////////////////////////////////////////////////
1004 // Evaluate filerequires collected so far
1005 ///////////////////////////////////////////////////////////////////
1006 for( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
1008 for ( iter.findByFile( *it ); *iter; ++iter ) {
1009 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1011 WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
1014 Dependencies _deps = pptr->deps();
1015 #warning Add FileDeps
1016 // pptr->addProvides( *it );
1017 pptr->setDeps(_deps);
1022 ///////////////////////////////////////////////////////////////////
1023 // Build final packages list
1024 ///////////////////////////////////////////////////////////////////
1025 _packages.buildList();
1026 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 );
1156 #warning FIXME error handling
1157 // return 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 );
1174 #warning FIXME error handling
1175 // return 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 ERR << "Attempt to run rpm: " << "Error::E_RpmDB_not_open" << endl;
1388 // always set root and dbpath
1389 args.push_back("rpm");
1390 args.push_back("--root");
1391 args.push_back(_root.asString().c_str());
1392 args.push_back("--dbpath");
1393 args.push_back(_dbPath.asString().c_str());
1395 const char* argv[args.size() + opts.size() + 1];
1397 const char** p = argv;
1398 p = copy (args.begin (), args.end (), p);
1399 p = copy (opts.begin (), opts.end (), p);
1402 // Invalidate all outstanding database handles in case
1403 // the database gets modified.
1404 librpmDb::dbRelease( true );
1406 // Launch the program with default locale
1407 process = new ExternalProgram(argv, disp, false, -1, true);
1411 /*--------------------------------------------------------------*/
1412 /* Read a line from the rpm process */
1413 /*--------------------------------------------------------------*/
1415 RpmDb::systemReadLine(string &line)
1419 if ( process == NULL )
1422 line = process->receiveLine();
1424 if (line.length() == 0)
1427 if (line[line.length() - 1] == '\n')
1428 line.erase(line.length() - 1);
1433 /*--------------------------------------------------------------*/
1434 /* Return the exit status of the rpm process, closing the */
1435 /* connection if not already done */
1436 /*--------------------------------------------------------------*/
1438 RpmDb::systemStatus()
1440 if ( process == NULL )
1443 exit_code = process->close();
1448 // DBG << "exit code " << exit_code << endl;
1453 /*--------------------------------------------------------------*/
1454 /* Forcably kill the rpm process */
1455 /*--------------------------------------------------------------*/
1459 if (process) process->kill();
1463 // generate diff mails for config files
1464 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1466 string msg = line.substr(9);
1467 string::size_type pos1 = string::npos;
1468 string::size_type pos2 = string::npos;
1469 string file1s, file2s;
1473 pos1 = msg.find (typemsg);
1476 if( pos1 == string::npos )
1479 pos2 = pos1 + strlen (typemsg);
1481 if (pos2 >= msg.length() )
1484 file1 = msg.substr (0, pos1);
1485 file2 = msg.substr (pos2);
1487 file1s = file1.asString();
1488 file2s = file2.asString();
1490 if (!_root.empty() && _root != "/")
1492 file1 = _root + file1;
1493 file2 = _root + file2;
1497 #warning FIXME the diffing functionality
1499 int ret = Diff::differ (file1.asString(), file2.asString(), out, 25);
1502 Pathname file = _root + WARNINGMAILPATH;
1503 if (filesystem::assert_dir(file) != 0)
1505 ERR << "Could not create " << file.asString() << endl;
1508 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1509 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1512 ERR << "Could not open " << file << endl;
1516 // Translator: %s = name of an rpm package. A list of diffs follows
1518 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1521 ERR << "diff failed" << endl;
1522 notify << str::form(difffailmsg,
1523 file1s.c_str(), file2s.c_str()) << endl;
1527 notify << str::form(diffgenmsg,
1528 file1s.c_str(), file2s.c_str()) << endl;
1530 // remove root for the viewer's pleasure (#38240)
1531 if (!_root.empty() && _root != "/")
1533 if(out.substr(0,4) == "--- ")
1535 out.replace(4, file1.asString().length(), file1s);
1537 string::size_type pos = out.find("\n+++ ");
1538 if(pos != string::npos)
1540 out.replace(pos+5, file2.asString().length(), file2s);
1543 notify << out << endl;
1546 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1551 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1558 ///////////////////////////////////////////////////////////////////
1561 // METHOD NAME : RpmDb::installPackage
1562 // METHOD TYPE : PMError
1564 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1566 FAILIFNOTINITIALIZED;
1567 Logfile progresslog;
1569 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1572 RpmInstallReport report;
1573 report.start( filename );
1576 if ( _packagebackups ) {
1577 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1578 if ( ! backupPackage( filename ) ) {
1579 ERR << "backup of " << filename.asString() << " failed" << endl;
1581 // FIXME status handling
1582 report.progress( 0 ); // allow 1% for backup creation.
1584 report.progress( 100 );
1589 if (flags & RPMINST_NOUPGRADE)
1590 opts.push_back("-i");
1592 opts.push_back("-U");
1593 opts.push_back("--percent");
1595 if (flags & RPMINST_NODIGEST)
1596 opts.push_back("--nodigest");
1597 if (flags & RPMINST_NOSIGNATURE)
1598 opts.push_back("--nosignature");
1599 if (flags & RPMINST_NODOCS)
1600 opts.push_back ("--excludedocs");
1601 if (flags & RPMINST_NOSCRIPTS)
1602 opts.push_back ("--noscripts");
1603 if (flags & RPMINST_FORCE)
1604 opts.push_back ("--force");
1605 if (flags & RPMINST_NODEPS)
1606 opts.push_back ("--nodeps");
1607 if(flags & RPMINST_IGNORESIZE)
1608 opts.push_back ("--ignoresize");
1609 if(flags & RPMINST_JUSTDB)
1610 opts.push_back ("--justdb");
1612 opts.push_back("--");
1613 opts.push_back (filename.asString().c_str());
1615 modifyDatabase(); // BEFORE run_rpm
1616 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1620 vector<string> configwarnings;
1621 vector<string> errorlines;
1623 while (systemReadLine(line))
1625 if (line.substr(0,2)=="%%")
1628 sscanf (line.c_str () + 2, "%d", &percent);
1629 report.progress( percent );
1632 rpmmsg += line+'\n';
1634 if( line.substr(0,8) == "warning:" )
1636 configwarnings.push_back(line);
1639 int rpm_status = systemStatus();
1642 for(vector<string>::iterator it = configwarnings.begin();
1643 it != configwarnings.end(); ++it)
1645 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1647 _("rpm saved %s as %s but it was impossible to determine the difference"),
1649 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1650 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1652 _("rpm created %s as %s but it was impossible to determine the difference"),
1654 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1659 if ( rpm_status != 0 ) {
1660 // %s = filename of rpm package
1661 progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1662 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1663 ERR << "rpm failed, message was: " << rpmmsg << endl;
1664 err = "Error::E_RpmDB_subprocess_failed";
1665 // TODO err.setDetails( rpmmsg );
1666 #warning TODO throw exception
1668 // %s = filename of rpm package
1669 progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1670 if( ! rpmmsg.empty() ) {
1671 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1678 ///////////////////////////////////////////////////////////////////
1681 // METHOD NAME : RpmDb::removePackage
1682 // METHOD TYPE : PMError
1684 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1686 return removePackage( package->name(), flags );
1689 ///////////////////////////////////////////////////////////////////
1692 // METHOD NAME : RpmDb::removePackage
1693 // METHOD TYPE : PMError
1695 void RpmDb::removePackage( const string & name_r, unsigned flags )
1697 FAILIFNOTINITIALIZED;
1698 Logfile progresslog;
1700 MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
1703 RemovePkgReport report;
1704 report.start( name_r );
1707 if ( _packagebackups ) {
1708 // FIXME solve this status report somehow
1709 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1710 if ( ! backupPackage( name_r ) ) {
1711 ERR << "backup of " << name_r << " failed" << endl;
1713 report.progress( 0 );
1715 report.progress( 100 );
1720 opts.push_back("-e");
1721 opts.push_back("--allmatches");
1723 if (flags & RPMINST_NOSCRIPTS)
1724 opts.push_back("--noscripts");
1725 if (flags & RPMINST_NODEPS)
1726 opts.push_back("--nodeps");
1727 if (flags & RPMINST_JUSTDB)
1728 opts.push_back("--justdb");
1729 if (flags & RPMINST_FORCE) {
1730 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1733 opts.push_back("--");
1734 opts.push_back(name_r.c_str());
1736 modifyDatabase(); // BEFORE run_rpm
1737 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1742 // got no progress from command, so we fake it:
1743 // 5 - command started
1744 // 50 - command completed
1746 report.progress( 5 );
1747 while (systemReadLine(line))
1749 rpmmsg += line+'\n';
1751 report.progress( 50 );
1752 int rpm_status = systemStatus();
1756 if ( rpm_status != 0 ) {
1757 // %s = name of rpm package
1758 progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
1759 progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1760 ERR << "rpm failed, message was: " << rpmmsg << endl;
1761 // FIXME err = string("Error::E_RpmDB_subprocess_failed") + endl + rpmmsg;
1762 #warning Add report->stop(Exception) here
1764 progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
1765 if( ! rpmmsg.empty() ) {
1766 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1774 RpmDb::checkPackageResult2string(unsigned code)
1777 // begin of line characters
1779 // end of line characters
1782 return string(_("Ok"))+eol;
1784 //translator: these are different kinds of how an rpm package can be broken
1785 msg = _("Package is not OK for the following reasons:");
1788 if(code&CHK_INCORRECT_VERSION)
1791 msg+=_("Package contains different version than expected");
1794 if(code&CHK_INCORRECT_FILEMD5)
1797 msg+=_("Package file has incorrect MD5 sum");
1800 if(code&CHK_GPGSIG_MISSING)
1803 msg+=_("Package is not signed");
1806 if(code&CHK_MD5SUM_MISSING)
1809 msg+=_("Package has no MD5 sum");
1812 if(code&CHK_INCORRECT_GPGSIG)
1815 msg+=_("Package has incorrect signature");
1818 if(code&CHK_INCORRECT_PKGMD5)
1821 msg+=_("Package archive has incorrect MD5 sum");
1824 if(code&CHK_OTHER_FAILURE)
1827 msg+=_("rpm failed for unkown reason, see log file");
1834 ///////////////////////////////////////////////////////////////////
1837 // METHOD NAME : RpmDb::backupPackage
1838 // METHOD TYPE : bool
1840 bool RpmDb::backupPackage( const Pathname & filename )
1842 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1846 return backupPackage( h->tag_name() );
1849 ///////////////////////////////////////////////////////////////////
1852 // METHOD NAME : RpmDb::backupPackage
1853 // METHOD TYPE : bool
1855 bool RpmDb::backupPackage(const string& packageName)
1857 Logfile progresslog;
1859 Pathname backupFilename;
1860 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1862 if (_backuppath.empty())
1864 INT << "_backuppath empty" << endl;
1870 if (!queryChangedFiles(fileList, packageName))
1872 ERR << "Error while getting changed files for package " <<
1873 packageName << endl;
1877 if (fileList.size() <= 0)
1879 DBG << "package " << packageName << " not changed -> no backup" << endl;
1883 if (filesystem::assert_dir(_root + _backuppath) != 0)
1889 // build up archive name
1890 time_t currentTime = time(0);
1891 struct tm *currentLocalTime = localtime(¤tTime);
1893 int date = (currentLocalTime->tm_year + 1900) * 10000
1894 + (currentLocalTime->tm_mon + 1) * 100
1895 + currentLocalTime->tm_mday;
1900 backupFilename = _root + _backuppath
1901 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1904 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1906 PathInfo pi(filestobackupfile);
1907 if(pi.isExist() && !pi.isFile())
1909 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1913 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1917 ERR << "could not open " << filestobackupfile.asString() << endl;
1921 for (FileList::const_iterator cit = fileList.begin();
1922 cit != fileList.end(); ++cit)
1925 if ( name[0] == '/' )
1927 // remove slash, file must be relative to -C parameter of tar
1928 name = name.substr( 1 );
1930 DBG << "saving file "<< name << endl;
1935 const char* const argv[] =
1940 _root.asString().c_str(),
1941 "--ignore-failed-read",
1943 backupFilename.asString().c_str(),
1945 filestobackupfile.asString().c_str(),
1949 // execute tar in inst-sys (we dont know if there is a tar below _root !)
1950 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1954 // TODO: its probably possible to start tar with -v and watch it adding
1955 // files to report progress
1956 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
1961 int ret = tar.close();
1965 ERR << "tar failed: " << tarmsg << endl;
1970 MIL << "tar backup ok" << endl;
1971 progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
1974 filesystem::unlink(filestobackupfile);
1980 void RpmDb::setBackupPath(const Pathname& path)
1986 } // namespace target