00001
00002
00003
00004
00005
00006
00007
00008
00012 #include "librpm.h"
00013
00014 #include <cstdlib>
00015 #include <cstdio>
00016 #include <ctime>
00017
00018 #include <iostream>
00019 #include <fstream>
00020 #include <sstream>
00021 #include <list>
00022 #include <map>
00023 #include <set>
00024 #include <string>
00025 #include <vector>
00026 #include <algorithm>
00027
00028 #include <boost/format.hpp>
00029
00030 #include "zypp/base/Logger.h"
00031 #include "zypp/base/String.h"
00032 #include "zypp/base/Gettext.h"
00033
00034 #include "zypp/Date.h"
00035 #include "zypp/Pathname.h"
00036 #include "zypp/PathInfo.h"
00037 #include "zypp/PublicKey.h"
00038
00039 #include "zypp/target/rpm/RpmDb.h"
00040 #include "zypp/target/rpm/RpmCallbacks.h"
00041
00042 #include "zypp/HistoryLog.h"
00043 #include "zypp/target/rpm/librpmDb.h"
00044 #include "zypp/target/rpm/RpmException.h"
00045 #include "zypp/TmpPath.h"
00046 #include "zypp/KeyRing.h"
00047 #include "zypp/ZYppFactory.h"
00048 #include "zypp/ZConfig.h"
00049
00050 using namespace std;
00051 using namespace zypp::filesystem;
00052
00053 #define WARNINGMAILPATH "/var/log/YaST2/"
00054 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
00055 #define MAXRPMMESSAGELINES 10000
00056
00057 namespace zypp
00058 {
00059 namespace zypp_readonly_hack
00060 {
00061 bool IGotIt();
00062 }
00063 namespace target
00064 {
00065 namespace rpm
00066 {
00067 namespace
00068 {
00069 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
00070 const char* quoteInFilename_m = "\'\"";
00071 #else
00072 const char* quoteInFilename_m = " \t\'\"";
00073 #endif
00074 inline string rpmQuoteFilename( const Pathname & path_r )
00075 {
00076 string path( path_r.asString() );
00077 for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
00078 pos != string::npos;
00079 pos = path.find_first_of( quoteInFilename_m, pos ) )
00080 {
00081 path.insert( pos, "\\" );
00082 pos += 2;
00083 }
00084 return path;
00085 }
00086 }
00087
00088 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
00089 {
00090 KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
00091 {
00092 connect();
00093 }
00094
00095 ~KeyRingSignalReceiver()
00096 {
00097 disconnect();
00098 }
00099
00100 virtual void trustedKeyAdded( const PublicKey &key )
00101 {
00102 MIL << "trusted key added to zypp Keyring. Importing" << endl;
00103
00104 try
00105 {
00106 _rpmdb.importPubkey( key );
00107 }
00108 catch (RpmException &e)
00109 {
00110 ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
00111 }
00112 }
00113
00114 virtual void trustedKeyRemoved( const PublicKey &key )
00115 {
00116 MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
00117
00118
00119 try
00120 {
00121 _rpmdb.removePubkey( key );
00122 }
00123 catch (RpmException &e)
00124 {
00125 ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
00126 }
00127 }
00128
00129 RpmDb &_rpmdb;
00130 };
00131
00132 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
00133
00134 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
00135 {
00136 const char* argv[] =
00137 {
00138 "diff",
00139 "-u",
00140 file1.c_str(),
00141 file2.c_str(),
00142 NULL
00143 };
00144 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00145
00146
00147
00148
00149 string line;
00150 int count = 0;
00151 for (line = prog.receiveLine(), count=0;
00152 !line.empty();
00153 line = prog.receiveLine(), count++ )
00154 {
00155 if (maxlines<0?true:count<maxlines)
00156 out+=line;
00157 }
00158
00159 return prog.close();
00160 }
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
00171 {
00172 return librpmDb::stringPath( root_r, sub_r );
00173 }
00174
00175
00176
00177
00178
00179
00180
00181 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
00182 {
00183 if ( obj == RpmDb::DbSI_NO_INIT )
00184 {
00185 str << "NO_INIT";
00186 }
00187 else
00188 {
00189 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
00190 str << "V4(";
00191 ENUM_OUT( DbSI_HAVE_V4, 'X' );
00192 ENUM_OUT( DbSI_MADE_V4, 'c' );
00193 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00194 str << ")V3(";
00195 ENUM_OUT( DbSI_HAVE_V3, 'X' );
00196 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00197 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00198 str << ")";
00199 #undef ENUM_OUT
00200 }
00201 return str;
00202 }
00203
00204
00205
00207
00208
00209
00211
00212 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
00213
00215
00217
00218
00219
00220
00221
00222 RpmDb::RpmDb()
00223 : _dbStateInfo( DbSI_NO_INIT )
00224 #warning Check for obsolete memebers
00225 , _backuppath ("/var/adm/backup")
00226 , _packagebackups(false)
00227 , _warndirexists(false)
00228 {
00229 process = 0;
00230 exit_code = -1;
00231 librpmDb::globalInit();
00232
00233
00234 setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
00235 sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
00236 }
00237
00239
00240
00241
00242
00243
00244 RpmDb::~RpmDb()
00245 {
00246 MIL << "~RpmDb()" << endl;
00247 closeDatabase();
00248 delete process;
00249 MIL << "~RpmDb() end" << endl;
00250 sKeyRingReceiver.reset();
00251 }
00252
00253 Date RpmDb::timestamp() const
00254 {
00255 Date ts_rpm;
00256
00257 Pathname db_path;
00258 if ( dbPath().empty() )
00259 db_path = "/var/lib/rpm";
00260 else
00261 db_path = dbPath();
00262
00263 PathInfo rpmdb_info(root() + db_path + "/Packages");
00264
00265 if ( rpmdb_info.isExist() )
00266 return rpmdb_info.mtime();
00267 else
00268 return Date::now();
00269 }
00271
00272
00273
00274
00275
00276 ostream & RpmDb::dumpOn( ostream & str ) const
00277 {
00278 str << "RpmDb[";
00279
00280 if ( _dbStateInfo == DbSI_NO_INIT )
00281 {
00282 str << "NO_INIT";
00283 }
00284 else
00285 {
00286 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
00287 str << "V4(";
00288 ENUM_OUT( DbSI_HAVE_V4, 'X' );
00289 ENUM_OUT( DbSI_MADE_V4, 'c' );
00290 ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00291 str << ")V3(";
00292 ENUM_OUT( DbSI_HAVE_V3, 'X' );
00293 ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00294 ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00295 str << "): " << stringPath( _root, _dbPath );
00296 #undef ENUM_OUT
00297 }
00298 return str << "]";
00299 }
00300
00302
00303
00304
00305
00306
00307 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
00308 {
00310
00312 bool quickinit( root_r.empty() );
00313
00314 if ( root_r.empty() )
00315 root_r = "/";
00316
00317 if ( dbPath_r.empty() )
00318 dbPath_r = "/var/lib/rpm";
00319
00320 if ( ! (root_r.absolute() && dbPath_r.absolute()) )
00321 {
00322 ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
00323 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00324 }
00325
00326 MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
00327 << ( doRebuild_r ? " (rebuilddb)" : "" )
00328 << ( quickinit ? " (quickinit)" : "" ) << endl;
00329
00331
00333 if ( initialized() )
00334 {
00335 if ( root_r == _root && dbPath_r == _dbPath )
00336 {
00337 return;
00338 }
00339 else
00340 {
00341 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
00342 }
00343 }
00344
00346
00348 librpmDb::unblockAccess();
00349
00350 if ( quickinit )
00351 {
00352 MIL << "QUICK initDatabase (no systemRoot set)" << endl;
00353 return;
00354 }
00355
00356 DbStateInfoBits info = DbSI_NO_INIT;
00357 try
00358 {
00359 internal_initDatabase( root_r, dbPath_r, info );
00360 }
00361 catch (const RpmException & excpt_r)
00362 {
00363 ZYPP_CAUGHT(excpt_r);
00364 librpmDb::blockAccess();
00365 ERR << "Cleanup on error: state " << info << endl;
00366
00367 if ( dbsi_has( info, DbSI_MADE_V4 ) )
00368 {
00369
00370
00371 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00372 }
00373 ZYPP_RETHROW(excpt_r);
00374 }
00375 if ( dbsi_has( info, DbSI_HAVE_V3 ) )
00376 {
00377 if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
00378 {
00379
00380 MIL << "Cleanup: state " << info << endl;
00381 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00382 dbsi_clr( info, DbSI_HAVE_V3 );
00383 }
00384 else
00385 {
00386
00387
00388
00389 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
00390 }
00391 }
00392 #warning CHECK: notify root about conversion backup.
00393
00394 _root = root_r;
00395 _dbPath = dbPath_r;
00396 _dbStateInfo = info;
00397
00398 if ( doRebuild_r )
00399 {
00400 if ( dbsi_has( info, DbSI_HAVE_V4 )
00401 && ! dbsi_has( info, DbSI_MADE_V4 ) )
00402 {
00403 rebuildDatabase();
00404 }
00405 }
00406
00407 MIL << "Syncronizing keys with zypp keyring" << endl;
00408 syncTrustedKeys();
00409
00410
00411
00412
00413
00414 librpmDb::dbRelease( true );
00415
00416 MIL << "InitDatabase: " << *this << endl;
00417 }
00418
00420
00421
00422
00423
00424
00425 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
00426 DbStateInfoBits & info_r )
00427 {
00428 info_r = DbSI_NO_INIT;
00429
00431
00433 librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
00434
00435 if ( dbInfo.illegalArgs() )
00436 {
00437
00438 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00439 }
00440 if ( ! dbInfo.usableArgs() )
00441 {
00442 ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
00443 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00444 }
00445
00446 if ( dbInfo.hasDbV4() )
00447 {
00448 dbsi_set( info_r, DbSI_HAVE_V4 );
00449 MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
00450 }
00451 else
00452 {
00453 MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
00454 }
00455
00456 if ( dbInfo.hasDbV3() )
00457 {
00458 dbsi_set( info_r, DbSI_HAVE_V3 );
00459 }
00460 if ( dbInfo.hasDbV3ToV4() )
00461 {
00462 dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
00463 }
00464
00465 DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00466 librpmDb::dumpState( DBG ) << endl;
00467
00469
00471
00472
00473 librpmDb::dbAccess( root_r, dbPath_r );
00474
00475 if ( ! dbInfo.hasDbV4() )
00476 {
00477 dbInfo.restat();
00478 if ( dbInfo.hasDbV4() )
00479 {
00480 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
00481 }
00482 }
00483
00484 DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00485 librpmDb::dumpState( DBG ) << endl;
00486
00488
00489
00491 librpmDb::constPtr dbptr;
00492 librpmDb::dbAccess( dbptr );
00493 bool dbEmpty = dbptr->empty();
00494 if ( dbEmpty )
00495 {
00496 MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
00497 }
00498
00499 if ( dbInfo.hasDbV3() )
00500 {
00501 MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
00502
00503 if ( dbEmpty )
00504 {
00505 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
00506 convertV3toV4( dbInfo.dbV3().path(), dbptr );
00507
00508
00509 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
00510 if ( res )
00511 {
00512 WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
00513 }
00514 else
00515 {
00516 dbInfo.restat();
00517 if ( dbInfo.hasDbV3ToV4() )
00518 {
00519 MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
00520 dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
00521 }
00522 }
00523
00524 }
00525 else
00526 {
00527
00528 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
00529
00530 dbsi_set( info_r, DbSI_MODIFIED_V4 );
00531
00532 }
00533
00534 DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00535 librpmDb::dumpState( DBG ) << endl;
00536 }
00537
00538 if ( dbInfo.hasDbV3ToV4() )
00539 {
00540 MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
00541 }
00542 }
00543
00545
00546
00547
00548
00549
00550 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
00551 {
00552 const char * v3backup = "packages.rpm3";
00553 const char * master = "Packages";
00554 const char * index[] =
00555 {
00556 "Basenames",
00557 "Conflictname",
00558 "Depends",
00559 "Dirnames",
00560 "Filemd5s",
00561 "Group",
00562 "Installtid",
00563 "Name",
00564 "Providename",
00565 "Provideversion",
00566 "Pubkeys",
00567 "Requirename",
00568 "Requireversion",
00569 "Sha1header",
00570 "Sigmd5",
00571 "Triggername",
00572
00573 NULL
00574 };
00575
00576 PathInfo pi( dbdir_r );
00577 if ( ! pi.isDir() )
00578 {
00579 ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
00580 return;
00581 }
00582
00583 for ( const char ** f = index; *f; ++f )
00584 {
00585 pi( dbdir_r + *f );
00586 if ( pi.isFile() )
00587 {
00588 filesystem::unlink( pi.path() );
00589 }
00590 }
00591
00592 pi( dbdir_r + master );
00593 if ( pi.isFile() )
00594 {
00595 MIL << "Removing rpm4 database " << pi << endl;
00596 filesystem::unlink( pi.path() );
00597 }
00598
00599 if ( v3backup_r )
00600 {
00601 pi( dbdir_r + v3backup );
00602 if ( pi.isFile() )
00603 {
00604 MIL << "Removing converted rpm3 database backup " << pi << endl;
00605 filesystem::unlink( pi.path() );
00606 }
00607 }
00608 }
00609
00611
00612
00613
00614
00615
00616 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
00617 {
00618 const char * master = "packages.rpm";
00619 const char * index[] =
00620 {
00621 "conflictsindex.rpm",
00622 "fileindex.rpm",
00623 "groupindex.rpm",
00624 "nameindex.rpm",
00625 "providesindex.rpm",
00626 "requiredby.rpm",
00627 "triggerindex.rpm",
00628
00629 NULL
00630 };
00631
00632 PathInfo pi( dbdir_r );
00633 if ( ! pi.isDir() )
00634 {
00635 ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
00636 return;
00637 }
00638
00639 for ( const char ** f = index; *f; ++f )
00640 {
00641 pi( dbdir_r + *f );
00642 if ( pi.isFile() )
00643 {
00644 filesystem::unlink( pi.path() );
00645 }
00646 }
00647
00648 #warning CHECK: compare vs existing v3 backup. notify root
00649 pi( dbdir_r + master );
00650 if ( pi.isFile() )
00651 {
00652 Pathname m( pi.path() );
00653 if ( v3backup_r )
00654 {
00655
00656 filesystem::unlink( m );
00657 Pathname b( m.extend( "3" ) );
00658 pi( b );
00659 }
00660 else
00661 {
00662 Pathname b( m.extend( ".deleted" ) );
00663 pi( b );
00664 if ( pi.isFile() )
00665 {
00666
00667 filesystem::unlink( b );
00668 }
00669 filesystem::rename( m, b );
00670 pi( b );
00671 }
00672 MIL << "(Re)moved rpm3 database to " << pi << endl;
00673 }
00674 }
00675
00677
00678
00679
00680
00681
00682 void RpmDb::modifyDatabase()
00683 {
00684 if ( ! initialized() )
00685 return;
00686
00687
00688 dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
00689
00690
00691 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
00692 {
00693 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
00694 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
00695 dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
00696 }
00697 }
00698
00700
00701
00702
00703
00704
00705 void RpmDb::closeDatabase()
00706 {
00707 if ( ! initialized() )
00708 {
00709 return;
00710 }
00711
00712 MIL << "Calling closeDatabase: " << *this << endl;
00713
00715
00717 librpmDb::blockAccess();
00718
00720
00722 if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
00723 {
00724 MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
00725 if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
00726 {
00727
00728 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
00729 }
00730 else
00731 {
00732
00733 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
00734 }
00735 }
00736
00738
00740 _root = _dbPath = Pathname();
00741 _dbStateInfo = DbSI_NO_INIT;
00742
00743 MIL << "closeDatabase: " << *this << endl;
00744 }
00745
00747
00748
00749
00750
00751
00752 void RpmDb::rebuildDatabase()
00753 {
00754 callback::SendReport<RebuildDBReport> report;
00755
00756 report->start( root() + dbPath() );
00757
00758 try
00759 {
00760 doRebuildDatabase(report);
00761 }
00762 catch (RpmException & excpt_r)
00763 {
00764 report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
00765 ZYPP_RETHROW(excpt_r);
00766 }
00767 report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
00768 }
00769
00770 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
00771 {
00772 FAILIFNOTINITIALIZED;
00773
00774 MIL << "RpmDb::rebuildDatabase" << *this << endl;
00775
00776
00777 PathInfo dbMaster( root() + dbPath() + "Packages" );
00778 PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
00779
00780
00781 RpmArgVec opts;
00782 opts.push_back("--rebuilddb");
00783 opts.push_back("-vv");
00784
00785
00786
00787 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
00788
00789
00790 PathInfo newMaster( root()
00791 + dbPath().extend( str::form( "rebuilddb.%d",
00792 process?process->getpid():0) )
00793 + "Packages" );
00794
00795 string line;
00796 string errmsg;
00797
00798 while ( systemReadLine( line ) )
00799 {
00800 if ( newMaster() )
00801 {
00802
00803 if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
00804 {
00805 WAR << "User requested abort." << endl;
00806 systemKill();
00807 filesystem::recursive_rmdir( newMaster.path().dirname() );
00808 }
00809 }
00810
00811 if ( line.compare( 0, 2, "D:" ) )
00812 {
00813 errmsg += line + '\n';
00814
00815 WAR << line << endl;
00816 }
00817 }
00818
00819 int rpm_status = systemStatus();
00820
00821 if ( rpm_status != 0 )
00822 {
00823
00824 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
00825 (errmsg.empty() ? error_message: errmsg))));
00826 }
00827 else
00828 {
00829 report->progress( 100, root() + dbPath() );
00830 }
00831 }
00832
00834 namespace
00835 {
00837
00838 struct Key
00839 {
00840 Key()
00841 : _inRpmKeys( nullptr )
00842 , _inZyppKeys( nullptr )
00843 {}
00844
00845 void updateIf( const Edition & rpmKey_r )
00846 {
00847 std::string keyRelease( rpmKey_r.release() );
00848 int comp = _release.compare( keyRelease );
00849 if ( comp < 0 )
00850 {
00851
00852 _release.swap( keyRelease );
00853 _inRpmKeys = &rpmKey_r;
00854 _inZyppKeys = nullptr;
00855 if ( !keyRelease.empty() )
00856 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
00857 }
00858 else if ( comp == 0 )
00859 {
00860
00861 if ( ! _inRpmKeys )
00862 _inRpmKeys = &rpmKey_r;
00863 }
00864
00865 else
00866 DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
00867 }
00868
00869 void updateIf( const PublicKeyData & zyppKey_r )
00870 {
00871 std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
00872 int comp = _release.compare( keyRelease );
00873 if ( comp < 0 )
00874 {
00875
00876 _release.swap( keyRelease );
00877 _inRpmKeys = nullptr;
00878 _inZyppKeys = &zyppKey_r;
00879 if ( !keyRelease.empty() )
00880 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
00881 }
00882 else if ( comp == 0 )
00883 {
00884
00885 if ( ! _inZyppKeys )
00886 _inZyppKeys = &zyppKey_r;
00887 }
00888
00889 else
00890 DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
00891 }
00892
00893 std::string _release;
00894 const Edition * _inRpmKeys;
00895 const PublicKeyData * _inZyppKeys;
00896 };
00898
00903 void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
00904 {
00905
00906
00907 std::map<std::string,Key> _keymap;
00908
00909 for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
00910 {
00911 _keymap[(*it).version()].updateIf( *it );
00912 }
00913
00914 for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
00915 {
00916 _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
00917 }
00918
00919
00920 std::set<Edition> rpmKeys;
00921 std::list<PublicKeyData> zyppKeys;
00922 for_( it, _keymap.begin(), _keymap.end() )
00923 {
00924 DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
00925 << ( (*it).second._inRpmKeys ? "R" : "_" )
00926 << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
00927 if ( ! (*it).second._inRpmKeys )
00928 {
00929 zyppKeys.push_back( *(*it).second._inZyppKeys );
00930 }
00931 if ( ! (*it).second._inZyppKeys )
00932 {
00933 rpmKeys.insert( *(*it).second._inRpmKeys );
00934 }
00935 }
00936 rpmKeys_r.swap( rpmKeys );
00937 zyppKeys_r.swap( zyppKeys );
00938 }
00939 }
00941
00942 void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
00943 {
00944 MIL << "Going to sync trusted keys..." << endl;
00945 std::set<Edition> rpmKeys( pubkeyEditions() );
00946 std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
00947 computeKeyRingSync( rpmKeys, zyppKeys );
00948 MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
00949 MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
00950
00952 if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
00953 {
00954
00955 MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
00956
00957 callback::TempConnect<KeyRingSignals> tempDisconnect;
00958 librpmDb::db_const_iterator keepDbOpen;
00959
00960 TmpFile tmpfile( getZYpp()->tmpPath() );
00961 {
00962 ofstream tmpos( tmpfile.path().c_str() );
00963 for_( it, rpmKeys.begin(), rpmKeys.end() )
00964 {
00965
00966 RpmHeader::constPtr result;
00967 getData( string("gpg-pubkey"), *it, result );
00968 tmpos << result->tag_description() << endl;
00969 }
00970 }
00971 try
00972 {
00973 getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true );
00974 }
00975 catch (Exception &e)
00976 {
00977 ERR << "Could not import keys into in zypp keyring" << endl;
00978 }
00979 }
00980
00982 if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
00983 {
00984
00985 MIL << "Importing zypp trusted keyring" << std::endl;
00986 for_( it, zyppKeys.begin(), zyppKeys.end() )
00987 {
00988 try
00989 {
00990 importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
00991 }
00992 catch ( const RpmException & exp )
00993 {
00994 ZYPP_CAUGHT( exp );
00995 }
00996 }
00997 }
00998 MIL << "Trusted keys synced." << endl;
00999 }
01000
01001 void RpmDb::importZyppKeyRingTrustedKeys()
01002 { syncTrustedKeys( SYNC_FROM_KEYRING ); }
01003
01004 void RpmDb::exportTrustedKeysInZyppKeyRing()
01005 { syncTrustedKeys( SYNC_TO_KEYRING ); }
01006
01008
01009
01010
01011
01012
01013 void RpmDb::importPubkey( const PublicKey & pubkey_r )
01014 {
01015 FAILIFNOTINITIALIZED;
01016
01017
01018 if ( zypp_readonly_hack::IGotIt() )
01019 {
01020 WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
01021 return;
01022 }
01023
01024
01025 Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
01026 set<Edition> rpmKeys = pubkeyEditions();
01027 bool hasOldkeys = false;
01028
01029 for_( it, rpmKeys.begin(), rpmKeys.end() )
01030 {
01031 if ( keyEd == *it )
01032 {
01033 MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
01034 return;
01035 }
01036
01037 if ( keyEd.version() != (*it).version() )
01038 continue;
01039
01040 if ( keyEd.release() < (*it).release() )
01041 {
01042 MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
01043 return;
01044 }
01045 else
01046 {
01047 hasOldkeys = true;
01048 }
01049 }
01050 MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
01051
01052 if ( hasOldkeys )
01053 {
01054
01055
01056 std::string keyName( "gpg-pubkey-" + keyEd.version() );
01057 RpmArgVec opts;
01058 opts.push_back ( "-e" );
01059 opts.push_back ( "--allmatches" );
01060 opts.push_back ( "--" );
01061 opts.push_back ( keyName.c_str() );
01062
01063
01064 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01065
01066 string line;
01067 while ( systemReadLine( line ) )
01068 {
01069 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
01070 }
01071
01072 if ( systemStatus() != 0 )
01073 {
01074 ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
01075 }
01076 else
01077 {
01078 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
01079 }
01080 }
01081
01082
01083 RpmArgVec opts;
01084 opts.push_back ( "--import" );
01085 opts.push_back ( "--" );
01086 opts.push_back ( pubkey_r.path().asString().c_str() );
01087
01088
01089
01090 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01091
01092 string line;
01093 while ( systemReadLine( line ) )
01094 {
01095 ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
01096 }
01097
01098 if ( systemStatus() != 0 )
01099 {
01100
01101 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
01102 _("Failed to import public key from file %s: %s"))
01103 % pubkey_r.asString() % error_message)));
01104 }
01105 else
01106 {
01107 MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
01108 }
01109 }
01110
01112
01113
01114
01115
01116
01117 void RpmDb::removePubkey( const PublicKey & pubkey_r )
01118 {
01119 FAILIFNOTINITIALIZED;
01120
01121
01122
01123 set<Edition> rpm_keys = pubkeyEditions();
01124 set<Edition>::const_iterator found_edition = rpm_keys.end();
01125 std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
01126
01127 for_( it, rpm_keys.begin(), rpm_keys.end() )
01128 {
01129 if ( (*it).version() == pubkeyVersion )
01130 {
01131 found_edition = it;
01132 break;
01133 }
01134 }
01135
01136
01137 if (found_edition == rpm_keys.end())
01138 {
01139 WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
01140 return;
01141 }
01142
01143 string rpm_name("gpg-pubkey-" + found_edition->asString());
01144
01145 RpmArgVec opts;
01146 opts.push_back ( "-e" );
01147 opts.push_back ( "--" );
01148 opts.push_back ( rpm_name.c_str() );
01149
01150
01151
01152 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01153
01154 string line;
01155 while ( systemReadLine( line ) )
01156 {
01157 if ( line.substr( 0, 6 ) == "error:" )
01158 {
01159 WAR << line << endl;
01160 }
01161 else
01162 {
01163 DBG << line << endl;
01164 }
01165 }
01166
01167 int rpm_status = systemStatus();
01168
01169 if ( rpm_status != 0 )
01170 {
01171
01172 ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
01173 _("Failed to remove public key %s: %s")) % pubkey_r.asString()
01174 % error_message)));
01175 }
01176 else
01177 {
01178 MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
01179 }
01180 }
01181
01183
01184
01185
01186
01187
01188 list<PublicKey> RpmDb::pubkeys() const
01189 {
01190 list<PublicKey> ret;
01191
01192 librpmDb::db_const_iterator it;
01193 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01194 {
01195 Edition edition = it->tag_edition();
01196 if (edition != Edition::noedition)
01197 {
01198
01199 RpmHeader::constPtr result;
01200 getData( string("gpg-pubkey"), edition, result );
01201 TmpFile file(getZYpp()->tmpPath());
01202 ofstream os;
01203 try
01204 {
01205 os.open(file.path().asString().c_str());
01206
01207 os << result->tag_description();
01208
01209
01210
01211 os.close();
01212
01213 PublicKey key(file);
01214 ret.push_back(key);
01215 }
01216 catch (exception &e)
01217 {
01218 ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
01219
01220 }
01221 }
01222 }
01223 return ret;
01224 }
01225
01226 set<Edition> RpmDb::pubkeyEditions() const
01227 {
01228 set<Edition> ret;
01229
01230 librpmDb::db_const_iterator it;
01231 for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01232 {
01233 Edition edition = it->tag_edition();
01234 if (edition != Edition::noedition)
01235 ret.insert( edition );
01236 }
01237 return ret;
01238 }
01239
01240
01242
01243
01244
01245
01246
01247
01248
01249 list<FileInfo>
01250 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
01251 {
01252 list<FileInfo> result;
01253
01254 librpmDb::db_const_iterator it;
01255 bool found;
01256 if (edition_r == Edition::noedition)
01257 {
01258 found = it.findPackage( name_r );
01259 }
01260 else
01261 {
01262 found = it.findPackage( name_r, edition_r );
01263 }
01264 if (!found)
01265 return result;
01266
01267 return result;
01268 }
01269
01270
01272
01273
01274
01275
01276
01277
01278
01279 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
01280 {
01281 librpmDb::db_const_iterator it;
01282 bool res;
01283 do
01284 {
01285 res = it.findByFile( file_r );
01286 if (!res) break;
01287 if (!name_r.empty())
01288 {
01289 res = (it->tag_name() == name_r);
01290 }
01291 ++it;
01292 }
01293 while (res && *it);
01294 return res;
01295 }
01296
01298
01299
01300
01301
01302
01303
01304
01305 string RpmDb::whoOwnsFile( const string & file_r) const
01306 {
01307 librpmDb::db_const_iterator it;
01308 if (it.findByFile( file_r ))
01309 {
01310 return it->tag_name();
01311 }
01312 return "";
01313 }
01314
01316
01317
01318
01319
01320
01321
01322
01323 bool RpmDb::hasProvides( const string & tag_r ) const
01324 {
01325 librpmDb::db_const_iterator it;
01326 return it.findByProvides( tag_r );
01327 }
01328
01330
01331
01332
01333
01334
01335
01336
01337 bool RpmDb::hasRequiredBy( const string & tag_r ) const
01338 {
01339 librpmDb::db_const_iterator it;
01340 return it.findByRequiredBy( tag_r );
01341 }
01342
01344
01345
01346
01347
01348
01349
01350
01351 bool RpmDb::hasConflicts( const string & tag_r ) const
01352 {
01353 librpmDb::db_const_iterator it;
01354 return it.findByConflicts( tag_r );
01355 }
01356
01358
01359
01360
01361
01362
01363
01364
01365 bool RpmDb::hasPackage( const string & name_r ) const
01366 {
01367 librpmDb::db_const_iterator it;
01368 return it.findPackage( name_r );
01369 }
01370
01372
01373
01374
01375
01376
01377
01378
01379 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
01380 {
01381 librpmDb::db_const_iterator it;
01382 return it.findPackage( name_r, ed_r );
01383 }
01384
01386
01387
01388
01389
01390
01391
01392
01393 void RpmDb::getData( const string & name_r,
01394 RpmHeader::constPtr & result_r ) const
01395 {
01396 librpmDb::db_const_iterator it;
01397 it.findPackage( name_r );
01398 result_r = *it;
01399 if (it.dbError())
01400 ZYPP_THROW(*(it.dbError()));
01401 }
01402
01404
01405
01406
01407
01408
01409
01410
01411 void RpmDb::getData( const string & name_r, const Edition & ed_r,
01412 RpmHeader::constPtr & result_r ) const
01413 {
01414 librpmDb::db_const_iterator it;
01415 it.findPackage( name_r, ed_r );
01416 result_r = *it;
01417 if (it.dbError())
01418 ZYPP_THROW(*(it.dbError()));
01419 }
01420
01422
01423
01424
01425
01426 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
01427 {
01428 PathInfo file( path_r );
01429 if ( ! file.isFile() )
01430 {
01431 ERR << "Not a file: " << file << endl;
01432 return CHK_ERROR;
01433 }
01434
01435 FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
01436 if ( fd == 0 || ::Ferror(fd) )
01437 {
01438 ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
01439 if ( fd )
01440 ::Fclose( fd );
01441 return CHK_ERROR;
01442 }
01443
01444 rpmts ts = ::rpmtsCreate();
01445 ::rpmtsSetRootDir( ts, root().asString().c_str() );
01446 ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
01447 int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
01448 ts = rpmtsFree(ts);
01449
01450 ::Fclose( fd );
01451
01452 switch ( res )
01453 {
01454 case RPMRC_OK:
01455 return CHK_OK;
01456 break;
01457 case RPMRC_NOTFOUND:
01458 WAR << "Signature is unknown type. " << file << endl;
01459 return CHK_NOTFOUND;
01460 break;
01461 case RPMRC_FAIL:
01462 WAR << "Signature does not verify. " << file << endl;
01463 return CHK_FAIL;
01464 break;
01465 case RPMRC_NOTTRUSTED:
01466 WAR << "Signature is OK, but key is not trusted. " << file << endl;
01467 return CHK_NOTTRUSTED;
01468 break;
01469 case RPMRC_NOKEY:
01470 WAR << "Public key is unavailable. " << file << endl;
01471 return CHK_NOKEY;
01472 break;
01473 }
01474 ERR << "Error reading header." << file << endl;
01475 return CHK_ERROR;
01476 }
01477
01478
01479 bool
01480 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
01481 {
01482 bool ok = true;
01483
01484 fileList.clear();
01485
01486 if ( ! initialized() ) return false;
01487
01488 RpmArgVec opts;
01489
01490 opts.push_back ("-V");
01491 opts.push_back ("--nodeps");
01492 opts.push_back ("--noscripts");
01493 opts.push_back ("--nomd5");
01494 opts.push_back ("--");
01495 opts.push_back (packageName.c_str());
01496
01497 run_rpm (opts, ExternalProgram::Discard_Stderr);
01498
01499 if ( process == NULL )
01500 return false;
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513 string line;
01514 while (systemReadLine(line))
01515 {
01516 if (line.length() > 12 &&
01517 (line[0] == 'S' || line[0] == 's' ||
01518 (line[0] == '.' && line[7] == 'T')))
01519 {
01520
01521 string filename;
01522
01523 filename.assign(line, 11, line.length() - 11);
01524 fileList.insert(filename);
01525 }
01526 }
01527
01528 systemStatus();
01529
01530
01531
01532 return ok;
01533 }
01534
01535
01536
01537
01538
01539
01540
01541
01542
01543
01544
01545 void
01546 RpmDb::run_rpm (const RpmArgVec& opts,
01547 ExternalProgram::Stderr_Disposition disp)
01548 {
01549 if ( process )
01550 {
01551 delete process;
01552 process = NULL;
01553 }
01554 exit_code = -1;
01555
01556 if ( ! initialized() )
01557 {
01558 ZYPP_THROW(RpmDbNotOpenException());
01559 }
01560
01561 RpmArgVec args;
01562
01563
01564 args.push_back("rpm");
01565 args.push_back("--root");
01566 args.push_back(_root.asString().c_str());
01567 args.push_back("--dbpath");
01568 args.push_back(_dbPath.asString().c_str());
01569
01570 const char* argv[args.size() + opts.size() + 1];
01571
01572 const char** p = argv;
01573 p = copy (args.begin (), args.end (), p);
01574 p = copy (opts.begin (), opts.end (), p);
01575 *p = 0;
01576
01577
01578
01579 librpmDb::dbRelease( true );
01580
01581
01582 process = new ExternalProgram(argv, disp, false, -1, true);
01583 return;
01584 }
01585
01586
01587
01588
01589 bool RpmDb::systemReadLine( string & line )
01590 {
01591 line.erase();
01592
01593 if ( process == NULL )
01594 return false;
01595
01596 if ( process->inputFile() )
01597 {
01598 process->setBlocking( false );
01599 FILE * inputfile = process->inputFile();
01600 int inputfileFd = ::fileno( inputfile );
01601 do
01602 {
01603
01604 fd_set rfds;
01605 FD_ZERO( &rfds );
01606 FD_SET( inputfileFd, &rfds );
01607
01608
01609 struct timeval tv;
01610 tv.tv_sec = 5;
01611 tv.tv_usec = 0;
01612
01613 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
01614
01615 if ( retval == -1 )
01616 {
01617 ERR << "select error: " << strerror(errno) << endl;
01618 if ( errno != EINTR )
01619 return false;
01620 }
01621 else if ( retval )
01622 {
01623
01624 static size_t linebuffer_size = 0;
01625 static char * linebuffer = 0;
01626 ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
01627 if ( nread == -1 )
01628 {
01629 if ( ::feof( inputfile ) )
01630 return line.size();
01631 }
01632 else
01633 {
01634 if ( nread > 0 )
01635 {
01636 if ( linebuffer[nread-1] == '\n' )
01637 --nread;
01638 line += string( linebuffer, nread );
01639 }
01640
01641 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
01642 return true;
01643 }
01644 clearerr( inputfile );
01645 }
01646 else
01647 {
01648
01649 if ( ! process->running() )
01650 return false;
01651 }
01652 } while ( true );
01653 }
01654
01655 return false;
01656 }
01657
01658
01659
01660
01661
01662 int
01663 RpmDb::systemStatus()
01664 {
01665 if ( process == NULL )
01666 return -1;
01667
01668 exit_code = process->close();
01669 if (exit_code == 0)
01670 error_message = "";
01671 else
01672 error_message = process->execError();
01673 process->kill();
01674 delete process;
01675 process = 0;
01676
01677
01678
01679 return exit_code;
01680 }
01681
01682
01683
01684
01685 void
01686 RpmDb::systemKill()
01687 {
01688 if (process) process->kill();
01689 }
01690
01691
01692
01693 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
01694 {
01695 string msg = line.substr(9);
01696 string::size_type pos1 = string::npos;
01697 string::size_type pos2 = string::npos;
01698 string file1s, file2s;
01699 Pathname file1;
01700 Pathname file2;
01701
01702 pos1 = msg.find (typemsg);
01703 for (;;)
01704 {
01705 if ( pos1 == string::npos )
01706 break;
01707
01708 pos2 = pos1 + strlen (typemsg);
01709
01710 if (pos2 >= msg.length() )
01711 break;
01712
01713 file1 = msg.substr (0, pos1);
01714 file2 = msg.substr (pos2);
01715
01716 file1s = file1.asString();
01717 file2s = file2.asString();
01718
01719 if (!_root.empty() && _root != "/")
01720 {
01721 file1 = _root + file1;
01722 file2 = _root + file2;
01723 }
01724
01725 string out;
01726 int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
01727 if (ret)
01728 {
01729 Pathname file = _root + WARNINGMAILPATH;
01730 if (filesystem::assert_dir(file) != 0)
01731 {
01732 ERR << "Could not create " << file.asString() << endl;
01733 break;
01734 }
01735 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
01736 ofstream notify(file.asString().c_str(), ios::out|ios::app);
01737 if (!notify)
01738 {
01739 ERR << "Could not open " << file << endl;
01740 break;
01741 }
01742
01743
01744
01745 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
01746 if (ret>1)
01747 {
01748 ERR << "diff failed" << endl;
01749 notify << str::form(difffailmsg,
01750 file1s.c_str(), file2s.c_str()) << endl;
01751 }
01752 else
01753 {
01754 notify << str::form(diffgenmsg,
01755 file1s.c_str(), file2s.c_str()) << endl;
01756
01757
01758 if (!_root.empty() && _root != "/")
01759 {
01760 if (out.substr(0,4) == "--- ")
01761 {
01762 out.replace(4, file1.asString().length(), file1s);
01763 }
01764 string::size_type pos = out.find("\n+++ ");
01765 if (pos != string::npos)
01766 {
01767 out.replace(pos+5, file2.asString().length(), file2s);
01768 }
01769 }
01770 notify << out << endl;
01771 }
01772 notify.close();
01773 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
01774 notify.close();
01775 }
01776 else
01777 {
01778 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
01779 }
01780 break;
01781 }
01782 }
01783
01785
01786
01787
01788
01789
01790 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
01791 {
01792 callback::SendReport<RpmInstallReport> report;
01793
01794 report->start(filename);
01795
01796 do
01797 try
01798 {
01799 doInstallPackage(filename, flags, report);
01800 report->finish();
01801 break;
01802 }
01803 catch (RpmException & excpt_r)
01804 {
01805 RpmInstallReport::Action user = report->problem( excpt_r );
01806
01807 if ( user == RpmInstallReport::ABORT )
01808 {
01809 report->finish( excpt_r );
01810 ZYPP_RETHROW(excpt_r);
01811 }
01812 else if ( user == RpmInstallReport::IGNORE )
01813 {
01814 break;
01815 }
01816 }
01817 while (true);
01818 }
01819
01820 void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
01821 {
01822 FAILIFNOTINITIALIZED;
01823 HistoryLog historylog;
01824
01825 MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
01826
01827
01828
01829 if ( _packagebackups )
01830 {
01831
01832 if ( ! backupPackage( filename ) )
01833 {
01834 ERR << "backup of " << filename.asString() << " failed" << endl;
01835 }
01836
01837 report->progress( 0 );
01838 }
01839 else
01840 {
01841 report->progress( 100 );
01842 }
01843
01844
01845 RpmArgVec opts;
01846 if (flags & RPMINST_NOUPGRADE)
01847 opts.push_back("-i");
01848 else
01849 opts.push_back("-U");
01850
01851 opts.push_back("--percent");
01852
01853
01854 if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
01855 opts.push_back("--ignorearch");
01856
01857 if (flags & RPMINST_NODIGEST)
01858 opts.push_back("--nodigest");
01859 if (flags & RPMINST_NOSIGNATURE)
01860 opts.push_back("--nosignature");
01861 if (flags & RPMINST_EXCLUDEDOCS)
01862 opts.push_back ("--excludedocs");
01863 if (flags & RPMINST_NOSCRIPTS)
01864 opts.push_back ("--noscripts");
01865 if (flags & RPMINST_FORCE)
01866 opts.push_back ("--force");
01867 if (flags & RPMINST_NODEPS)
01868 opts.push_back ("--nodeps");
01869 if (flags & RPMINST_IGNORESIZE)
01870 opts.push_back ("--ignoresize");
01871 if (flags & RPMINST_JUSTDB)
01872 opts.push_back ("--justdb");
01873 if (flags & RPMINST_TEST)
01874 opts.push_back ("--test");
01875
01876 opts.push_back("--");
01877
01878
01879 string quotedFilename( rpmQuoteFilename( filename ) );
01880 opts.push_back ( quotedFilename.c_str() );
01881
01882 modifyDatabase();
01883 run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01884
01885 string line;
01886 string rpmmsg;
01887 vector<string> configwarnings;
01888
01889 unsigned linecnt = 0;
01890 while (systemReadLine(line))
01891 {
01892 if ( linecnt < MAXRPMMESSAGELINES )
01893 ++linecnt;
01894 else
01895 continue;
01896
01897 if (line.substr(0,2)=="%%")
01898 {
01899 int percent;
01900 sscanf (line.c_str () + 2, "%d", &percent);
01901 report->progress( percent );
01902 }
01903 else
01904 rpmmsg += line+'\n';
01905
01906 if ( line.substr(0,8) == "warning:" )
01907 {
01908 configwarnings.push_back(line);
01909 }
01910 }
01911 if ( linecnt > MAXRPMMESSAGELINES )
01912 rpmmsg += "[truncated]\n";
01913
01914 int rpm_status = systemStatus();
01915
01916
01917 for (vector<string>::iterator it = configwarnings.begin();
01918 it != configwarnings.end(); ++it)
01919 {
01920 processConfigFiles(*it, Pathname::basename(filename), " saved as ",
01921
01922 _("rpm saved %s as %s, but it was impossible to determine the difference"),
01923
01924 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
01925 processConfigFiles(*it, Pathname::basename(filename), " created as ",
01926
01927 _("rpm created %s as %s, but it was impossible to determine the difference"),
01928
01929 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
01930 }
01931
01932 if ( rpm_status != 0 )
01933 {
01934 historylog.comment(
01935 str::form("%s install failed", Pathname::basename(filename).c_str()),
01936 true );
01937 ostringstream sstr;
01938 sstr << "rpm output:" << endl << rpmmsg << endl;
01939 historylog.comment(sstr.str());
01940
01941 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
01942 (rpmmsg.empty() ? error_message : rpmmsg)));
01943 }
01944 else if ( ! rpmmsg.empty() )
01945 {
01946 historylog.comment(
01947 str::form("%s installed ok", Pathname::basename(filename).c_str()),
01948 true );
01949 ostringstream sstr;
01950 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
01951 historylog.comment(sstr.str());
01952
01953
01954
01955 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
01956 }
01957 }
01958
01960
01961
01962
01963
01964
01965 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
01966 {
01967
01968 return removePackage( package->name()
01969 + "-" + package->edition().version()
01970 + "-" + package->edition().release()
01971 + "." + package->arch().asString(), flags );
01972 }
01973
01975
01976
01977
01978
01979
01980 void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
01981 {
01982 callback::SendReport<RpmRemoveReport> report;
01983
01984 report->start( name_r );
01985
01986 do
01987 try
01988 {
01989 doRemovePackage(name_r, flags, report);
01990 report->finish();
01991 break;
01992 }
01993 catch (RpmException & excpt_r)
01994 {
01995 RpmRemoveReport::Action user = report->problem( excpt_r );
01996
01997 if ( user == RpmRemoveReport::ABORT )
01998 {
01999 report->finish( excpt_r );
02000 ZYPP_RETHROW(excpt_r);
02001 }
02002 else if ( user == RpmRemoveReport::IGNORE )
02003 {
02004 break;
02005 }
02006 }
02007 while (true);
02008 }
02009
02010
02011 void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
02012 {
02013 FAILIFNOTINITIALIZED;
02014 HistoryLog historylog;
02015
02016 MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
02017
02018
02019 if ( _packagebackups )
02020 {
02021
02022
02023 if ( ! backupPackage( name_r ) )
02024 {
02025 ERR << "backup of " << name_r << " failed" << endl;
02026 }
02027 report->progress( 0 );
02028 }
02029 else
02030 {
02031 report->progress( 100 );
02032 }
02033
02034
02035 RpmArgVec opts;
02036 opts.push_back("-e");
02037 opts.push_back("--allmatches");
02038
02039 if (flags & RPMINST_NOSCRIPTS)
02040 opts.push_back("--noscripts");
02041 if (flags & RPMINST_NODEPS)
02042 opts.push_back("--nodeps");
02043 if (flags & RPMINST_JUSTDB)
02044 opts.push_back("--justdb");
02045 if (flags & RPMINST_TEST)
02046 opts.push_back ("--test");
02047 if (flags & RPMINST_FORCE)
02048 {
02049 WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
02050 }
02051
02052 opts.push_back("--");
02053 opts.push_back(name_r.c_str());
02054
02055 modifyDatabase();
02056 run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
02057
02058 string line;
02059 string rpmmsg;
02060
02061
02062
02063
02064
02065 report->progress( 5 );
02066 unsigned linecnt = 0;
02067 while (systemReadLine(line))
02068 {
02069 if ( linecnt < MAXRPMMESSAGELINES )
02070 ++linecnt;
02071 else
02072 continue;
02073 rpmmsg += line+'\n';
02074 }
02075 if ( linecnt > MAXRPMMESSAGELINES )
02076 rpmmsg += "[truncated]\n";
02077 report->progress( 50 );
02078 int rpm_status = systemStatus();
02079
02080 if ( rpm_status != 0 )
02081 {
02082 historylog.comment(
02083 str::form("%s remove failed", name_r.c_str()), true );
02084 ostringstream sstr;
02085 sstr << "rpm output:" << endl << rpmmsg << endl;
02086 historylog.comment(sstr.str());
02087
02088 ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
02089 (rpmmsg.empty() ? error_message: rpmmsg)));
02090 }
02091 else if ( ! rpmmsg.empty() )
02092 {
02093 historylog.comment(
02094 str::form("%s removed ok", name_r.c_str()), true );
02095
02096 ostringstream sstr;
02097 sstr << "Additional rpm output:" << endl << rpmmsg << endl;
02098 historylog.comment(sstr.str());
02099
02100
02101
02102 report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
02103 }
02104 }
02105
02107
02108
02109
02110
02111
02112 bool RpmDb::backupPackage( const Pathname & filename )
02113 {
02114 RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
02115 if ( ! h )
02116 return false;
02117
02118 return backupPackage( h->tag_name() );
02119 }
02120
02122
02123
02124
02125
02126
02127 bool RpmDb::backupPackage(const string& packageName)
02128 {
02129 HistoryLog progresslog;
02130 bool ret = true;
02131 Pathname backupFilename;
02132 Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
02133
02134 if (_backuppath.empty())
02135 {
02136 INT << "_backuppath empty" << endl;
02137 return false;
02138 }
02139
02140 FileList fileList;
02141
02142 if (!queryChangedFiles(fileList, packageName))
02143 {
02144 ERR << "Error while getting changed files for package " <<
02145 packageName << endl;
02146 return false;
02147 }
02148
02149 if (fileList.size() <= 0)
02150 {
02151 DBG << "package " << packageName << " not changed -> no backup" << endl;
02152 return true;
02153 }
02154
02155 if (filesystem::assert_dir(_root + _backuppath) != 0)
02156 {
02157 return false;
02158 }
02159
02160 {
02161
02162 time_t currentTime = time(0);
02163 struct tm *currentLocalTime = localtime(¤tTime);
02164
02165 int date = (currentLocalTime->tm_year + 1900) * 10000
02166 + (currentLocalTime->tm_mon + 1) * 100
02167 + currentLocalTime->tm_mday;
02168
02169 int num = 0;
02170 do
02171 {
02172 backupFilename = _root + _backuppath
02173 + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
02174
02175 }
02176 while ( PathInfo(backupFilename).isExist() && num++ < 1000);
02177
02178 PathInfo pi(filestobackupfile);
02179 if (pi.isExist() && !pi.isFile())
02180 {
02181 ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
02182 return false;
02183 }
02184
02185 ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
02186
02187 if (!fp)
02188 {
02189 ERR << "could not open " << filestobackupfile.asString() << endl;
02190 return false;
02191 }
02192
02193 for (FileList::const_iterator cit = fileList.begin();
02194 cit != fileList.end(); ++cit)
02195 {
02196 string name = *cit;
02197 if ( name[0] == '/' )
02198 {
02199
02200 name = name.substr( 1 );
02201 }
02202 DBG << "saving file "<< name << endl;
02203 fp << name << endl;
02204 }
02205 fp.close();
02206
02207 const char* const argv[] =
02208 {
02209 "tar",
02210 "-czhP",
02211 "-C",
02212 _root.asString().c_str(),
02213 "--ignore-failed-read",
02214 "-f",
02215 backupFilename.asString().c_str(),
02216 "-T",
02217 filestobackupfile.asString().c_str(),
02218 NULL
02219 };
02220
02221
02222 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
02223
02224 string tarmsg;
02225
02226
02227
02228 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
02229 {
02230 tarmsg+=output;
02231 }
02232
02233 int ret = tar.close();
02234
02235 if ( ret != 0)
02236 {
02237 ERR << "tar failed: " << tarmsg << endl;
02238 ret = false;
02239 }
02240 else
02241 {
02242 MIL << "tar backup ok" << endl;
02243 progresslog.comment(
02244 str::form(_("created backup %s"), backupFilename.asString().c_str())
02245 , true);
02246 }
02247
02248 filesystem::unlink(filestobackupfile);
02249 }
02250
02251 return ret;
02252 }
02253
02254 void RpmDb::setBackupPath(const Pathname& path)
02255 {
02256 _backuppath = path;
02257 }
02258
02259 }
02260 }
02261 }