1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
32 #include <boost/format.hpp>
34 #include "zypp/base/Logger.h"
35 #include "zypp/base/String.h"
36 #include "zypp/base/Gettext.h"
38 #include "zypp/Date.h"
39 #include "zypp/Pathname.h"
40 #include "zypp/PathInfo.h"
41 #include "zypp/PublicKey.h"
43 #include "zypp/target/rpm/RpmDb.h"
44 #include "zypp/target/rpm/RpmCallbacks.h"
46 #include "zypp/HistoryLog.h"
47 #include "zypp/target/rpm/librpmDb.h"
48 #include "zypp/target/rpm/RpmException.h"
49 #include "zypp/TmpPath.h"
50 #include "zypp/KeyRing.h"
51 #include "zypp/ZYppFactory.h"
52 #include "zypp/ZConfig.h"
55 using namespace zypp::filesystem;
57 #define WARNINGMAILPATH "/var/log/YaST2/"
58 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
59 #define MAXRPMMESSAGELINES 10000
61 #define WORKAROUNDRPMPWDBUG
65 namespace zypp_readonly_hack
67 bool IGotIt(); // in readonly-mode
75 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
76 const char* quoteInFilename_m = "\'\"";
78 const char* quoteInFilename_m = " \t\'\"";
80 inline string rpmQuoteFilename( const Pathname & path_r )
82 string path( path_r.asString() );
83 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
85 pos = path.find_first_of( quoteInFilename_m, pos ) )
87 path.insert( pos, "\\" );
88 pos += 2; // skip '\\' and the quoted char.
94 /** Workaround bnc#827609 - rpm needs a readable pwd so we
95 * chdir to /. Turn realtive pathnames into absolute ones
96 * by prepending cwd so rpm still finds them
98 inline Pathname workaroundRpmPwdBug( Pathname path_r )
100 #if defined(WORKAROUNDRPMPWDBUG)
101 if ( path_r.relative() )
103 // try to prepend cwd
104 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
106 return Pathname( cwd ) / path_r;
107 WAR << "Can't get cwd!" << endl;
110 return path_r; // no problem with absolute pathnames
114 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
116 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
121 ~KeyRingSignalReceiver()
126 virtual void trustedKeyAdded( const PublicKey &key )
128 MIL << "trusted key added to zypp Keyring. Importing" << endl;
129 // now import the key in rpm
132 _rpmdb.importPubkey( key );
134 catch (RpmException &e)
136 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
140 virtual void trustedKeyRemoved( const PublicKey &key )
142 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
144 // remove the key from rpm
147 _rpmdb.removePubkey( key );
149 catch (RpmException &e)
151 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
158 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
160 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
170 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
177 for (line = prog.receiveLine(), count=0;
179 line = prog.receiveLine(), count++ )
181 if (maxlines<0?true:count<maxlines)
190 /******************************************************************
193 ** FUNCTION NAME : stringPath
194 ** FUNCTION TYPE : inline string
196 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
198 return librpmDb::stringPath( root_r, sub_r );
201 /******************************************************************
204 ** FUNCTION NAME : operator<<
205 ** FUNCTION TYPE : ostream &
207 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
209 if ( obj == RpmDb::DbSI_NO_INIT )
215 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
217 ENUM_OUT( DbSI_HAVE_V4, 'X' );
218 ENUM_OUT( DbSI_MADE_V4, 'c' );
219 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
221 ENUM_OUT( DbSI_HAVE_V3, 'X' );
222 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
223 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
232 ///////////////////////////////////////////////////////////////////
234 // CLASS NAME : RpmDb
236 ///////////////////////////////////////////////////////////////////
238 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
240 ///////////////////////////////////////////////////////////////////
242 ///////////////////////////////////////////////////////////////////
245 // METHOD NAME : RpmDb::RpmDb
246 // METHOD TYPE : Constructor
249 : _dbStateInfo( DbSI_NO_INIT )
250 #warning Check for obsolete memebers
251 , _backuppath ("/var/adm/backup")
252 , _packagebackups(false)
253 , _warndirexists(false)
257 librpmDb::globalInit();
258 // Some rpm versions are patched not to abort installation if
259 // symlink creation failed.
260 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
261 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
264 ///////////////////////////////////////////////////////////////////
267 // METHOD NAME : RpmDb::~RpmDb
268 // METHOD TYPE : Destructor
272 MIL << "~RpmDb()" << endl;
275 MIL << "~RpmDb() end" << endl;
276 sKeyRingReceiver.reset();
279 Date RpmDb::timestamp() const
284 if ( dbPath().empty() )
285 db_path = "/var/lib/rpm";
289 PathInfo rpmdb_info(root() + db_path + "/Packages");
291 if ( rpmdb_info.isExist() )
292 return rpmdb_info.mtime();
296 ///////////////////////////////////////////////////////////////////
299 // METHOD NAME : RpmDb::dumpOn
300 // METHOD TYPE : ostream &
302 ostream & RpmDb::dumpOn( ostream & str ) const
306 if ( _dbStateInfo == DbSI_NO_INIT )
312 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
314 ENUM_OUT( DbSI_HAVE_V4, 'X' );
315 ENUM_OUT( DbSI_MADE_V4, 'c' );
316 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
318 ENUM_OUT( DbSI_HAVE_V3, 'X' );
319 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
320 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
321 str << "): " << stringPath( _root, _dbPath );
327 ///////////////////////////////////////////////////////////////////
330 // METHOD NAME : RpmDb::initDatabase
331 // METHOD TYPE : PMError
333 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
335 ///////////////////////////////////////////////////////////////////
337 ///////////////////////////////////////////////////////////////////
338 bool quickinit( root_r.empty() );
340 if ( root_r.empty() )
343 if ( dbPath_r.empty() )
344 dbPath_r = "/var/lib/rpm";
346 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
348 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
349 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
352 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
353 << ( doRebuild_r ? " (rebuilddb)" : "" )
354 << ( quickinit ? " (quickinit)" : "" ) << endl;
356 ///////////////////////////////////////////////////////////////////
357 // Check whether already initialized
358 ///////////////////////////////////////////////////////////////////
361 if ( root_r == _root && dbPath_r == _dbPath )
367 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
371 ///////////////////////////////////////////////////////////////////
373 ///////////////////////////////////////////////////////////////////
374 librpmDb::unblockAccess();
378 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
382 DbStateInfoBits info = DbSI_NO_INIT;
385 internal_initDatabase( root_r, dbPath_r, info );
387 catch (const RpmException & excpt_r)
389 ZYPP_CAUGHT(excpt_r);
390 librpmDb::blockAccess();
391 ERR << "Cleanup on error: state " << info << endl;
393 if ( dbsi_has( info, DbSI_MADE_V4 ) )
395 // remove the newly created rpm4 database and
396 // any backup created on conversion.
397 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
399 ZYPP_RETHROW(excpt_r);
401 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
403 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
405 // Move obsolete rpm3 database beside.
406 MIL << "Cleanup: state " << info << endl;
407 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
408 dbsi_clr( info, DbSI_HAVE_V3 );
412 // Performing an update: Keep the original rpm3 database
413 // and wait if the rpm4 database gets modified by installing
414 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
415 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
418 #warning CHECK: notify root about conversion backup.
426 if ( dbsi_has( info, DbSI_HAVE_V4 )
427 && ! dbsi_has( info, DbSI_MADE_V4 ) )
433 MIL << "Synchronizing keys with zypp keyring" << endl;
436 // Close the database in case any write acces (create/convert)
437 // happened during init. This should drop any lock acquired
438 // by librpm. On demand it will be reopened readonly and should
439 // not hold any lock.
440 librpmDb::dbRelease( true );
442 MIL << "InitDatabase: " << *this << endl;
445 ///////////////////////////////////////////////////////////////////
448 // METHOD NAME : RpmDb::internal_initDatabase
449 // METHOD TYPE : PMError
451 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
452 DbStateInfoBits & info_r )
454 info_r = DbSI_NO_INIT;
456 ///////////////////////////////////////////////////////////////////
457 // Get info about the desired database dir
458 ///////////////////////////////////////////////////////////////////
459 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
461 if ( dbInfo.illegalArgs() )
463 // should not happen (checked in initDatabase)
464 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
466 if ( ! dbInfo.usableArgs() )
468 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
469 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
472 if ( dbInfo.hasDbV4() )
474 dbsi_set( info_r, DbSI_HAVE_V4 );
475 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
479 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
482 if ( dbInfo.hasDbV3() )
484 dbsi_set( info_r, DbSI_HAVE_V3 );
486 if ( dbInfo.hasDbV3ToV4() )
488 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
491 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
492 librpmDb::dumpState( DBG ) << endl;
494 ///////////////////////////////////////////////////////////////////
495 // Access database, create if needed
496 ///////////////////////////////////////////////////////////////////
498 // creates dbdir and empty rpm4 database if not present
499 librpmDb::dbAccess( root_r, dbPath_r );
501 if ( ! dbInfo.hasDbV4() )
504 if ( dbInfo.hasDbV4() )
506 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
510 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
511 librpmDb::dumpState( DBG ) << endl;
513 ///////////////////////////////////////////////////////////////////
514 // Check whether to convert something. Create backup but do
515 // not remove anything here
516 ///////////////////////////////////////////////////////////////////
517 librpmDb::constPtr dbptr;
518 librpmDb::dbAccess( dbptr );
519 bool dbEmpty = dbptr->empty();
522 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
525 if ( dbInfo.hasDbV3() )
527 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
531 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
532 convertV3toV4( dbInfo.dbV3().path(), dbptr );
534 // create a backup copy
535 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
538 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
543 if ( dbInfo.hasDbV3ToV4() )
545 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
546 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
554 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
555 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
556 dbsi_set( info_r, DbSI_MODIFIED_V4 );
560 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
561 librpmDb::dumpState( DBG ) << endl;
564 if ( dbInfo.hasDbV3ToV4() )
566 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
570 ///////////////////////////////////////////////////////////////////
573 // METHOD NAME : RpmDb::removeV4
574 // METHOD TYPE : void
576 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
578 const char * v3backup = "packages.rpm3";
579 const char * master = "Packages";
580 const char * index[] =
602 PathInfo pi( dbdir_r );
605 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
609 for ( const char ** f = index; *f; ++f )
614 filesystem::unlink( pi.path() );
618 pi( dbdir_r + master );
621 MIL << "Removing rpm4 database " << pi << endl;
622 filesystem::unlink( pi.path() );
627 pi( dbdir_r + v3backup );
630 MIL << "Removing converted rpm3 database backup " << pi << endl;
631 filesystem::unlink( pi.path() );
636 ///////////////////////////////////////////////////////////////////
639 // METHOD NAME : RpmDb::removeV3
640 // METHOD TYPE : void
642 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
644 const char * master = "packages.rpm";
645 const char * index[] =
647 "conflictsindex.rpm",
658 PathInfo pi( dbdir_r );
661 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
665 for ( const char ** f = index; *f; ++f )
670 filesystem::unlink( pi.path() );
674 #warning CHECK: compare vs existing v3 backup. notify root
675 pi( dbdir_r + master );
678 Pathname m( pi.path() );
681 // backup was already created
682 filesystem::unlink( m );
683 Pathname b( m.extend( "3" ) );
684 pi( b ); // stat backup
688 Pathname b( m.extend( ".deleted" ) );
692 // rempve existing backup
693 filesystem::unlink( b );
695 filesystem::rename( m, b );
696 pi( b ); // stat backup
698 MIL << "(Re)moved rpm3 database to " << pi << endl;
702 ///////////////////////////////////////////////////////////////////
705 // METHOD NAME : RpmDb::modifyDatabase
706 // METHOD TYPE : void
708 void RpmDb::modifyDatabase()
710 if ( ! initialized() )
713 // tag database as modified
714 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
716 // Move outdated rpm3 database beside.
717 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
719 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
720 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
721 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
725 ///////////////////////////////////////////////////////////////////
728 // METHOD NAME : RpmDb::closeDatabase
729 // METHOD TYPE : PMError
731 void RpmDb::closeDatabase()
733 if ( ! initialized() )
738 MIL << "Calling closeDatabase: " << *this << endl;
740 ///////////////////////////////////////////////////////////////////
741 // Block further database access
742 ///////////////////////////////////////////////////////////////////
743 librpmDb::blockAccess();
745 ///////////////////////////////////////////////////////////////////
746 // Check fate if old version database still present
747 ///////////////////////////////////////////////////////////////////
748 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
750 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
751 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
753 // Move outdated rpm3 database beside.
754 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
758 // Remove unmodified rpm4 database
759 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
763 ///////////////////////////////////////////////////////////////////
765 ///////////////////////////////////////////////////////////////////
766 _root = _dbPath = Pathname();
767 _dbStateInfo = DbSI_NO_INIT;
769 MIL << "closeDatabase: " << *this << endl;
772 ///////////////////////////////////////////////////////////////////
775 // METHOD NAME : RpmDb::rebuildDatabase
776 // METHOD TYPE : PMError
778 void RpmDb::rebuildDatabase()
780 callback::SendReport<RebuildDBReport> report;
782 report->start( root() + dbPath() );
786 doRebuildDatabase(report);
788 catch (RpmException & excpt_r)
790 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
791 ZYPP_RETHROW(excpt_r);
793 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
796 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
798 FAILIFNOTINITIALIZED;
800 MIL << "RpmDb::rebuildDatabase" << *this << endl;
801 // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
803 PathInfo dbMaster( root() + dbPath() + "Packages" );
804 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
808 opts.push_back("--rebuilddb");
809 opts.push_back("-vv");
811 // don't call modifyDatabase because it would remove the old
812 // rpm3 database, if the current database is a temporary one.
813 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
815 // progress report: watch this file growing
816 PathInfo newMaster( root()
817 + dbPath().extend( str::form( "rebuilddb.%d",
818 process?process->getpid():0) )
824 while ( systemReadLine( line ) )
827 { // file is removed at the end of rebuild.
828 // current size should be upper limit for new db
829 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
831 WAR << "User requested abort." << endl;
833 filesystem::recursive_rmdir( newMaster.path().dirname() );
837 if ( line.compare( 0, 2, "D:" ) )
839 errmsg += line + '\n';
840 // report.notify( line );
845 int rpm_status = systemStatus();
847 if ( rpm_status != 0 )
849 //TranslatorExplanation after semicolon is error message
850 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
851 (errmsg.empty() ? error_message: errmsg))));
855 report->progress( 100, root() + dbPath() ); // 100%
859 ///////////////////////////////////////////////////////////////////
862 /** \ref RpmDb::syncTrustedKeys helper
863 * Compute which keys need to be exprted to / imported from the zypp keyring.
864 * Return result via argument list.
866 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
868 ///////////////////////////////////////////////////////////////////
869 // Remember latest release and where it ocurred
873 : _inRpmKeys( nullptr )
874 , _inZyppKeys( nullptr )
877 void updateIf( const Edition & rpmKey_r )
879 std::string keyRelease( rpmKey_r.release() );
880 int comp = _release.compare( keyRelease );
883 // update to newer release
884 _release.swap( keyRelease );
885 _inRpmKeys = &rpmKey_r;
886 _inZyppKeys = nullptr;
887 if ( !keyRelease.empty() )
888 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
890 else if ( comp == 0 )
892 // stay with this release
894 _inRpmKeys = &rpmKey_r;
896 // else: this is an old release
898 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
901 void updateIf( const PublicKeyData & zyppKey_r )
903 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
904 int comp = _release.compare( keyRelease );
907 // update to newer release
908 _release.swap( keyRelease );
909 _inRpmKeys = nullptr;
910 _inZyppKeys = &zyppKey_r;
911 if ( !keyRelease.empty() )
912 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
914 else if ( comp == 0 )
916 // stay with this release
918 _inZyppKeys = &zyppKey_r;
920 // else: this is an old release
922 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
925 std::string _release;
926 const Edition * _inRpmKeys;
927 const PublicKeyData * _inZyppKeys;
929 ///////////////////////////////////////////////////////////////////
931 // collect keys by ID(version) and latest creation(release)
932 std::map<std::string,Key> _keymap;
934 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
936 _keymap[(*it).version()].updateIf( *it );
939 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
941 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
944 // compute missing keys
945 std::set<Edition> rpmKeys;
946 std::list<PublicKeyData> zyppKeys;
947 for_( it, _keymap.begin(), _keymap.end() )
949 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
950 << ( (*it).second._inRpmKeys ? "R" : "_" )
951 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
952 if ( ! (*it).second._inRpmKeys )
954 zyppKeys.push_back( *(*it).second._inZyppKeys );
956 if ( ! (*it).second._inZyppKeys )
958 rpmKeys.insert( *(*it).second._inRpmKeys );
961 rpmKeys_r.swap( rpmKeys );
962 zyppKeys_r.swap( zyppKeys );
965 ///////////////////////////////////////////////////////////////////
967 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
969 MIL << "Going to sync trusted keys..." << endl;
970 std::set<Edition> rpmKeys( pubkeyEditions() );
971 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
972 computeKeyRingSync( rpmKeys, zyppKeys );
973 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
974 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
976 ///////////////////////////////////////////////////////////////////
977 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
979 // export to zypp keyring
980 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
981 // Temporarily disconnect to prevent the attemt to re-import the exported keys.
982 callback::TempConnect<KeyRingSignals> tempDisconnect;
983 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
985 TmpFile tmpfile( getZYpp()->tmpPath() );
987 ofstream tmpos( tmpfile.path().c_str() );
988 for_( it, rpmKeys.begin(), rpmKeys.end() )
990 // we export the rpm key into a file
991 RpmHeader::constPtr result;
992 getData( string("gpg-pubkey"), *it, result );
993 tmpos << result->tag_description() << endl;
998 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1000 catch (Exception &e)
1002 ERR << "Could not import keys into in zypp keyring" << endl;
1006 ///////////////////////////////////////////////////////////////////
1007 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1009 // import from zypp keyring
1010 MIL << "Importing zypp trusted keyring" << std::endl;
1011 for_( it, zyppKeys.begin(), zyppKeys.end() )
1015 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1017 catch ( const RpmException & exp )
1023 MIL << "Trusted keys synced." << endl;
1026 void RpmDb::importZyppKeyRingTrustedKeys()
1027 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
1029 void RpmDb::exportTrustedKeysInZyppKeyRing()
1030 { syncTrustedKeys( SYNC_TO_KEYRING ); }
1032 ///////////////////////////////////////////////////////////////////
1035 // METHOD NAME : RpmDb::importPubkey
1036 // METHOD TYPE : PMError
1038 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1040 FAILIFNOTINITIALIZED;
1042 // bnc#828672: On the fly key import in READONLY
1043 if ( zypp_readonly_hack::IGotIt() )
1045 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1049 // check if the key is already in the rpm database
1050 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1051 set<Edition> rpmKeys = pubkeyEditions();
1052 bool hasOldkeys = false;
1054 for_( it, rpmKeys.begin(), rpmKeys.end() )
1056 if ( keyEd == *it ) // quick test (Edition is IdStringType!)
1058 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1062 if ( keyEd.version() != (*it).version() )
1063 continue; // different key ID (version)
1065 if ( keyEd.release() < (*it).release() )
1067 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1075 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1079 // We must explicitly delete old key IDs first (all releases,
1080 // that's why we don't call removePubkey here).
1081 std::string keyName( "gpg-pubkey-" + keyEd.version() );
1083 opts.push_back ( "-e" );
1084 opts.push_back ( "--allmatches" );
1085 opts.push_back ( "--" );
1086 opts.push_back ( keyName.c_str() );
1087 // don't call modifyDatabase because it would remove the old
1088 // rpm3 database, if the current database is a temporary one.
1089 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1092 while ( systemReadLine( line ) )
1094 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1097 if ( systemStatus() != 0 )
1099 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1103 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1107 // import the new key
1109 opts.push_back ( "--import" );
1110 opts.push_back ( "--" );
1111 std::string pubkeypath( pubkey_r.path().asString() );
1112 opts.push_back ( pubkeypath.c_str() );
1114 // don't call modifyDatabase because it would remove the old
1115 // rpm3 database, if the current database is a temporary one.
1116 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1119 while ( systemReadLine( line ) )
1121 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1124 if ( systemStatus() != 0 )
1126 //TranslatorExplanation first %s is file name, second is error message
1127 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1128 _("Failed to import public key from file %s: %s"))
1129 % pubkey_r.asString() % error_message)));
1133 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1137 ///////////////////////////////////////////////////////////////////
1140 // METHOD NAME : RpmDb::removePubkey
1141 // METHOD TYPE : PMError
1143 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1145 FAILIFNOTINITIALIZED;
1147 // check if the key is in the rpm database and just
1148 // return if it does not.
1149 set<Edition> rpm_keys = pubkeyEditions();
1150 set<Edition>::const_iterator found_edition = rpm_keys.end();
1151 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1153 for_( it, rpm_keys.begin(), rpm_keys.end() )
1155 if ( (*it).version() == pubkeyVersion )
1162 // the key does not exist, cannot be removed
1163 if (found_edition == rpm_keys.end())
1165 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1169 string rpm_name("gpg-pubkey-" + found_edition->asString());
1172 opts.push_back ( "-e" );
1173 opts.push_back ( "--" );
1174 opts.push_back ( rpm_name.c_str() );
1176 // don't call modifyDatabase because it would remove the old
1177 // rpm3 database, if the current database is a temporary one.
1178 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1181 while ( systemReadLine( line ) )
1183 if ( line.substr( 0, 6 ) == "error:" )
1185 WAR << line << endl;
1189 DBG << line << endl;
1193 int rpm_status = systemStatus();
1195 if ( rpm_status != 0 )
1197 //TranslatorExplanation first %s is key name, second is error message
1198 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
1199 _("Failed to remove public key %s: %s")) % pubkey_r.asString()
1204 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1208 ///////////////////////////////////////////////////////////////////
1211 // METHOD NAME : RpmDb::pubkeys
1212 // METHOD TYPE : set<Edition>
1214 list<PublicKey> RpmDb::pubkeys() const
1216 list<PublicKey> ret;
1218 librpmDb::db_const_iterator it;
1219 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1221 Edition edition = it->tag_edition();
1222 if (edition != Edition::noedition)
1224 // we export the rpm key into a file
1225 RpmHeader::constPtr result;
1226 getData( string("gpg-pubkey"), edition, result );
1227 TmpFile file(getZYpp()->tmpPath());
1231 os.open(file.path().asString().c_str());
1232 // dump rpm key into the tmp file
1233 os << result->tag_description();
1234 //MIL << "-----------------------------------------------" << endl;
1235 //MIL << result->tag_description() <<endl;
1236 //MIL << "-----------------------------------------------" << endl;
1238 // read the public key from the dumped file
1239 PublicKey key(file);
1242 catch (exception &e)
1244 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1245 // just ignore the key
1252 set<Edition> RpmDb::pubkeyEditions() const
1256 librpmDb::db_const_iterator it;
1257 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1259 Edition edition = it->tag_edition();
1260 if (edition != Edition::noedition)
1261 ret.insert( edition );
1267 ///////////////////////////////////////////////////////////////////
1270 // METHOD NAME : RpmDb::fileList
1271 // METHOD TYPE : bool
1276 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1278 list<FileInfo> result;
1280 librpmDb::db_const_iterator it;
1282 if (edition_r == Edition::noedition)
1284 found = it.findPackage( name_r );
1288 found = it.findPackage( name_r, edition_r );
1297 ///////////////////////////////////////////////////////////////////
1300 // METHOD NAME : RpmDb::hasFile
1301 // METHOD TYPE : bool
1305 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1307 librpmDb::db_const_iterator it;
1311 res = it.findByFile( file_r );
1313 if (!name_r.empty())
1315 res = (it->tag_name() == name_r);
1323 ///////////////////////////////////////////////////////////////////
1326 // METHOD NAME : RpmDb::whoOwnsFile
1327 // METHOD TYPE : string
1331 string RpmDb::whoOwnsFile( const string & file_r) const
1333 librpmDb::db_const_iterator it;
1334 if (it.findByFile( file_r ))
1336 return it->tag_name();
1341 ///////////////////////////////////////////////////////////////////
1344 // METHOD NAME : RpmDb::hasProvides
1345 // METHOD TYPE : bool
1349 bool RpmDb::hasProvides( const string & tag_r ) const
1351 librpmDb::db_const_iterator it;
1352 return it.findByProvides( tag_r );
1355 ///////////////////////////////////////////////////////////////////
1358 // METHOD NAME : RpmDb::hasRequiredBy
1359 // METHOD TYPE : bool
1363 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1365 librpmDb::db_const_iterator it;
1366 return it.findByRequiredBy( tag_r );
1369 ///////////////////////////////////////////////////////////////////
1372 // METHOD NAME : RpmDb::hasConflicts
1373 // METHOD TYPE : bool
1377 bool RpmDb::hasConflicts( const string & tag_r ) const
1379 librpmDb::db_const_iterator it;
1380 return it.findByConflicts( tag_r );
1383 ///////////////////////////////////////////////////////////////////
1386 // METHOD NAME : RpmDb::hasPackage
1387 // METHOD TYPE : bool
1391 bool RpmDb::hasPackage( const string & name_r ) const
1393 librpmDb::db_const_iterator it;
1394 return it.findPackage( name_r );
1397 ///////////////////////////////////////////////////////////////////
1400 // METHOD NAME : RpmDb::hasPackage
1401 // METHOD TYPE : bool
1405 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1407 librpmDb::db_const_iterator it;
1408 return it.findPackage( name_r, ed_r );
1411 ///////////////////////////////////////////////////////////////////
1414 // METHOD NAME : RpmDb::getData
1415 // METHOD TYPE : PMError
1419 void RpmDb::getData( const string & name_r,
1420 RpmHeader::constPtr & result_r ) const
1422 librpmDb::db_const_iterator it;
1423 it.findPackage( name_r );
1426 ZYPP_THROW(*(it.dbError()));
1429 ///////////////////////////////////////////////////////////////////
1432 // METHOD NAME : RpmDb::getData
1433 // METHOD TYPE : void
1437 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1438 RpmHeader::constPtr & result_r ) const
1440 librpmDb::db_const_iterator it;
1441 it.findPackage( name_r, ed_r );
1444 ZYPP_THROW(*(it.dbError()));
1447 ///////////////////////////////////////////////////////////////////
1450 struct RpmlogCapture : public std::string
1453 { rpmlog()._cap = this; }
1456 { rpmlog()._cap = nullptr; }
1464 rpmlogSetCallback( rpmLogCB, this );
1465 rpmSetVerbosity( RPMLOG_INFO );
1466 _f = ::fopen( "/dev/null","w");
1467 rpmlogSetFile( _f );
1471 { if ( _f ) ::fclose( _f ); }
1473 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1474 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1476 int rpmLog( rpmlogRec rec_r )
1478 if ( _cap ) (*_cap) = rpmlogRecMessage( rec_r );
1479 return RPMLOG_DEFAULT;
1486 static Rpmlog & rpmlog()
1487 { static Rpmlog _rpmlog; return _rpmlog; }
1492 ///////////////////////////////////////////////////////////////////
1494 // METHOD NAME : RpmDb::checkPackage
1495 // METHOD TYPE : RpmDb::CheckPackageResult
1497 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1499 PathInfo file( path_r );
1500 if ( ! file.isFile() )
1502 ERR << "Not a file: " << file << endl;
1506 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1507 if ( fd == 0 || ::Ferror(fd) )
1509 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1514 rpmts ts = ::rpmtsCreate();
1515 ::rpmtsSetRootDir( ts, root().asString().c_str() );
1516 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1518 rpmQVKArguments_s qva;
1519 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1520 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1522 RpmlogCapture vresult;
1523 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1531 detail_r.push_back( CheckPackageDetail::value_type( CHK_OK, std::move(vresult) ) );
1535 // results per line...
1537 std::vector<std::string> lines;
1538 str::split( vresult, std::back_inserter(lines), "\n" );
1539 unsigned count[6] = { 0, 0, 0, 0, 0, 0 };
1541 for ( unsigned i = 1; i < lines.size(); ++i )
1543 std::string & line( lines[i] );
1544 CheckPackageResult lineres = CHK_ERROR;
1545 if ( line.find( ": OK" ) != std::string::npos )
1546 { lineres = CHK_OK; }
1547 else if ( line.find( ": NOKEY" ) != std::string::npos )
1548 { lineres = CHK_NOKEY; }
1549 else if ( line.find( ": BAD" ) != std::string::npos )
1550 { lineres = CHK_FAIL; }
1551 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1552 { lineres = CHK_NOTFOUND; }
1553 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1554 { lineres = CHK_NOTTRUSTED; }
1557 detail_r.push_back( CheckPackageDetail::value_type( lineres, std::move(line) ) );
1560 CheckPackageResult ret = CHK_ERROR;
1561 if ( count[CHK_FAIL] )
1564 else if ( count[CHK_NOTFOUND] )
1567 else if ( count[CHK_NOKEY] )
1570 else if ( count[CHK_NOTTRUSTED] )
1571 ret = CHK_NOTTRUSTED;
1576 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1577 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1580 // determine changed files of installed package
1582 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1588 if ( ! initialized() ) return false;
1592 opts.push_back ("-V");
1593 opts.push_back ("--nodeps");
1594 opts.push_back ("--noscripts");
1595 opts.push_back ("--nomd5");
1596 opts.push_back ("--");
1597 opts.push_back (packageName.c_str());
1599 run_rpm (opts, ExternalProgram::Discard_Stderr);
1601 if ( process == NULL )
1612 M Mode (includes permissions and file type)
1616 while (systemReadLine(line))
1618 if (line.length() > 12 &&
1619 (line[0] == 'S' || line[0] == 's' ||
1620 (line[0] == '.' && line[7] == 'T')))
1622 // file has been changed
1625 filename.assign(line, 11, line.length() - 11);
1626 fileList.insert(filename);
1631 // exit code ignored, rpm returns 1 no matter if package is installed or
1639 /****************************************************************/
1640 /* private member-functions */
1641 /****************************************************************/
1643 /*--------------------------------------------------------------*/
1644 /* Run rpm with the specified arguments, handling stderr */
1645 /* as specified by disp */
1646 /*--------------------------------------------------------------*/
1648 RpmDb::run_rpm (const RpmArgVec& opts,
1649 ExternalProgram::Stderr_Disposition disp)
1658 if ( ! initialized() )
1660 ZYPP_THROW(RpmDbNotOpenException());
1665 // always set root and dbpath
1666 #if defined(WORKAROUNDRPMPWDBUG)
1667 args.push_back("#/"); // chdir to / to workaround bnc#819354
1669 args.push_back("rpm");
1670 args.push_back("--root");
1671 args.push_back(_root.asString().c_str());
1672 args.push_back("--dbpath");
1673 args.push_back(_dbPath.asString().c_str());
1675 const char* argv[args.size() + opts.size() + 1];
1677 const char** p = argv;
1678 p = copy (args.begin (), args.end (), p);
1679 p = copy (opts.begin (), opts.end (), p);
1682 // Invalidate all outstanding database handles in case
1683 // the database gets modified.
1684 librpmDb::dbRelease( true );
1686 // Launch the program with default locale
1687 process = new ExternalProgram(argv, disp, false, -1, true);
1691 /*--------------------------------------------------------------*/
1692 /* Read a line from the rpm process */
1693 /*--------------------------------------------------------------*/
1694 bool RpmDb::systemReadLine( string & line )
1698 if ( process == NULL )
1701 if ( process->inputFile() )
1703 process->setBlocking( false );
1704 FILE * inputfile = process->inputFile();
1705 int inputfileFd = ::fileno( inputfile );
1708 /* Watch inputFile to see when it has input. */
1711 FD_SET( inputfileFd, &rfds );
1713 /* Wait up to 5 seconds. */
1718 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1722 ERR << "select error: " << strerror(errno) << endl;
1723 if ( errno != EINTR )
1728 // Data is available now.
1729 static size_t linebuffer_size = 0; // static because getline allocs
1730 static char * linebuffer = 0; // and reallocs if buffer is too small
1731 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1734 if ( ::feof( inputfile ) )
1735 return line.size(); // in case of pending output
1741 if ( linebuffer[nread-1] == '\n' )
1743 line += string( linebuffer, nread );
1746 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1747 return true; // complete line
1749 clearerr( inputfile );
1753 // No data within time.
1754 if ( ! process->running() )
1763 /*--------------------------------------------------------------*/
1764 /* Return the exit status of the rpm process, closing the */
1765 /* connection if not already done */
1766 /*--------------------------------------------------------------*/
1768 RpmDb::systemStatus()
1770 if ( process == NULL )
1773 exit_code = process->close();
1777 error_message = process->execError();
1782 // DBG << "exit code " << exit_code << endl;
1787 /*--------------------------------------------------------------*/
1788 /* Forcably kill the rpm process */
1789 /*--------------------------------------------------------------*/
1793 if (process) process->kill();
1797 // generate diff mails for config files
1798 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1800 string msg = line.substr(9);
1801 string::size_type pos1 = string::npos;
1802 string::size_type pos2 = string::npos;
1803 string file1s, file2s;
1807 pos1 = msg.find (typemsg);
1810 if ( pos1 == string::npos )
1813 pos2 = pos1 + strlen (typemsg);
1815 if (pos2 >= msg.length() )
1818 file1 = msg.substr (0, pos1);
1819 file2 = msg.substr (pos2);
1821 file1s = file1.asString();
1822 file2s = file2.asString();
1824 if (!_root.empty() && _root != "/")
1826 file1 = _root + file1;
1827 file2 = _root + file2;
1831 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1834 Pathname file = _root + WARNINGMAILPATH;
1835 if (filesystem::assert_dir(file) != 0)
1837 ERR << "Could not create " << file.asString() << endl;
1840 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1841 ofstream notify(file.asString().c_str(), ios::out|ios::app);
1844 ERR << "Could not open " << file << endl;
1848 // Translator: %s = name of an rpm package. A list of diffs follows
1850 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1853 ERR << "diff failed" << endl;
1854 notify << str::form(difffailmsg,
1855 file1s.c_str(), file2s.c_str()) << endl;
1859 notify << str::form(diffgenmsg,
1860 file1s.c_str(), file2s.c_str()) << endl;
1862 // remove root for the viewer's pleasure (#38240)
1863 if (!_root.empty() && _root != "/")
1865 if (out.substr(0,4) == "--- ")
1867 out.replace(4, file1.asString().length(), file1s);
1869 string::size_type pos = out.find("\n+++ ");
1870 if (pos != string::npos)
1872 out.replace(pos+5, file2.asString().length(), file2s);
1875 notify << out << endl;
1878 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1883 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1889 ///////////////////////////////////////////////////////////////////
1892 // METHOD NAME : RpmDb::installPackage
1893 // METHOD TYPE : PMError
1895 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1897 callback::SendReport<RpmInstallReport> report;
1899 report->start(filename);
1904 doInstallPackage(filename, flags, report);
1908 catch (RpmException & excpt_r)
1910 RpmInstallReport::Action user = report->problem( excpt_r );
1912 if ( user == RpmInstallReport::ABORT )
1914 report->finish( excpt_r );
1915 ZYPP_RETHROW(excpt_r);
1917 else if ( user == RpmInstallReport::IGNORE )
1925 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1927 FAILIFNOTINITIALIZED;
1928 HistoryLog historylog;
1930 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1934 if ( _packagebackups )
1936 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1937 if ( ! backupPackage( filename ) )
1939 ERR << "backup of " << filename.asString() << " failed" << endl;
1941 // FIXME status handling
1942 report->progress( 0 ); // allow 1% for backup creation.
1947 if (flags & RPMINST_NOUPGRADE)
1948 opts.push_back("-i");
1950 opts.push_back("-U");
1952 opts.push_back("--percent");
1953 opts.push_back("--noglob");
1955 // ZConfig defines cross-arch installation
1956 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1957 opts.push_back("--ignorearch");
1959 if (flags & RPMINST_NODIGEST)
1960 opts.push_back("--nodigest");
1961 if (flags & RPMINST_NOSIGNATURE)
1962 opts.push_back("--nosignature");
1963 if (flags & RPMINST_EXCLUDEDOCS)
1964 opts.push_back ("--excludedocs");
1965 if (flags & RPMINST_NOSCRIPTS)
1966 opts.push_back ("--noscripts");
1967 if (flags & RPMINST_FORCE)
1968 opts.push_back ("--force");
1969 if (flags & RPMINST_NODEPS)
1970 opts.push_back ("--nodeps");
1971 if (flags & RPMINST_IGNORESIZE)
1972 opts.push_back ("--ignoresize");
1973 if (flags & RPMINST_JUSTDB)
1974 opts.push_back ("--justdb");
1975 if (flags & RPMINST_TEST)
1976 opts.push_back ("--test");
1977 if (flags & RPMINST_NOPOSTTRANS)
1978 opts.push_back ("--noposttrans");
1980 opts.push_back("--");
1982 // rpm requires additional quoting of special chars:
1983 string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1984 opts.push_back ( quotedFilename.c_str() );
1986 modifyDatabase(); // BEFORE run_rpm
1987 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1991 vector<string> configwarnings;
1993 unsigned linecnt = 0;
1994 while (systemReadLine(line))
1996 if ( linecnt < MAXRPMMESSAGELINES )
2001 if (line.substr(0,2)=="%%")
2004 sscanf (line.c_str () + 2, "%d", &percent);
2005 report->progress( percent );
2008 rpmmsg += line+'\n';
2010 if ( line.substr(0,8) == "warning:" )
2012 configwarnings.push_back(line);
2015 if ( linecnt > MAXRPMMESSAGELINES )
2016 rpmmsg += "[truncated]\n";
2018 int rpm_status = systemStatus();
2021 for (vector<string>::iterator it = configwarnings.begin();
2022 it != configwarnings.end(); ++it)
2024 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2026 _("rpm saved %s as %s, but it was impossible to determine the difference"),
2028 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2029 processConfigFiles(*it, Pathname::basename(filename), " created as ",
2031 _("rpm created %s as %s, but it was impossible to determine the difference"),
2033 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2036 if ( rpm_status != 0 )
2039 str::form("%s install failed", Pathname::basename(filename).c_str()),
2040 true /*timestamp*/);
2042 sstr << "rpm output:" << endl << rpmmsg << endl;
2043 historylog.comment(sstr.str());
2044 // TranslatorExplanation the colon is followed by an error message
2045 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2046 (rpmmsg.empty() ? error_message : rpmmsg)));
2048 else if ( ! rpmmsg.empty() )
2051 str::form("%s installed ok", Pathname::basename(filename).c_str()),
2052 true /*timestamp*/);
2054 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2055 historylog.comment(sstr.str());
2057 // report additional rpm output in finish
2058 // TranslatorExplanation Text is followed by a ':' and the actual output.
2059 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2063 ///////////////////////////////////////////////////////////////////
2066 // METHOD NAME : RpmDb::removePackage
2067 // METHOD TYPE : PMError
2069 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2071 // 'rpm -e' does not like epochs
2072 return removePackage( package->name()
2073 + "-" + package->edition().version()
2074 + "-" + package->edition().release()
2075 + "." + package->arch().asString(), flags );
2078 ///////////////////////////////////////////////////////////////////
2081 // METHOD NAME : RpmDb::removePackage
2082 // METHOD TYPE : PMError
2084 void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
2086 callback::SendReport<RpmRemoveReport> report;
2088 report->start( name_r );
2093 doRemovePackage(name_r, flags, report);
2097 catch (RpmException & excpt_r)
2099 RpmRemoveReport::Action user = report->problem( excpt_r );
2101 if ( user == RpmRemoveReport::ABORT )
2103 report->finish( excpt_r );
2104 ZYPP_RETHROW(excpt_r);
2106 else if ( user == RpmRemoveReport::IGNORE )
2115 void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2117 FAILIFNOTINITIALIZED;
2118 HistoryLog historylog;
2120 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2123 if ( _packagebackups )
2125 // FIXME solve this status report somehow
2126 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2127 if ( ! backupPackage( name_r ) )
2129 ERR << "backup of " << name_r << " failed" << endl;
2131 report->progress( 0 );
2135 report->progress( 100 );
2140 opts.push_back("-e");
2141 opts.push_back("--allmatches");
2143 if (flags & RPMINST_NOSCRIPTS)
2144 opts.push_back("--noscripts");
2145 if (flags & RPMINST_NODEPS)
2146 opts.push_back("--nodeps");
2147 if (flags & RPMINST_JUSTDB)
2148 opts.push_back("--justdb");
2149 if (flags & RPMINST_TEST)
2150 opts.push_back ("--test");
2151 if (flags & RPMINST_FORCE)
2153 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2156 opts.push_back("--");
2157 opts.push_back(name_r.c_str());
2159 modifyDatabase(); // BEFORE run_rpm
2160 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2165 // got no progress from command, so we fake it:
2166 // 5 - command started
2167 // 50 - command completed
2169 report->progress( 5 );
2170 unsigned linecnt = 0;
2171 while (systemReadLine(line))
2173 if ( linecnt < MAXRPMMESSAGELINES )
2177 rpmmsg += line+'\n';
2179 if ( linecnt > MAXRPMMESSAGELINES )
2180 rpmmsg += "[truncated]\n";
2181 report->progress( 50 );
2182 int rpm_status = systemStatus();
2184 if ( rpm_status != 0 )
2187 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2189 sstr << "rpm output:" << endl << rpmmsg << endl;
2190 historylog.comment(sstr.str());
2191 // TranslatorExplanation the colon is followed by an error message
2192 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
2193 (rpmmsg.empty() ? error_message: rpmmsg)));
2195 else if ( ! rpmmsg.empty() )
2198 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2201 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2202 historylog.comment(sstr.str());
2204 // report additional rpm output in finish
2205 // TranslatorExplanation Text is followed by a ':' and the actual output.
2206 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2210 ///////////////////////////////////////////////////////////////////
2213 // METHOD NAME : RpmDb::backupPackage
2214 // METHOD TYPE : bool
2216 bool RpmDb::backupPackage( const Pathname & filename )
2218 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2222 return backupPackage( h->tag_name() );
2225 ///////////////////////////////////////////////////////////////////
2228 // METHOD NAME : RpmDb::backupPackage
2229 // METHOD TYPE : bool
2231 bool RpmDb::backupPackage(const string& packageName)
2233 HistoryLog progresslog;
2235 Pathname backupFilename;
2236 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2238 if (_backuppath.empty())
2240 INT << "_backuppath empty" << endl;
2246 if (!queryChangedFiles(fileList, packageName))
2248 ERR << "Error while getting changed files for package " <<
2249 packageName << endl;
2253 if (fileList.size() <= 0)
2255 DBG << "package " << packageName << " not changed -> no backup" << endl;
2259 if (filesystem::assert_dir(_root + _backuppath) != 0)
2265 // build up archive name
2266 time_t currentTime = time(0);
2267 struct tm *currentLocalTime = localtime(¤tTime);
2269 int date = (currentLocalTime->tm_year + 1900) * 10000
2270 + (currentLocalTime->tm_mon + 1) * 100
2271 + currentLocalTime->tm_mday;
2276 backupFilename = _root + _backuppath
2277 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2280 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2282 PathInfo pi(filestobackupfile);
2283 if (pi.isExist() && !pi.isFile())
2285 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2289 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2293 ERR << "could not open " << filestobackupfile.asString() << endl;
2297 for (FileList::const_iterator cit = fileList.begin();
2298 cit != fileList.end(); ++cit)
2301 if ( name[0] == '/' )
2303 // remove slash, file must be relative to -C parameter of tar
2304 name = name.substr( 1 );
2306 DBG << "saving file "<< name << endl;
2311 const char* const argv[] =
2316 _root.asString().c_str(),
2317 "--ignore-failed-read",
2319 backupFilename.asString().c_str(),
2321 filestobackupfile.asString().c_str(),
2325 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2326 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2330 // TODO: its probably possible to start tar with -v and watch it adding
2331 // files to report progress
2332 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2337 int ret = tar.close();
2341 ERR << "tar failed: " << tarmsg << endl;
2346 MIL << "tar backup ok" << endl;
2347 progresslog.comment(
2348 str::form(_("created backup %s"), backupFilename.asString().c_str())
2349 , /*timestamp*/true);
2352 filesystem::unlink(filestobackupfile);
2358 void RpmDb::setBackupPath(const Pathname& path)
2363 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2367 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2368 // translators: possible rpm package signature check result [brief]
2369 OUTS( CHK_OK, _("Signature is OK") );
2370 // translators: possible rpm package signature check result [brief]
2371 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2372 // translators: possible rpm package signature check result [brief]
2373 OUTS( CHK_FAIL, _("Signature does not verify") );
2374 // translators: possible rpm package signature check result [brief]
2375 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2376 // translators: possible rpm package signature check result [brief]
2377 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2378 // translators: possible rpm package signature check result [brief]
2379 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2382 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2385 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2387 for ( const auto & el : obj )
2388 str << el.second << endl;
2393 } // namespace target