1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
32 #include "zypp/base/Logger.h"
33 #include "zypp/base/String.h"
34 #include "zypp/base/Gettext.h"
35 #include "zypp/base/LocaleGuard.h"
36 #include "zypp/base/DtorReset.h"
38 #include "zypp/Date.h"
39 #include "zypp/Pathname.h"
40 #include "zypp/PathInfo.h"
41 #include "zypp/PublicKey.h"
42 #include "zypp/ProgressData.h"
44 #include "zypp/target/rpm/RpmDb.h"
45 #include "zypp/target/rpm/RpmCallbacks.h"
47 #include "zypp/HistoryLog.h"
48 #include "zypp/target/rpm/librpmDb.h"
49 #include "zypp/target/rpm/RpmException.h"
50 #include "zypp/TmpPath.h"
51 #include "zypp/KeyRing.h"
52 #include "zypp/ZYppFactory.h"
53 #include "zypp/ZConfig.h"
56 using namespace zypp::filesystem;
58 #define WARNINGMAILPATH "/var/log/YaST2/"
59 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
60 #define MAXRPMMESSAGELINES 10000
62 #define WORKAROUNDRPMPWDBUG
64 #undef ZYPP_BASE_LOGGER_LOGGROUP
65 #define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
69 namespace zypp_readonly_hack
71 bool IGotIt(); // in readonly-mode
79 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
80 const char* quoteInFilename_m = "\'\"";
82 const char* quoteInFilename_m = " \t\'\"";
84 inline std::string rpmQuoteFilename( const Pathname & path_r )
86 std::string path( path_r.asString() );
87 for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
88 pos != std::string::npos;
89 pos = path.find_first_of( quoteInFilename_m, pos ) )
91 path.insert( pos, "\\" );
92 pos += 2; // skip '\\' and the quoted char.
98 /** Workaround bnc#827609 - rpm needs a readable pwd so we
99 * chdir to /. Turn realtive pathnames into absolute ones
100 * by prepending cwd so rpm still finds them
102 inline Pathname workaroundRpmPwdBug( Pathname path_r )
104 #if defined(WORKAROUNDRPMPWDBUG)
105 if ( path_r.relative() )
107 // try to prepend cwd
108 AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
110 return Pathname( cwd ) / path_r;
111 WAR << "Can't get cwd!" << endl;
114 return path_r; // no problem with absolute pathnames
118 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
120 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
125 ~KeyRingSignalReceiver()
130 virtual void trustedKeyAdded( const PublicKey &key )
132 MIL << "trusted key added to zypp Keyring. Importing..." << endl;
133 _rpmdb.importPubkey( key );
136 virtual void trustedKeyRemoved( const PublicKey &key )
138 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
139 _rpmdb.removePubkey( key );
145 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
147 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
157 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
164 for (line = prog.receiveLine(), count=0;
166 line = prog.receiveLine(), count++ )
168 if (maxlines<0?true:count<maxlines)
177 /******************************************************************
180 ** FUNCTION NAME : stringPath
181 ** FUNCTION TYPE : inline std::string
183 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
185 return librpmDb::stringPath( root_r, sub_r );
188 ///////////////////////////////////////////////////////////////////
190 // CLASS NAME : RpmDb
192 ///////////////////////////////////////////////////////////////////
194 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
196 ///////////////////////////////////////////////////////////////////
198 ///////////////////////////////////////////////////////////////////
201 // METHOD NAME : RpmDb::RpmDb
202 // METHOD TYPE : Constructor
205 : _backuppath ("/var/adm/backup")
206 , _packagebackups(false)
210 librpmDb::globalInit();
211 // Some rpm versions are patched not to abort installation if
212 // symlink creation failed.
213 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
214 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
217 ///////////////////////////////////////////////////////////////////
220 // METHOD NAME : RpmDb::~RpmDb
221 // METHOD TYPE : Destructor
225 MIL << "~RpmDb()" << endl;
228 MIL << "~RpmDb() end" << endl;
229 sKeyRingReceiver.reset();
232 ///////////////////////////////////////////////////////////////////
235 // METHOD NAME : RpmDb::dumpOn
236 // METHOD TYPE : std::ostream &
238 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
240 return str << "RpmDb[" << stringPath( _root, _dbPath ) << "]";
243 ///////////////////////////////////////////////////////////////////
246 // METHOD NAME : RpmDb::initDatabase
247 // METHOD TYPE : PMError
249 void RpmDb::initDatabase( Pathname root_r, bool doRebuild_r )
251 ///////////////////////////////////////////////////////////////////
253 ///////////////////////////////////////////////////////////////////
254 bool quickinit( root_r.empty() );
256 if ( root_r.empty() )
259 // NOTE: Former argument, but now locked to "/var/lib/rpm".
260 // A custom dbPath is not actually needed and would only work
261 // reliably if libsolv also supports it. By now no further
262 // cleanup in the code.
263 const Pathname & dbPath_r { librpmDb::defaultDbPath() };
265 if ( ! root_r.absolute() )
267 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
268 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
271 if ( ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
272 && PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
274 WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
275 filesystem::symlink( "../../usr/lib/sysimage/rpm", root_r/"/var/lib/rpm" );
278 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
279 << ( doRebuild_r ? " (rebuilddb)" : "" )
280 << ( quickinit ? " (quickinit)" : "" ) << endl;
282 ///////////////////////////////////////////////////////////////////
283 // Check whether already initialized
284 ///////////////////////////////////////////////////////////////////
287 if ( root_r == _root && dbPath_r == _dbPath )
293 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
297 ///////////////////////////////////////////////////////////////////
299 ///////////////////////////////////////////////////////////////////
300 librpmDb::unblockAccess();
304 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
310 // creates dbdir and empty rpm database if not present
311 librpmDb::dbAccess( root_r );
313 catch (const RpmException & excpt_r)
315 ZYPP_CAUGHT(excpt_r);
316 librpmDb::blockAccess();
317 ZYPP_RETHROW(excpt_r);
326 MIL << "Synchronizing keys with zypp keyring" << endl;
329 // Close the database in case any write acces (create/convert)
330 // happened during init. This should drop any lock acquired
331 // by librpm. On demand it will be reopened readonly and should
332 // not hold any lock.
333 librpmDb::dbRelease( true );
335 MIL << "InitDatabase: " << *this << endl;
338 ///////////////////////////////////////////////////////////////////
341 // METHOD NAME : RpmDb::closeDatabase
342 // METHOD TYPE : PMError
344 void RpmDb::closeDatabase()
346 if ( ! initialized() )
351 MIL << "Calling closeDatabase: " << *this << endl;
353 ///////////////////////////////////////////////////////////////////
354 // Block further database access
355 ///////////////////////////////////////////////////////////////////
356 librpmDb::blockAccess();
358 ///////////////////////////////////////////////////////////////////
360 ///////////////////////////////////////////////////////////////////
361 _root = _dbPath = Pathname();
363 MIL << "closeDatabase: " << *this << endl;
366 ///////////////////////////////////////////////////////////////////
369 // METHOD NAME : RpmDb::rebuildDatabase
370 // METHOD TYPE : PMError
372 void RpmDb::rebuildDatabase()
374 callback::SendReport<RebuildDBReport> report;
376 report->start( root() + dbPath() );
380 doRebuildDatabase(report);
382 catch (RpmException & excpt_r)
384 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
385 ZYPP_RETHROW(excpt_r);
387 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
390 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
392 FAILIFNOTINITIALIZED;
393 MIL << "RpmDb::rebuildDatabase" << *this << endl;
395 const Pathname mydbpath { root()/dbPath() }; // the configured path used in reports
397 // For --rebuilddb take care we're using the real db directory
398 // and not a symlink. Otherwise rpm will rename the symlink and
399 // replace it with a real directory containing the converted db.
400 DtorReset guardRoot { _root };
401 DtorReset guardDbPath{ _dbPath };
403 _dbPath = filesystem::expandlink( mydbpath );
407 opts.push_back("--rebuilddb");
408 opts.push_back("-vv");
409 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
412 // generate and report progress
415 ProgressData::value_type hdrTotal = 0;
416 for ( librpmDb::db_const_iterator it; *it; ++it, ++hdrTotal )
418 tics.range( hdrTotal );
420 tics.sendTo( [&report,&mydbpath]( const ProgressData & tics_r ) -> bool {
421 return report->progress( tics_r.reportValue(), mydbpath );
427 while ( systemReadLine( line ) )
429 static const std::string debugPrefix { "D:" };
430 static const std::string progressPrefix { "D: read h#" };
431 static const std::string ignoreSuffix { "digest: OK" };
433 if ( ! str::startsWith( line, debugPrefix ) )
435 if ( ! str::endsWith( line, ignoreSuffix ) )
442 else if ( str::startsWith( line, progressPrefix ) )
446 WAR << "User requested abort." << endl;
452 if ( systemStatus() != 0 )
454 //TranslatorExplanation after semicolon is error message
455 ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
463 ///////////////////////////////////////////////////////////////////
466 /** \ref RpmDb::syncTrustedKeys helper
467 * Compute which keys need to be exprted to / imported from the zypp keyring.
468 * Return result via argument list.
470 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
472 ///////////////////////////////////////////////////////////////////
473 // Remember latest release and where it ocurred
477 : _inRpmKeys( nullptr )
478 , _inZyppKeys( nullptr )
481 void updateIf( const Edition & rpmKey_r )
483 std::string keyRelease( rpmKey_r.release() );
484 int comp = _release.compare( keyRelease );
487 // update to newer release
488 _release.swap( keyRelease );
489 _inRpmKeys = &rpmKey_r;
490 _inZyppKeys = nullptr;
491 if ( !keyRelease.empty() )
492 DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
494 else if ( comp == 0 )
496 // stay with this release
498 _inRpmKeys = &rpmKey_r;
500 // else: this is an old release
502 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
505 void updateIf( const PublicKeyData & zyppKey_r )
507 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
508 int comp = _release.compare( keyRelease );
511 // update to newer release
512 _release.swap( keyRelease );
513 _inRpmKeys = nullptr;
514 _inZyppKeys = &zyppKey_r;
515 if ( !keyRelease.empty() )
516 DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
518 else if ( comp == 0 )
520 // stay with this release
522 _inZyppKeys = &zyppKey_r;
524 // else: this is an old release
526 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
529 std::string _release;
530 const Edition * _inRpmKeys;
531 const PublicKeyData * _inZyppKeys;
533 ///////////////////////////////////////////////////////////////////
535 // collect keys by ID(version) and latest creation(release)
536 std::map<std::string,Key> _keymap;
538 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
540 _keymap[(*it).version()].updateIf( *it );
543 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
545 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
548 // compute missing keys
549 std::set<Edition> rpmKeys;
550 std::list<PublicKeyData> zyppKeys;
551 for_( it, _keymap.begin(), _keymap.end() )
553 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
554 << ( (*it).second._inRpmKeys ? "R" : "_" )
555 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
556 if ( ! (*it).second._inRpmKeys )
558 zyppKeys.push_back( *(*it).second._inZyppKeys );
560 if ( ! (*it).second._inZyppKeys )
562 rpmKeys.insert( *(*it).second._inRpmKeys );
565 rpmKeys_r.swap( rpmKeys );
566 zyppKeys_r.swap( zyppKeys );
569 ///////////////////////////////////////////////////////////////////
571 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
573 MIL << "Going to sync trusted keys..." << endl;
574 std::set<Edition> rpmKeys( pubkeyEditions() );
575 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
577 if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
579 // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
580 // when re-acquiring the zyppp lock. For now we remove all excess keys.
581 // TODO: Once we can safely assume that all PK versions are updated we
582 // can think about re-importing newer key versions found in the zypp keyring and
583 // removing only excess ones (but case is not very likely). Unfixed PK versions
584 // however will remove the newer version found in the zypp keyring and by doing
585 // this, the key here will be removed via callback as well (keys are deleted
586 // via gpg id, regardless of the edition).
587 MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
588 // Temporarily disconnect to prevent the attempt to pass back the delete request.
589 callback::TempConnect<KeyRingSignals> tempDisconnect;
591 for ( const PublicKeyData & keyData : zyppKeys )
593 if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
595 DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
596 getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
597 if ( !dirty ) dirty = true;
601 zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
604 computeKeyRingSync( rpmKeys, zyppKeys );
605 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
606 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
608 ///////////////////////////////////////////////////////////////////
609 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
611 // export to zypp keyring
612 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
613 // Temporarily disconnect to prevent the attempt to re-import the exported keys.
614 callback::TempConnect<KeyRingSignals> tempDisconnect;
615 librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
617 TmpFile tmpfile( getZYpp()->tmpPath() );
619 std::ofstream tmpos( tmpfile.path().c_str() );
620 for_( it, rpmKeys.begin(), rpmKeys.end() )
622 // we export the rpm key into a file
623 RpmHeader::constPtr result;
624 getData( "gpg-pubkey", *it, result );
625 tmpos << result->tag_description() << endl;
630 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
631 // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
632 // Modern rpm does not import those keys, but when migrating a pre SLE12 system
633 // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
634 std::set<Edition> missingKeys;
635 for ( const Edition & key : rpmKeys )
637 if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
639 ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
640 missingKeys.insert( key );
642 if ( ! missingKeys.empty() )
643 callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
645 catch ( const Exception & excpt )
647 ZYPP_CAUGHT( excpt );
648 ERR << "Could not import keys into zypp keyring: " << endl;
652 ///////////////////////////////////////////////////////////////////
653 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
655 // import from zypp keyring
656 MIL << "Importing zypp trusted keyring" << std::endl;
657 for_( it, zyppKeys.begin(), zyppKeys.end() )
661 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
663 catch ( const RpmException & exp )
669 MIL << "Trusted keys synced." << endl;
672 void RpmDb::importZyppKeyRingTrustedKeys()
673 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
675 void RpmDb::exportTrustedKeysInZyppKeyRing()
676 { syncTrustedKeys( SYNC_TO_KEYRING ); }
678 ///////////////////////////////////////////////////////////////////
681 // METHOD NAME : RpmDb::importPubkey
682 // METHOD TYPE : PMError
684 void RpmDb::importPubkey( const PublicKey & pubkey_r )
686 FAILIFNOTINITIALIZED;
688 // bnc#828672: On the fly key import in READONLY
689 if ( zypp_readonly_hack::IGotIt() )
691 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
695 // check if the key is already in the rpm database
696 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
697 std::set<Edition> rpmKeys = pubkeyEditions();
698 bool hasOldkeys = false;
700 for_( it, rpmKeys.begin(), rpmKeys.end() )
702 // bsc#1008325: Keys using subkeys for signing don't get a higher release
703 // if new subkeys are added, because the primary key remains unchanged.
704 // For now always re-import keys with subkeys. Here we don't want to export the
705 // keys in the rpm database to check whether the subkeys are the same. The calling
706 // code should take care, we don't re-import the same kesy over and over again.
707 if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
709 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
713 if ( keyEd.version() != (*it).version() )
714 continue; // different key ID (version)
716 if ( keyEd.release() < (*it).release() )
718 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
726 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
730 // We must explicitly delete old key IDs first (all releases,
731 // that's why we don't call removePubkey here).
732 std::string keyName( "gpg-pubkey-" + keyEd.version() );
734 opts.push_back ( "-e" );
735 opts.push_back ( "--allmatches" );
736 opts.push_back ( "--" );
737 opts.push_back ( keyName.c_str() );
738 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
741 while ( systemReadLine( line ) )
743 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
746 if ( systemStatus() != 0 )
748 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
752 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
756 // import the new key
758 opts.push_back ( "--import" );
759 opts.push_back ( "--" );
760 std::string pubkeypath( pubkey_r.path().asString() );
761 opts.push_back ( pubkeypath.c_str() );
762 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
765 std::vector<std::string> excplines;
766 while ( systemReadLine( line ) )
768 if ( str::startsWith( line, "error:" ) )
771 excplines.push_back( std::move(line) );
777 if ( systemStatus() != 0 )
779 // Translator: %1% is a gpg public key
780 RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
781 excp.moveToHistory( excplines );
782 excp.addHistory( std::move(error_message) );
783 ZYPP_THROW( std::move(excp) );
787 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
791 ///////////////////////////////////////////////////////////////////
794 // METHOD NAME : RpmDb::removePubkey
795 // METHOD TYPE : PMError
797 void RpmDb::removePubkey( const PublicKey & pubkey_r )
799 FAILIFNOTINITIALIZED;
801 // check if the key is in the rpm database and just
802 // return if it does not.
803 std::set<Edition> rpm_keys = pubkeyEditions();
804 std::set<Edition>::const_iterator found_edition = rpm_keys.end();
805 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
807 for_( it, rpm_keys.begin(), rpm_keys.end() )
809 if ( (*it).version() == pubkeyVersion )
816 // the key does not exist, cannot be removed
817 if (found_edition == rpm_keys.end())
819 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
823 std::string rpm_name("gpg-pubkey-" + found_edition->asString());
826 opts.push_back ( "-e" );
827 opts.push_back ( "--" );
828 opts.push_back ( rpm_name.c_str() );
829 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
832 std::vector<std::string> excplines;
833 while ( systemReadLine( line ) )
835 if ( str::startsWith( line, "error:" ) )
838 excplines.push_back( std::move(line) );
844 if ( systemStatus() != 0 )
846 // Translator: %1% is a gpg public key
847 RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
848 excp.moveToHistory( excplines );
849 excp.addHistory( std::move(error_message) );
850 ZYPP_THROW( std::move(excp) );
854 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
858 ///////////////////////////////////////////////////////////////////
861 // METHOD NAME : RpmDb::pubkeys
862 // METHOD TYPE : std::set<Edition>
864 std::list<PublicKey> RpmDb::pubkeys() const
866 std::list<PublicKey> ret;
868 librpmDb::db_const_iterator it;
869 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
871 Edition edition = it->tag_edition();
872 if (edition != Edition::noedition)
874 // we export the rpm key into a file
875 RpmHeader::constPtr result;
876 getData( "gpg-pubkey", edition, result );
877 TmpFile file(getZYpp()->tmpPath());
881 os.open(file.path().asString().c_str());
882 // dump rpm key into the tmp file
883 os << result->tag_description();
884 //MIL << "-----------------------------------------------" << endl;
885 //MIL << result->tag_description() <<endl;
886 //MIL << "-----------------------------------------------" << endl;
888 // read the public key from the dumped file
892 catch ( std::exception & e )
894 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
895 // just ignore the key
902 std::set<Edition> RpmDb::pubkeyEditions() const
904 std::set<Edition> ret;
906 librpmDb::db_const_iterator it;
907 for ( it.findByName( "gpg-pubkey" ); *it; ++it )
909 Edition edition = it->tag_edition();
910 if (edition != Edition::noedition)
911 ret.insert( edition );
917 ///////////////////////////////////////////////////////////////////
920 // METHOD NAME : RpmDb::fileList
921 // METHOD TYPE : bool
926 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
928 std::list<FileInfo> result;
930 librpmDb::db_const_iterator it;
932 if (edition_r == Edition::noedition)
934 found = it.findPackage( name_r );
938 found = it.findPackage( name_r, edition_r );
947 ///////////////////////////////////////////////////////////////////
950 // METHOD NAME : RpmDb::hasFile
951 // METHOD TYPE : bool
955 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
957 librpmDb::db_const_iterator it;
961 res = it.findByFile( file_r );
965 res = (it->tag_name() == name_r);
973 ///////////////////////////////////////////////////////////////////
976 // METHOD NAME : RpmDb::whoOwnsFile
977 // METHOD TYPE : std::string
981 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
983 librpmDb::db_const_iterator it;
984 if (it.findByFile( file_r ))
986 return it->tag_name();
991 ///////////////////////////////////////////////////////////////////
994 // METHOD NAME : RpmDb::hasProvides
995 // METHOD TYPE : bool
999 bool RpmDb::hasProvides( const std::string & tag_r ) const
1001 librpmDb::db_const_iterator it;
1002 return it.findByProvides( tag_r );
1005 ///////////////////////////////////////////////////////////////////
1008 // METHOD NAME : RpmDb::hasRequiredBy
1009 // METHOD TYPE : bool
1013 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1015 librpmDb::db_const_iterator it;
1016 return it.findByRequiredBy( tag_r );
1019 ///////////////////////////////////////////////////////////////////
1022 // METHOD NAME : RpmDb::hasConflicts
1023 // METHOD TYPE : bool
1027 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1029 librpmDb::db_const_iterator it;
1030 return it.findByConflicts( tag_r );
1033 ///////////////////////////////////////////////////////////////////
1036 // METHOD NAME : RpmDb::hasPackage
1037 // METHOD TYPE : bool
1041 bool RpmDb::hasPackage( const std::string & name_r ) const
1043 librpmDb::db_const_iterator it;
1044 return it.findPackage( name_r );
1047 ///////////////////////////////////////////////////////////////////
1050 // METHOD NAME : RpmDb::hasPackage
1051 // METHOD TYPE : bool
1055 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1057 librpmDb::db_const_iterator it;
1058 return it.findPackage( name_r, ed_r );
1061 ///////////////////////////////////////////////////////////////////
1064 // METHOD NAME : RpmDb::getData
1065 // METHOD TYPE : PMError
1069 void RpmDb::getData( const std::string & name_r,
1070 RpmHeader::constPtr & result_r ) const
1072 librpmDb::db_const_iterator it;
1073 it.findPackage( name_r );
1076 ZYPP_THROW(*(it.dbError()));
1079 ///////////////////////////////////////////////////////////////////
1082 // METHOD NAME : RpmDb::getData
1083 // METHOD TYPE : void
1087 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1088 RpmHeader::constPtr & result_r ) const
1090 librpmDb::db_const_iterator it;
1091 it.findPackage( name_r, ed_r );
1094 ZYPP_THROW(*(it.dbError()));
1097 ///////////////////////////////////////////////////////////////////
1100 struct RpmlogCapture : public std::string
1103 { rpmlog()._cap = this; }
1106 { rpmlog()._cap = nullptr; }
1114 rpmlogSetCallback( rpmLogCB, this );
1115 rpmSetVerbosity( RPMLOG_INFO );
1116 _f = ::fopen( "/dev/null","w");
1117 rpmlogSetFile( _f );
1121 { if ( _f ) ::fclose( _f ); }
1123 static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1124 { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1126 int rpmLog( rpmlogRec rec_r )
1128 if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1129 return RPMLOG_DEFAULT;
1136 static Rpmlog & rpmlog()
1137 { static Rpmlog _rpmlog; return _rpmlog; }
1140 RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1141 const Pathname & root_r, // target root
1142 bool requireGPGSig_r, // whether no gpg signature is to be reported
1143 RpmDb::CheckPackageDetail & detail_r ) // detailed result
1145 PathInfo file( path_r );
1146 if ( ! file.isFile() )
1148 ERR << "Not a file: " << file << endl;
1149 return RpmDb::CHK_ERROR;
1152 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1153 if ( fd == 0 || ::Ferror(fd) )
1155 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1158 return RpmDb::CHK_ERROR;
1160 rpmts ts = ::rpmtsCreate();
1161 ::rpmtsSetRootDir( ts, root_r.c_str() );
1162 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1164 rpmQVKArguments_s qva;
1165 memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1166 #ifdef HAVE_NO_RPMTSSETVFYFLAGS
1167 // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
1168 // and qva_flags is not used in signature checking at all.
1169 qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1171 ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
1173 RpmlogCapture vresult;
1174 LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1175 int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1181 // results per line...
1182 // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1183 // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1184 // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1185 // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1187 // TODO: try to get SIG info from the header rather than parsing the output
1188 std::vector<std::string> lines;
1189 str::split( vresult, std::back_inserter(lines), "\n" );
1190 unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1192 for ( unsigned i = 1; i < lines.size(); ++i )
1194 std::string & line( lines[i] );
1195 RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
1196 if ( line.find( ": OK" ) != std::string::npos )
1198 lineres = RpmDb::CHK_OK;
1199 if ( line.find( "Signature, key ID" ) == std::string::npos )
1200 ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1202 else if ( line.find( ": NOKEY" ) != std::string::npos )
1203 { lineres = RpmDb::CHK_NOKEY; }
1204 else if ( line.find( ": BAD" ) != std::string::npos )
1205 { lineres = RpmDb::CHK_FAIL; }
1206 else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1207 { lineres = RpmDb::CHK_NOTFOUND; }
1208 else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1209 { lineres = RpmDb::CHK_NOTTRUSTED; }
1212 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1215 RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
1217 if ( count[RpmDb::CHK_FAIL] )
1218 ret = RpmDb::CHK_FAIL;
1220 else if ( count[RpmDb::CHK_NOTFOUND] )
1221 ret = RpmDb::CHK_NOTFOUND;
1223 else if ( count[RpmDb::CHK_NOKEY] )
1224 ret = RpmDb::CHK_NOKEY;
1226 else if ( count[RpmDb::CHK_NOTTRUSTED] )
1227 ret = RpmDb::CHK_NOTTRUSTED;
1229 else if ( ret == RpmDb::CHK_OK )
1231 if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1233 detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1234 if ( requireGPGSig_r )
1235 ret = RpmDb::CHK_NOSIG;
1239 if ( ret != RpmDb::CHK_OK )
1241 WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1248 ///////////////////////////////////////////////////////////////////
1250 // METHOD NAME : RpmDb::checkPackage
1251 // METHOD TYPE : RpmDb::CheckPackageResult
1253 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
1254 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1256 RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
1257 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1259 RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
1260 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1263 // determine changed files of installed package
1265 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1271 if ( ! initialized() ) return false;
1275 opts.push_back ("-V");
1276 opts.push_back ("--nodeps");
1277 opts.push_back ("--noscripts");
1278 opts.push_back ("--nomd5");
1279 opts.push_back ("--");
1280 opts.push_back (packageName.c_str());
1282 run_rpm (opts, ExternalProgram::Discard_Stderr);
1284 if ( process == NULL )
1295 M Mode (includes permissions and file type)
1299 while (systemReadLine(line))
1301 if (line.length() > 12 &&
1302 (line[0] == 'S' || line[0] == 's' ||
1303 (line[0] == '.' && line[7] == 'T')))
1305 // file has been changed
1306 std::string filename;
1308 filename.assign(line, 11, line.length() - 11);
1309 fileList.insert(filename);
1314 // exit code ignored, rpm returns 1 no matter if package is installed or
1322 /****************************************************************/
1323 /* private member-functions */
1324 /****************************************************************/
1326 /*--------------------------------------------------------------*/
1327 /* Run rpm with the specified arguments, handling stderr */
1328 /* as specified by disp */
1329 /*--------------------------------------------------------------*/
1331 RpmDb::run_rpm (const RpmArgVec& opts,
1332 ExternalProgram::Stderr_Disposition disp)
1341 if ( ! initialized() )
1343 ZYPP_THROW(RpmDbNotOpenException());
1348 // always set root and dbpath
1349 #if defined(WORKAROUNDRPMPWDBUG)
1350 args.push_back("#/"); // chdir to / to workaround bnc#819354
1352 args.push_back("rpm");
1353 args.push_back("--root");
1354 args.push_back(_root.asString().c_str());
1355 args.push_back("--dbpath");
1356 args.push_back(_dbPath.asString().c_str());
1358 const char* argv[args.size() + opts.size() + 1];
1360 const char** p = argv;
1361 p = copy (args.begin (), args.end (), p);
1362 p = copy (opts.begin (), opts.end (), p);
1365 // Invalidate all outstanding database handles in case
1366 // the database gets modified.
1367 librpmDb::dbRelease( true );
1369 // Launch the program with default locale
1370 process = new ExternalProgram(argv, disp, false, -1, true);
1374 /*--------------------------------------------------------------*/
1375 /* Read a line from the rpm process */
1376 /*--------------------------------------------------------------*/
1377 bool RpmDb::systemReadLine( std::string & line )
1381 if ( process == NULL )
1384 if ( process->inputFile() )
1386 process->setBlocking( false );
1387 FILE * inputfile = process->inputFile();
1388 int inputfileFd = ::fileno( inputfile );
1391 /* Watch inputFile to see when it has input. */
1394 FD_SET( inputfileFd, &rfds );
1396 /* Wait up to 5 seconds. */
1401 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1405 ERR << "select error: " << strerror(errno) << endl;
1406 if ( errno != EINTR )
1411 // Data is available now.
1412 static size_t linebuffer_size = 0; // static because getline allocs
1413 static char * linebuffer = 0; // and reallocs if buffer is too small
1414 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1417 if ( ::feof( inputfile ) )
1418 return line.size(); // in case of pending output
1424 if ( linebuffer[nread-1] == '\n' )
1426 line += std::string( linebuffer, nread );
1429 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1430 return true; // complete line
1432 clearerr( inputfile );
1436 // No data within time.
1437 if ( ! process->running() )
1446 /*--------------------------------------------------------------*/
1447 /* Return the exit status of the rpm process, closing the */
1448 /* connection if not already done */
1449 /*--------------------------------------------------------------*/
1451 RpmDb::systemStatus()
1453 if ( process == NULL )
1456 exit_code = process->close();
1460 error_message = process->execError();
1465 // DBG << "exit code " << exit_code << endl;
1470 /*--------------------------------------------------------------*/
1471 /* Forcably kill the rpm process */
1472 /*--------------------------------------------------------------*/
1476 if (process) process->kill();
1480 // generate diff mails for config files
1481 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1483 std::string msg = line.substr(9);
1484 std::string::size_type pos1 = std::string::npos;
1485 std::string::size_type pos2 = std::string::npos;
1486 std::string file1s, file2s;
1490 pos1 = msg.find (typemsg);
1493 if ( pos1 == std::string::npos )
1496 pos2 = pos1 + strlen (typemsg);
1498 if (pos2 >= msg.length() )
1501 file1 = msg.substr (0, pos1);
1502 file2 = msg.substr (pos2);
1504 file1s = file1.asString();
1505 file2s = file2.asString();
1507 if (!_root.empty() && _root != "/")
1509 file1 = _root + file1;
1510 file2 = _root + file2;
1514 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1517 Pathname file = _root + WARNINGMAILPATH;
1518 if (filesystem::assert_dir(file) != 0)
1520 ERR << "Could not create " << file.asString() << endl;
1523 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1524 std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1527 ERR << "Could not open " << file << endl;
1531 // Translator: %s = name of an rpm package. A list of diffs follows
1533 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1536 ERR << "diff failed" << endl;
1537 notify << str::form(difffailmsg,
1538 file1s.c_str(), file2s.c_str()) << endl;
1542 notify << str::form(diffgenmsg,
1543 file1s.c_str(), file2s.c_str()) << endl;
1545 // remove root for the viewer's pleasure (#38240)
1546 if (!_root.empty() && _root != "/")
1548 if (out.substr(0,4) == "--- ")
1550 out.replace(4, file1.asString().length(), file1s);
1552 std::string::size_type pos = out.find("\n+++ ");
1553 if (pos != std::string::npos)
1555 out.replace(pos+5, file2.asString().length(), file2s);
1558 notify << out << endl;
1561 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1566 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1572 ///////////////////////////////////////////////////////////////////
1575 // METHOD NAME : RpmDb::installPackage
1576 // METHOD TYPE : PMError
1578 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1580 callback::SendReport<RpmInstallReport> report;
1582 report->start(filename);
1587 doInstallPackage(filename, flags, report);
1591 catch (RpmException & excpt_r)
1593 RpmInstallReport::Action user = report->problem( excpt_r );
1595 if ( user == RpmInstallReport::ABORT )
1597 report->finish( excpt_r );
1598 ZYPP_RETHROW(excpt_r);
1600 else if ( user == RpmInstallReport::IGNORE )
1608 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
1610 FAILIFNOTINITIALIZED;
1611 HistoryLog historylog;
1613 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1617 if ( _packagebackups )
1619 // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1620 if ( ! backupPackage( filename ) )
1622 ERR << "backup of " << filename.asString() << " failed" << endl;
1624 // FIXME status handling
1625 report->progress( 0 ); // allow 1% for backup creation.
1630 if (flags & RPMINST_NOUPGRADE)
1631 opts.push_back("-i");
1633 opts.push_back("-U");
1635 opts.push_back("--percent");
1636 opts.push_back("--noglob");
1638 // ZConfig defines cross-arch installation
1639 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
1640 opts.push_back("--ignorearch");
1642 if (flags & RPMINST_NODIGEST)
1643 opts.push_back("--nodigest");
1644 if (flags & RPMINST_NOSIGNATURE)
1645 opts.push_back("--nosignature");
1646 if (flags & RPMINST_EXCLUDEDOCS)
1647 opts.push_back ("--excludedocs");
1648 if (flags & RPMINST_NOSCRIPTS)
1649 opts.push_back ("--noscripts");
1650 if (flags & RPMINST_FORCE)
1651 opts.push_back ("--force");
1652 if (flags & RPMINST_NODEPS)
1653 opts.push_back ("--nodeps");
1654 if (flags & RPMINST_IGNORESIZE)
1655 opts.push_back ("--ignoresize");
1656 if (flags & RPMINST_JUSTDB)
1657 opts.push_back ("--justdb");
1658 if (flags & RPMINST_TEST)
1659 opts.push_back ("--test");
1660 if (flags & RPMINST_NOPOSTTRANS)
1661 opts.push_back ("--noposttrans");
1663 opts.push_back("--");
1665 // rpm requires additional quoting of special chars:
1666 std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
1667 opts.push_back ( quotedFilename.c_str() );
1668 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1671 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1672 std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
1674 unsigned linecnt = 0;
1675 while ( systemReadLine( line ) )
1677 if ( str::startsWith( line, "%%" ) )
1680 sscanf( line.c_str() + 2, "%d", &percent );
1681 report->progress( percent );
1685 if ( linecnt < MAXRPMMESSAGELINES )
1687 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1690 rpmmsg += line+'\n';
1692 if ( str::startsWith( line, "warning:" ) )
1693 configwarnings.push_back(line);
1695 if ( linecnt >= MAXRPMMESSAGELINES )
1696 rpmmsg += "[truncated]\n";
1698 int rpm_status = systemStatus();
1701 for (std::vector<std::string>::iterator it = configwarnings.begin();
1702 it != configwarnings.end(); ++it)
1704 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1706 _("rpm saved %s as %s, but it was impossible to determine the difference"),
1708 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1709 processConfigFiles(*it, Pathname::basename(filename), " created as ",
1711 _("rpm created %s as %s, but it was impossible to determine the difference"),
1713 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1716 if ( rpm_status != 0 )
1719 str::form("%s install failed", Pathname::basename(filename).c_str()),
1720 true /*timestamp*/);
1721 std::ostringstream sstr;
1722 sstr << "rpm output:" << endl << rpmmsg << endl;
1723 historylog.comment(sstr.str());
1724 // TranslatorExplanation the colon is followed by an error message
1725 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
1727 else if ( ! rpmmsg.empty() )
1730 str::form("%s installed ok", Pathname::basename(filename).c_str()),
1731 true /*timestamp*/);
1732 std::ostringstream sstr;
1733 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1734 historylog.comment(sstr.str());
1736 // report additional rpm output in finish
1737 // TranslatorExplanation Text is followed by a ':' and the actual output.
1738 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1742 ///////////////////////////////////////////////////////////////////
1745 // METHOD NAME : RpmDb::removePackage
1746 // METHOD TYPE : PMError
1748 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
1750 // 'rpm -e' does not like epochs
1751 return removePackage( package->name()
1752 + "-" + package->edition().version()
1753 + "-" + package->edition().release()
1754 + "." + package->arch().asString(), flags );
1757 ///////////////////////////////////////////////////////////////////
1760 // METHOD NAME : RpmDb::removePackage
1761 // METHOD TYPE : PMError
1763 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
1765 callback::SendReport<RpmRemoveReport> report;
1767 report->start( name_r );
1772 doRemovePackage(name_r, flags, report);
1776 catch (RpmException & excpt_r)
1778 RpmRemoveReport::Action user = report->problem( excpt_r );
1780 if ( user == RpmRemoveReport::ABORT )
1782 report->finish( excpt_r );
1783 ZYPP_RETHROW(excpt_r);
1785 else if ( user == RpmRemoveReport::IGNORE )
1794 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
1796 FAILIFNOTINITIALIZED;
1797 HistoryLog historylog;
1799 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1802 if ( _packagebackups )
1804 // FIXME solve this status report somehow
1805 // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1806 if ( ! backupPackage( name_r ) )
1808 ERR << "backup of " << name_r << " failed" << endl;
1810 report->progress( 0 );
1814 report->progress( 100 );
1819 opts.push_back("-e");
1820 opts.push_back("--allmatches");
1822 if (flags & RPMINST_NOSCRIPTS)
1823 opts.push_back("--noscripts");
1824 if (flags & RPMINST_NODEPS)
1825 opts.push_back("--nodeps");
1826 if (flags & RPMINST_JUSTDB)
1827 opts.push_back("--justdb");
1828 if (flags & RPMINST_TEST)
1829 opts.push_back ("--test");
1830 if (flags & RPMINST_FORCE)
1832 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
1835 opts.push_back("--");
1836 opts.push_back(name_r.c_str());
1837 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
1840 std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
1842 // got no progress from command, so we fake it:
1843 // 5 - command started
1844 // 50 - command completed
1846 report->progress( 5 );
1847 unsigned linecnt = 0;
1848 while (systemReadLine(line))
1850 if ( linecnt < MAXRPMMESSAGELINES )
1852 else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
1854 rpmmsg += line+'\n';
1856 if ( linecnt >= MAXRPMMESSAGELINES )
1857 rpmmsg += "[truncated]\n";
1858 report->progress( 50 );
1859 int rpm_status = systemStatus();
1861 if ( rpm_status != 0 )
1864 str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
1865 std::ostringstream sstr;
1866 sstr << "rpm output:" << endl << rpmmsg << endl;
1867 historylog.comment(sstr.str());
1868 // TranslatorExplanation the colon is followed by an error message
1869 ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
1871 else if ( ! rpmmsg.empty() )
1874 str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
1876 std::ostringstream sstr;
1877 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
1878 historylog.comment(sstr.str());
1880 // report additional rpm output in finish
1881 // TranslatorExplanation Text is followed by a ':' and the actual output.
1882 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
1886 ///////////////////////////////////////////////////////////////////
1889 // METHOD NAME : RpmDb::backupPackage
1890 // METHOD TYPE : bool
1892 bool RpmDb::backupPackage( const Pathname & filename )
1894 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
1898 return backupPackage( h->tag_name() );
1901 ///////////////////////////////////////////////////////////////////
1904 // METHOD NAME : RpmDb::backupPackage
1905 // METHOD TYPE : bool
1907 bool RpmDb::backupPackage(const std::string& packageName)
1909 HistoryLog progresslog;
1911 Pathname backupFilename;
1912 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
1914 if (_backuppath.empty())
1916 INT << "_backuppath empty" << endl;
1922 if (!queryChangedFiles(fileList, packageName))
1924 ERR << "Error while getting changed files for package " <<
1925 packageName << endl;
1929 if (fileList.size() <= 0)
1931 DBG << "package " << packageName << " not changed -> no backup" << endl;
1935 if (filesystem::assert_dir(_root + _backuppath) != 0)
1941 // build up archive name
1942 time_t currentTime = time(0);
1943 struct tm *currentLocalTime = localtime(¤tTime);
1945 int date = (currentLocalTime->tm_year + 1900) * 10000
1946 + (currentLocalTime->tm_mon + 1) * 100
1947 + currentLocalTime->tm_mday;
1952 backupFilename = _root + _backuppath
1953 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
1956 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
1958 PathInfo pi(filestobackupfile);
1959 if (pi.isExist() && !pi.isFile())
1961 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
1965 std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
1969 ERR << "could not open " << filestobackupfile.asString() << endl;
1973 for (FileList::const_iterator cit = fileList.begin();
1974 cit != fileList.end(); ++cit)
1976 std::string name = *cit;
1977 if ( name[0] == '/' )
1979 // remove slash, file must be relative to -C parameter of tar
1980 name = name.substr( 1 );
1982 DBG << "saving file "<< name << endl;
1987 const char* const argv[] =
1992 _root.asString().c_str(),
1993 "--ignore-failed-read",
1995 backupFilename.asString().c_str(),
1997 filestobackupfile.asString().c_str(),
2001 // execute tar in inst-sys (we dont know if there is a tar below _root !)
2002 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2006 // TODO: its probably possible to start tar with -v and watch it adding
2007 // files to report progress
2008 for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2013 int ret = tar.close();
2017 ERR << "tar failed: " << tarmsg << endl;
2022 MIL << "tar backup ok" << endl;
2023 progresslog.comment(
2024 str::form(_("created backup %s"), backupFilename.asString().c_str())
2025 , /*timestamp*/true);
2028 filesystem::unlink(filestobackupfile);
2034 void RpmDb::setBackupPath(const Pathname& path)
2039 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2043 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2044 // translators: possible rpm package signature check result [brief]
2045 OUTS( CHK_OK, _("Signature is OK") );
2046 // translators: possible rpm package signature check result [brief]
2047 OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2048 // translators: possible rpm package signature check result [brief]
2049 OUTS( CHK_FAIL, _("Signature does not verify") );
2050 // translators: possible rpm package signature check result [brief]
2051 OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2052 // translators: possible rpm package signature check result [brief]
2053 OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2054 // translators: possible rpm package signature check result [brief]
2055 OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2056 // translators: possible rpm package signature check result [brief]
2057 OUTS( CHK_NOSIG, _("File is unsigned") );
2060 return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2063 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2065 for ( const auto & el : obj )
2066 str << el.second << endl;
2071 } // namespace target