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