libzypp
10.5.0
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 00005 | / /__ | | | | | | | 00006 | /_____||_| |_| |_| | 00007 | | 00008 \---------------------------------------------------------------------*/ 00012 #include <iostream> 00013 #include <fstream> 00014 #include <sstream> 00015 #include <string> 00016 #include <list> 00017 #include <set> 00018 00019 #include <sys/types.h> 00020 #include <dirent.h> 00021 00022 #include "zypp/base/LogTools.h" 00023 #include "zypp/base/Exception.h" 00024 #include "zypp/base/Iterator.h" 00025 #include "zypp/base/Gettext.h" 00026 #include "zypp/base/IOStream.h" 00027 #include "zypp/base/Functional.h" 00028 #include "zypp/base/UserRequestException.h" 00029 00030 #include "zypp/ZConfig.h" 00031 #include "zypp/ZYppFactory.h" 00032 00033 #include "zypp/PoolItem.h" 00034 #include "zypp/ResObjects.h" 00035 #include "zypp/Url.h" 00036 #include "zypp/TmpPath.h" 00037 #include "zypp/RepoStatus.h" 00038 #include "zypp/ExternalProgram.h" 00039 #include "zypp/Repository.h" 00040 00041 #include "zypp/ResFilters.h" 00042 #include "zypp/HistoryLog.h" 00043 #include "zypp/target/TargetImpl.h" 00044 #include "zypp/target/TargetCallbackReceiver.h" 00045 #include "zypp/target/rpm/librpmDb.h" 00046 #include "zypp/target/CommitPackageCache.h" 00047 00048 #include "zypp/parser/ProductFileReader.h" 00049 00050 #include "zypp/pool/GetResolvablesToInsDel.h" 00051 #include "zypp/solver/detail/Testcase.h" 00052 00053 #include "zypp/repo/DeltaCandidates.h" 00054 #include "zypp/repo/PackageProvider.h" 00055 #include "zypp/repo/SrcPackageProvider.h" 00056 00057 #include "zypp/sat/Pool.h" 00058 #include "zypp/sat/Transaction.h" 00059 00060 #include "zypp/PluginScript.h" 00061 00062 using namespace std; 00063 00065 namespace zypp 00066 { 00067 00068 namespace target 00069 { 00070 00074 class CommitPlugins : private base::NonCopyable 00075 { 00076 public: 00077 00078 public: 00080 CommitPlugins() 00081 {} 00082 00084 ~CommitPlugins() 00085 { 00086 for_( it, _scripts.begin(), _scripts.end() ) 00087 { 00088 MIL << "Unload plugin: " << *it << endl; 00089 try { 00090 it->send( PluginFrame( "PLUGINEND" ) ); 00091 PluginFrame ret( it->receive() ); 00092 if ( ! ret.isAckCommand() ) 00093 { 00094 WAR << "Failed to unload plugin: Bad plugin response." << endl; 00095 } 00096 it->close(); 00097 } 00098 catch( const zypp::Exception & ) 00099 { 00100 WAR << "Failed to unload plugin." << endl; 00101 } 00102 } 00103 // _scripts dtor will disconnect all remaining plugins! 00104 } 00105 00112 void load( const Pathname & path_r ) 00113 { 00114 PathInfo pi( path_r ); 00115 if ( pi.isDir() ) 00116 { 00117 std::list<Pathname> entries; 00118 if ( filesystem::readdir( entries, pi.path(), false ) != 0 ) 00119 { 00120 WAR << "Plugin dir is not readable: " << pi << endl; 00121 return; 00122 } 00123 for_( it, entries.begin(), entries.end() ) 00124 { 00125 PathInfo pii( *it ); 00126 if ( pii.isFile() && pii.userMayRX() ) 00127 doLoad( pii ); 00128 } 00129 } 00130 else if ( pi.isFile() ) 00131 { 00132 if ( pi.userMayRX() ) 00133 doLoad( pi ); 00134 else 00135 WAR << "Plugin file is not executable: " << pi << endl; 00136 } 00137 else 00138 { 00139 WAR << "Plugin path is neither dir nor file: " << pi << endl; 00140 } 00141 } 00142 00143 private: 00144 void doLoad( const PathInfo & pi_r ) 00145 { 00146 MIL << "Load plugin: " << pi_r << endl; 00147 try { 00148 PluginScript plugin( pi_r.path() ); 00149 plugin.open(); 00150 plugin.send( PluginFrame( "PLUGINBEGIN" ) ); 00151 PluginFrame ret( plugin.receive() ); 00152 if ( ret.isAckCommand() ) 00153 { 00154 _scripts.push_back( plugin ); 00155 } 00156 else 00157 { 00158 WAR << "Failed to load plugin: Bad plugin response." << endl; 00159 } 00160 } 00161 catch( const zypp::Exception & ) 00162 { 00163 WAR << "Failed to load plugin." << endl; 00164 } 00165 } 00166 00167 private: 00168 std::list<PluginScript> _scripts; 00169 }; 00170 00171 void testCommitPlugins( const Pathname & path_r ) // for testing only 00172 { 00173 USR << "+++++" << endl; 00174 { 00175 CommitPlugins pl; 00176 pl.load( path_r ); 00177 USR << "=====" << endl; 00178 } 00179 USR << "-----" << endl; 00180 } 00181 00183 00185 void writeUpgradeTestcase() 00186 { 00187 unsigned toKeep( ZConfig::instance().solver_upgradeTestcasesToKeep() ); 00188 MIL << "Testcases to keep: " << toKeep << endl; 00189 if ( !toKeep ) 00190 return; 00191 Target_Ptr target( getZYpp()->getTarget() ); 00192 if ( ! target ) 00193 { 00194 WAR << "No Target no Testcase!" << endl; 00195 return; 00196 } 00197 00198 std::string stem( "updateTestcase" ); 00199 Pathname dir( target->assertRootPrefix("/var/log/") ); 00200 Pathname next( dir / Date::now().form( stem+"-%Y-%m-%d-%H-%M-%S" ) ); 00201 00202 { 00203 std::list<std::string> content; 00204 filesystem::readdir( content, dir, /*dots*/false ); 00205 std::set<std::string> cases; 00206 for_( c, content.begin(), content.end() ) 00207 { 00208 if ( str::startsWith( *c, stem ) ) 00209 cases.insert( *c ); 00210 } 00211 if ( cases.size() >= toKeep ) 00212 { 00213 unsigned toDel = cases.size() - toKeep + 1; // +1 for the new one 00214 for_( c, cases.begin(), cases.end() ) 00215 { 00216 filesystem::recursive_rmdir( dir/(*c) ); 00217 if ( ! --toDel ) 00218 break; 00219 } 00220 } 00221 } 00222 00223 MIL << "Write new testcase " << next << endl; 00224 getZYpp()->resolver()->createSolverTestcase( next.asString(), false/*no solving*/ ); 00225 } 00226 00228 namespace 00229 { 00230 00241 std::pair<bool,PatchScriptReport::Action> doExecuteScript( const Pathname & root_r, 00242 const Pathname & script_r, 00243 callback::SendReport<PatchScriptReport> & report_r ) 00244 { 00245 MIL << "Execute script " << PathInfo(Pathname::assertprefix( root_r,script_r)) << endl; 00246 00247 HistoryLog historylog; 00248 historylog.comment(script_r.asString() + _(" executed"), /*timestamp*/true); 00249 ExternalProgram prog( script_r.asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r ); 00250 00251 for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() ) 00252 { 00253 historylog.comment(output); 00254 if ( ! report_r->progress( PatchScriptReport::OUTPUT, output ) ) 00255 { 00256 WAR << "User request to abort script " << script_r << endl; 00257 prog.kill(); 00258 // the rest is handled by exit code evaluation 00259 // in case the script has meanwhile finished. 00260 } 00261 } 00262 00263 std::pair<bool,PatchScriptReport::Action> ret( std::make_pair( false, PatchScriptReport::ABORT ) ); 00264 00265 if ( prog.close() != 0 ) 00266 { 00267 ret.second = report_r->problem( prog.execError() ); 00268 WAR << "ACTION" << ret.second << "(" << prog.execError() << ")" << endl; 00269 std::ostringstream sstr; 00270 sstr << script_r << _(" execution failed") << " (" << prog.execError() << ")" << endl; 00271 historylog.comment(sstr.str(), /*timestamp*/true); 00272 return ret; 00273 } 00274 00275 report_r->finish(); 00276 ret.first = true; 00277 return ret; 00278 } 00279 00283 bool executeScript( const Pathname & root_r, 00284 const Pathname & script_r, 00285 callback::SendReport<PatchScriptReport> & report_r ) 00286 { 00287 std::pair<bool,PatchScriptReport::Action> action( std::make_pair( false, PatchScriptReport::ABORT ) ); 00288 00289 do { 00290 action = doExecuteScript( root_r, script_r, report_r ); 00291 if ( action.first ) 00292 return true; // success 00293 00294 switch ( action.second ) 00295 { 00296 case PatchScriptReport::ABORT: 00297 WAR << "User request to abort at script " << script_r << endl; 00298 return false; // requested abort. 00299 break; 00300 00301 case PatchScriptReport::IGNORE: 00302 WAR << "User request to skip script " << script_r << endl; 00303 return true; // requested skip. 00304 break; 00305 00306 case PatchScriptReport::RETRY: 00307 break; // again 00308 } 00309 } while ( action.second == PatchScriptReport::RETRY ); 00310 00311 // THIS is not intended to be reached: 00312 INT << "Abort on unknown ACTION request " << action.second << " returned" << endl; 00313 return false; // abort. 00314 } 00315 00321 bool RunUpdateScripts( const Pathname & root_r, 00322 const Pathname & scriptsPath_r, 00323 const std::vector<sat::Solvable> & checkPackages_r, 00324 bool aborting_r ) 00325 { 00326 if ( checkPackages_r.empty() ) 00327 return true; // no installed packages to check 00328 00329 MIL << "Looking for new update scripts in (" << root_r << ")" << scriptsPath_r << endl; 00330 Pathname scriptsDir( Pathname::assertprefix( root_r, scriptsPath_r ) ); 00331 if ( ! PathInfo( scriptsDir ).isDir() ) 00332 return true; // no script dir 00333 00334 std::list<std::string> scripts; 00335 filesystem::readdir( scripts, scriptsDir, /*dots*/false ); 00336 if ( scripts.empty() ) 00337 return true; // no scripts in script dir 00338 00339 // Now collect and execute all matching scripts. 00340 // On ABORT: at least log all outstanding scripts. 00341 // - "name-version-release" 00342 // - "name-version-release-*" 00343 bool abort = false; 00344 for_( it, checkPackages_r.begin(), checkPackages_r.end() ) 00345 { 00346 std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) ); 00347 for_( sit, scripts.begin(), scripts.end() ) 00348 { 00349 if ( ! str::hasPrefix( *sit, prefix ) ) 00350 continue; 00351 00352 if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' ) 00353 continue; // if not exact match it had to continue with '-' 00354 00355 PathInfo script( scriptsDir / *sit ); 00356 if ( ! script.isFile() ) 00357 continue; 00358 00359 // Assert it's set executable 00360 filesystem::addmod( script.path(), 0500 ); 00361 00362 Pathname localPath( scriptsPath_r/(*sit) ); // without root prefix 00363 if ( abort || aborting_r ) 00364 { 00365 WAR << "Aborting: Skip update script " << *sit << endl; 00366 HistoryLog().comment( 00367 localPath.asString() + _(" execution skipped while aborting"), 00368 /*timestamp*/true); 00369 } 00370 else 00371 { 00372 MIL << "Found update script " << *sit << endl; 00373 callback::SendReport<PatchScriptReport> report; 00374 report->start( make<Package>( *it ), script.path() ); 00375 00376 if ( ! executeScript( root_r, localPath, report ) ) // script path without root prefix! 00377 abort = true; // requested abort. 00378 } 00379 } 00380 } 00381 return !abort; 00382 } 00383 00385 // 00387 00388 inline void copyTo( std::ostream & out_r, const Pathname & file_r ) 00389 { 00390 std::ifstream infile( file_r.c_str() ); 00391 for( iostr::EachLine in( infile ); in; in.next() ) 00392 { 00393 out_r << *in << endl; 00394 } 00395 } 00396 00397 inline std::string notificationCmdSubst( const std::string & cmd_r, const UpdateNotificationFile & notification_r ) 00398 { 00399 std::string ret( cmd_r ); 00400 #define SUBST_IF(PAT,VAL) if ( ret.find( PAT ) != std::string::npos ) ret = str::gsub( ret, PAT, VAL ) 00401 SUBST_IF( "%p", notification_r.solvable().asString() ); 00402 SUBST_IF( "%P", notification_r.file().asString() ); 00403 #undef SUBST_IF 00404 return ret; 00405 } 00406 00407 void sendNotification( const Pathname & root_r, 00408 const UpdateNotifications & notifications_r ) 00409 { 00410 if ( notifications_r.empty() ) 00411 return; 00412 00413 std::string cmdspec( ZConfig::instance().updateMessagesNotify() ); 00414 MIL << "Notification command is '" << cmdspec << "'" << endl; 00415 if ( cmdspec.empty() ) 00416 return; 00417 00418 std::string::size_type pos( cmdspec.find( '|' ) ); 00419 if ( pos == std::string::npos ) 00420 { 00421 ERR << "Can't send Notification: Missing 'format |' in command spec." << endl; 00422 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true ); 00423 return; 00424 } 00425 00426 std::string formatStr( str::toLower( str::trim( cmdspec.substr( 0, pos ) ) ) ); 00427 std::string commandStr( str::trim( cmdspec.substr( pos + 1 ) ) ); 00428 00429 enum Format { UNKNOWN, NONE, SINGLE, DIGEST, BULK }; 00430 Format format = UNKNOWN; 00431 if ( formatStr == "none" ) 00432 format = NONE; 00433 else if ( formatStr == "single" ) 00434 format = SINGLE; 00435 else if ( formatStr == "digest" ) 00436 format = DIGEST; 00437 else if ( formatStr == "bulk" ) 00438 format = BULK; 00439 else 00440 { 00441 ERR << "Can't send Notification: Unknown format '" << formatStr << " |' in command spec." << endl; 00442 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true ); 00443 return; 00444 } 00445 00446 // Take care: commands are ececuted chroot(root_r). The message file 00447 // pathnames in notifications_r are local to root_r. For physical access 00448 // to the file they need to be prefixed. 00449 00450 if ( format == NONE || format == SINGLE ) 00451 { 00452 for_( it, notifications_r.begin(), notifications_r.end() ) 00453 { 00454 std::vector<std::string> command; 00455 if ( format == SINGLE ) 00456 command.push_back( "<"+Pathname::assertprefix( root_r, it->file() ).asString() ); 00457 str::splitEscaped( notificationCmdSubst( commandStr, *it ), std::back_inserter( command ) ); 00458 00459 ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r ); 00460 if ( true ) // Wait for feedback 00461 { 00462 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() ) 00463 { 00464 DBG << line; 00465 } 00466 int ret = prog.close(); 00467 if ( ret != 0 ) 00468 { 00469 ERR << "Notification command returned with error (" << ret << ")." << endl; 00470 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true ); 00471 return; 00472 } 00473 } 00474 } 00475 } 00476 else if ( format == DIGEST || format == BULK ) 00477 { 00478 filesystem::TmpFile tmpfile; 00479 ofstream out( tmpfile.path().c_str() ); 00480 for_( it, notifications_r.begin(), notifications_r.end() ) 00481 { 00482 if ( format == DIGEST ) 00483 { 00484 out << it->file() << endl; 00485 } 00486 else if ( format == BULK ) 00487 { 00488 copyTo( out << '\f', Pathname::assertprefix( root_r, it->file() ) ); 00489 } 00490 } 00491 00492 std::vector<std::string> command; 00493 command.push_back( "<"+tmpfile.path().asString() ); // redirect input 00494 str::splitEscaped( notificationCmdSubst( commandStr, *notifications_r.begin() ), std::back_inserter( command ) ); 00495 00496 ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r ); 00497 if ( true ) // Wait for feedback otherwise the TmpFile goes out of scope. 00498 { 00499 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() ) 00500 { 00501 DBG << line; 00502 } 00503 int ret = prog.close(); 00504 if ( ret != 0 ) 00505 { 00506 ERR << "Notification command returned with error (" << ret << ")." << endl; 00507 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true ); 00508 return; 00509 } 00510 } 00511 } 00512 else 00513 { 00514 INT << "Can't send Notification: Missing handler for 'format |' in command spec." << endl; 00515 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true ); 00516 return; 00517 } 00518 } 00519 00520 00526 void RunUpdateMessages( const Pathname & root_r, 00527 const Pathname & messagesPath_r, 00528 const std::vector<sat::Solvable> & checkPackages_r, 00529 ZYppCommitResult & result_r ) 00530 { 00531 if ( checkPackages_r.empty() ) 00532 return; // no installed packages to check 00533 00534 MIL << "Looking for new update messages in (" << root_r << ")" << messagesPath_r << endl; 00535 Pathname messagesDir( Pathname::assertprefix( root_r, messagesPath_r ) ); 00536 if ( ! PathInfo( messagesDir ).isDir() ) 00537 return; // no messages dir 00538 00539 std::list<std::string> messages; 00540 filesystem::readdir( messages, messagesDir, /*dots*/false ); 00541 if ( messages.empty() ) 00542 return; // no messages in message dir 00543 00544 // Now collect all matching messages in result and send them 00545 // - "name-version-release" 00546 // - "name-version-release-*" 00547 HistoryLog historylog; 00548 for_( it, checkPackages_r.begin(), checkPackages_r.end() ) 00549 { 00550 std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) ); 00551 for_( sit, messages.begin(), messages.end() ) 00552 { 00553 if ( ! str::hasPrefix( *sit, prefix ) ) 00554 continue; 00555 00556 if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' ) 00557 continue; // if not exact match it had to continue with '-' 00558 00559 PathInfo message( messagesDir / *sit ); 00560 if ( ! message.isFile() || message.size() == 0 ) 00561 continue; 00562 00563 MIL << "Found update message " << *sit << endl; 00564 Pathname localPath( messagesPath_r/(*sit) ); // without root prefix 00565 result_r.rUpdateMessages().push_back( UpdateNotificationFile( *it, localPath ) ); 00566 historylog.comment( str::Str() << _("New update message") << " " << localPath, /*timestamp*/true ); 00567 } 00568 } 00569 sendNotification( root_r, result_r.updateMessages() ); 00570 } 00571 00573 } // namespace 00575 00576 void XRunUpdateMessages( const Pathname & root_r, 00577 const Pathname & messagesPath_r, 00578 const std::vector<sat::Solvable> & checkPackages_r, 00579 ZYppCommitResult & result_r ) 00580 { RunUpdateMessages( root_r, messagesPath_r, checkPackages_r, result_r ); } 00581 00583 struct QueryInstalledEditionHelper 00584 { 00585 bool operator()( const std::string & name_r, 00586 const Edition & ed_r, 00587 const Arch & arch_r ) const 00588 { 00589 rpm::librpmDb::db_const_iterator it; 00590 for ( it.findByName( name_r ); *it; ++it ) 00591 { 00592 if ( arch_r == it->tag_arch() 00593 && ( ed_r == Edition::noedition || ed_r == it->tag_edition() ) ) 00594 { 00595 return true; 00596 } 00597 } 00598 return false; 00599 } 00600 }; 00601 00607 struct RepoProvidePackage 00608 { 00609 ResPool _pool; 00610 repo::RepoMediaAccess &_access; 00611 00612 RepoProvidePackage( repo::RepoMediaAccess &access, ResPool pool_r ) 00613 : _pool(pool_r), _access(access) 00614 {} 00615 00616 ManagedFile operator()( const PoolItem & pi ) 00617 { 00618 // Redirect PackageProvider queries for installed editions 00619 // (in case of patch/delta rpm processing) to rpmDb. 00620 repo::PackageProviderPolicy packageProviderPolicy; 00621 packageProviderPolicy.queryInstalledCB( QueryInstalledEditionHelper() ); 00622 00623 Package::constPtr p = asKind<Package>(pi.resolvable()); 00624 00625 // Build a repository list for repos 00626 // contributing to the pool 00627 std::list<Repository> repos( _pool.knownRepositoriesBegin(), _pool.knownRepositoriesEnd() ); 00628 repo::DeltaCandidates deltas(repos, p->name()); 00629 repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy ); 00630 00631 ManagedFile ret( pkgProvider.providePackage() ); 00632 return ret; 00633 } 00634 }; 00636 00637 IMPL_PTR_TYPE(TargetImpl); 00638 00639 TargetImpl_Ptr TargetImpl::_nullimpl; 00640 00642 TargetImpl_Ptr TargetImpl::nullimpl() 00643 { 00644 if (_nullimpl == 0) 00645 _nullimpl = new TargetImpl; 00646 return _nullimpl; 00647 } 00648 00650 // 00651 // METHOD NAME : TargetImpl::TargetImpl 00652 // METHOD TYPE : Ctor 00653 // 00654 TargetImpl::TargetImpl( const Pathname & root_r, bool doRebuild_r ) 00655 : _root( root_r ) 00656 , _requestedLocalesFile( home() / "RequestedLocales" ) 00657 , _softLocksFile( home() / "SoftLocks" ) 00658 , _hardLocksFile( Pathname::assertprefix( _root, ZConfig::instance().locksFile() ) ) 00659 { 00660 _rpm.initDatabase( root_r, Pathname(), doRebuild_r ); 00661 00662 HistoryLog::setRoot(_root); 00663 00664 createAnonymousId(); 00665 00666 MIL << "Initialized target on " << _root << endl; 00667 } 00668 00672 static string generateRandomId() 00673 { 00674 string id; 00675 const char* argv[] = 00676 { 00677 "/usr/bin/uuidgen", 00678 NULL 00679 }; 00680 00681 ExternalProgram prog( argv, 00682 ExternalProgram::Normal_Stderr, 00683 false, -1, true); 00684 std::string line; 00685 for(line = prog.receiveLine(); 00686 ! line.empty(); 00687 line = prog.receiveLine() ) 00688 { 00689 MIL << line << endl; 00690 id = line; 00691 break; 00692 } 00693 prog.close(); 00694 return id; 00695 } 00696 00702 void updateFileContent( const Pathname &filename, 00703 boost::function<bool ()> condition, 00704 boost::function<string ()> value ) 00705 { 00706 string val = value(); 00707 // if the value is empty, then just dont 00708 // do anything, regardless of the condition 00709 if ( val.empty() ) 00710 return; 00711 00712 if ( condition() ) 00713 { 00714 MIL << "updating '" << filename << "' content." << endl; 00715 00716 // if the file does not exist we need to generate the uuid file 00717 00718 std::ofstream filestr; 00719 // make sure the path exists 00720 filesystem::assert_dir( filename.dirname() ); 00721 filestr.open( filename.c_str() ); 00722 00723 if ( filestr.good() ) 00724 { 00725 filestr << val; 00726 filestr.close(); 00727 } 00728 else 00729 { 00730 // FIXME, should we ignore the error? 00731 ZYPP_THROW(Exception("Can't openfile '" + filename.asString() + "' for writing")); 00732 } 00733 } 00734 } 00735 00737 static bool fileMissing( const Pathname &pathname ) 00738 { 00739 return ! PathInfo(pathname).isExist(); 00740 } 00741 00742 void TargetImpl::createAnonymousId() const 00743 { 00744 00745 // create the anonymous unique id 00746 // this value is used for statistics 00747 Pathname idpath( home() / "AnonymousUniqueId"); 00748 00749 try 00750 { 00751 updateFileContent( idpath, 00752 boost::bind(fileMissing, idpath), 00753 generateRandomId ); 00754 } 00755 catch ( const Exception &e ) 00756 { 00757 WAR << "Can't create anonymous id file" << endl; 00758 } 00759 00760 } 00761 00762 void TargetImpl::createLastDistributionFlavorCache() const 00763 { 00764 // create the anonymous unique id 00765 // this value is used for statistics 00766 Pathname flavorpath( home() / "LastDistributionFlavor"); 00767 00768 // is there a product 00769 Product::constPtr p = baseProduct(); 00770 if ( ! p ) 00771 { 00772 WAR << "No base product, I won't create flavor cache" << endl; 00773 return; 00774 } 00775 00776 string flavor = p->flavor(); 00777 00778 try 00779 { 00780 00781 updateFileContent( flavorpath, 00782 // only if flavor is not empty 00783 functor::Constant<bool>( ! flavor.empty() ), 00784 functor::Constant<string>(flavor) ); 00785 } 00786 catch ( const Exception &e ) 00787 { 00788 WAR << "Can't create flavor cache" << endl; 00789 return; 00790 } 00791 } 00792 00794 // 00795 // METHOD NAME : TargetImpl::~TargetImpl 00796 // METHOD TYPE : Dtor 00797 // 00798 TargetImpl::~TargetImpl() 00799 { 00800 _rpm.closeDatabase(); 00801 MIL << "Targets closed" << endl; 00802 } 00803 00805 // 00806 // solv file handling 00807 // 00809 00810 Pathname TargetImpl::defaultSolvfilesPath() const 00811 { 00812 return Pathname::assertprefix( _root, ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoAlias() ); 00813 } 00814 00815 void TargetImpl::clearCache() 00816 { 00817 Pathname base = solvfilesPath(); 00818 filesystem::recursive_rmdir( base ); 00819 } 00820 00821 void TargetImpl::buildCache() 00822 { 00823 Pathname base = solvfilesPath(); 00824 Pathname rpmsolv = base/"solv"; 00825 Pathname rpmsolvcookie = base/"cookie"; 00826 00827 bool build_rpm_solv = true; 00828 // lets see if the rpm solv cache exists 00829 00830 RepoStatus rpmstatus( RepoStatus( _root/"/var/lib/rpm/Name" ) 00831 && (_root/"/etc/products.d") ); 00832 00833 bool solvexisted = PathInfo(rpmsolv).isExist(); 00834 if ( solvexisted ) 00835 { 00836 // see the status of the cache 00837 PathInfo cookie( rpmsolvcookie ); 00838 MIL << "Read cookie: " << cookie << endl; 00839 if ( cookie.isExist() ) 00840 { 00841 RepoStatus status = RepoStatus::fromCookieFile(rpmsolvcookie); 00842 // now compare it with the rpm database 00843 if ( status.checksum() == rpmstatus.checksum() ) 00844 build_rpm_solv = false; 00845 MIL << "Read cookie: " << rpmsolvcookie << " says: " 00846 << (build_rpm_solv ? "outdated" : "uptodate") << endl; 00847 } 00848 } 00849 00850 if ( build_rpm_solv ) 00851 { 00852 // if the solvfile dir does not exist yet, we better create it 00853 filesystem::assert_dir( base ); 00854 00855 Pathname oldSolvFile( solvexisted ? rpmsolv : Pathname() ); // to speedup rpmdb2solv 00856 00857 filesystem::TmpFile tmpsolv( filesystem::TmpFile::makeSibling( rpmsolv ) ); 00858 if ( !tmpsolv ) 00859 { 00860 // Can't create temporary solv file, usually due to insufficient permission 00861 // (user query while @System solv needs refresh). If so, try switching 00862 // to a location within zypps temp. space (will be cleaned at application end). 00863 00864 bool switchingToTmpSolvfile = false; 00865 Exception ex("Failed to cache rpm database."); 00866 ex.remember(str::form("Cannot create temporary file under %s.", base.c_str())); 00867 00868 if ( ! solvfilesPathIsTemp() ) 00869 { 00870 base = getZYpp()->tmpPath() / sat::Pool::instance().systemRepoAlias(); 00871 rpmsolv = base/"solv"; 00872 rpmsolvcookie = base/"cookie"; 00873 00874 filesystem::assert_dir( base ); 00875 tmpsolv = filesystem::TmpFile::makeSibling( rpmsolv ); 00876 00877 if ( tmpsolv ) 00878 { 00879 WAR << "Using a temporary solv file at " << base << endl; 00880 switchingToTmpSolvfile = true; 00881 _tmpSolvfilesPath = base; 00882 } 00883 else 00884 { 00885 ex.remember(str::form("Cannot create temporary file under %s.", base.c_str())); 00886 } 00887 } 00888 00889 if ( ! switchingToTmpSolvfile ) 00890 { 00891 ZYPP_THROW(ex); 00892 } 00893 } 00894 00895 // Take care we unlink the solvfile on exception 00896 ManagedFile guard( base, filesystem::recursive_rmdir ); 00897 00898 std::ostringstream cmd; 00899 cmd << "rpmdb2solv"; 00900 if ( ! _root.empty() ) 00901 cmd << " -r '" << _root << "'"; 00902 00903 cmd << " -p '" << Pathname::assertprefix( _root, "/etc/products.d" ) << "'"; 00904 00905 if ( ! oldSolvFile.empty() ) 00906 cmd << " '" << oldSolvFile << "'"; 00907 00908 cmd << " > '" << tmpsolv.path() << "'"; 00909 00910 MIL << "Executing: " << cmd << endl; 00911 ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout ); 00912 00913 cmd << endl; 00914 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) { 00915 WAR << " " << output; 00916 cmd << " " << output; 00917 } 00918 00919 int ret = prog.close(); 00920 if ( ret != 0 ) 00921 { 00922 Exception ex(str::form("Failed to cache rpm database (%d).", ret)); 00923 ex.remember( cmd.str() ); 00924 ZYPP_THROW(ex); 00925 } 00926 00927 ret = filesystem::rename( tmpsolv, rpmsolv ); 00928 if ( ret != 0 ) 00929 ZYPP_THROW(Exception("Failed to move cache to final destination")); 00930 // if this fails, don't bother throwing exceptions 00931 filesystem::chmod( rpmsolv, 0644 ); 00932 00933 rpmstatus.saveToCookieFile(rpmsolvcookie); 00934 00935 // We keep it. 00936 guard.resetDispose(); 00937 00938 // Finally send notification to plugins 00939 // NOTE: quick hack looking for spacewalk plugin only 00940 { 00941 Pathname script( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"system/spacewalk" ) ); 00942 if ( PathInfo( script ).isX() ) 00943 try { 00944 PluginScript spacewalk( script ); 00945 spacewalk.open(); 00946 00947 PluginFrame notify( "PACKAGESETCHANGED" ); 00948 spacewalk.send( notify ); 00949 00950 PluginFrame ret( spacewalk.receive() ); 00951 MIL << ret << endl; 00952 if ( ret.command() == "ERROR" ) 00953 ret.writeTo( WAR ) << endl; 00954 } 00955 catch ( const Exception & excpt ) 00956 { 00957 WAR << excpt.asUserHistory() << endl; 00958 } 00959 } 00960 } 00961 } 00962 00963 void TargetImpl::unload() 00964 { 00965 Repository system( sat::Pool::instance().findSystemRepo() ); 00966 if ( system ) 00967 system.eraseFromPool(); 00968 } 00969 00970 00971 void TargetImpl::load() 00972 { 00973 buildCache(); 00974 00975 // now add the repos to the pool 00976 sat::Pool satpool( sat::Pool::instance() ); 00977 Pathname rpmsolv( solvfilesPath() / "solv" ); 00978 MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoAlias() << ")" << endl; 00979 00980 // Providing an empty system repo, unload any old content 00981 Repository system( sat::Pool::instance().findSystemRepo() ); 00982 if ( system && ! system.solvablesEmpty() ) 00983 { 00984 system.eraseFromPool(); // invalidates system 00985 } 00986 if ( ! system ) 00987 { 00988 system = satpool.systemRepo(); 00989 } 00990 00991 try 00992 { 00993 system.addSolv( rpmsolv ); 00994 } 00995 catch ( const Exception & exp ) 00996 { 00997 ZYPP_CAUGHT( exp ); 00998 MIL << "Try to handle exception by rebuilding the solv-file" << endl; 00999 clearCache(); 01000 buildCache(); 01001 01002 system.addSolv( rpmsolv ); 01003 } 01004 01005 // (Re)Load the requested locales et al. 01006 // If the requested locales are empty, we leave the pool untouched 01007 // to avoid undoing changes the application applied. We expect this 01008 // to happen on a bare metal installation only. An already existing 01009 // target should be loaded before its settings are changed. 01010 { 01011 const LocaleSet & requestedLocales( _requestedLocalesFile.locales() ); 01012 if ( ! requestedLocales.empty() ) 01013 { 01014 satpool.setRequestedLocales( requestedLocales ); 01015 } 01016 } 01017 { 01018 SoftLocksFile::Data softLocks( _softLocksFile.data() ); 01019 if ( ! softLocks.empty() ) 01020 { 01021 // Don't soft lock any installed item. 01022 for_( it, system.solvablesBegin(), system.solvablesEnd() ) 01023 { 01024 softLocks.erase( it->ident() ); 01025 } 01026 ResPool::instance().setAutoSoftLocks( softLocks ); 01027 } 01028 } 01029 if ( ZConfig::instance().apply_locks_file() ) 01030 { 01031 const HardLocksFile::Data & hardLocks( _hardLocksFile.data() ); 01032 if ( ! hardLocks.empty() ) 01033 { 01034 ResPool::instance().setHardLockQueries( hardLocks ); 01035 } 01036 } 01037 01038 // now that the target is loaded, we can cache the flavor 01039 createLastDistributionFlavorCache(); 01040 01041 MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl; 01042 } 01043 01045 // 01046 // COMMIT 01047 // 01049 ZYppCommitResult TargetImpl::commit( ResPool pool_r, const ZYppCommitPolicy & policy_rX ) 01050 { 01051 // ----------------------------------------------------------------- // 01052 ZYppCommitPolicy policy_r( policy_rX ); 01053 01054 // Fake outstanding YCP fix: Honour restriction to media 1 01055 // at installation, but install all remaining packages if post-boot. 01056 if ( policy_r.restrictToMedia() > 1 ) 01057 policy_r.allMedia(); 01058 01059 if ( policy_r.downloadMode() == DownloadDefault ) { 01060 if ( root() == "/" ) 01061 policy_r.downloadMode(DownloadInHeaps); 01062 else 01063 policy_r.downloadMode(DownloadAsNeeded); 01064 } 01065 // DownloadOnly implies dry-run. 01066 else if ( policy_r.downloadMode() == DownloadOnly ) 01067 policy_r.dryRun( true ); 01068 // ----------------------------------------------------------------- // 01069 01070 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl; 01071 01073 // Prepare execution of commit plugins: 01075 CommitPlugins commitPlugins; 01076 if ( root() == "/" && ! policy_r.dryRun() ) 01077 { 01078 Pathname plugindir( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"commit" ) ); 01079 commitPlugins.load( plugindir ); 01080 } 01081 01083 // Write out a testcase if we're in dist upgrade mode. 01085 if ( getZYpp()->resolver()->upgradeMode() ) 01086 { 01087 if ( ! policy_r.dryRun() ) 01088 { 01089 writeUpgradeTestcase(); 01090 } 01091 else 01092 { 01093 DBG << "dryRun: Not writing upgrade testcase." << endl; 01094 } 01095 } 01096 01098 // Store non-package data: 01100 if ( ! policy_r.dryRun() ) 01101 { 01102 filesystem::assert_dir( home() ); 01103 // requested locales 01104 _requestedLocalesFile.setLocales( pool_r.getRequestedLocales() ); 01105 // weak locks 01106 { 01107 SoftLocksFile::Data newdata; 01108 pool_r.getActiveSoftLocks( newdata ); 01109 _softLocksFile.setData( newdata ); 01110 } 01111 // hard locks 01112 if ( ZConfig::instance().apply_locks_file() ) 01113 { 01114 HardLocksFile::Data newdata; 01115 pool_r.getHardLockQueries( newdata ); 01116 _hardLocksFile.setData( newdata ); 01117 } 01118 } 01119 else 01120 { 01121 DBG << "dryRun: Not stroring non-package data." << endl; 01122 } 01123 01125 // Compute transaction: 01127 ZYppCommitResult result( root() ); 01128 result.rTransaction() = pool_r.resolver().getTransaction(); 01129 result.rTransaction().order(); 01130 // steps: this is our todo-list 01131 ZYppCommitResult::TransactionStepList & steps( result.rTransactionStepList() ); 01132 if ( policy_r.restrictToMedia() ) 01133 { 01134 // Collect until the 1st package from an unwanted media occurs. 01135 // Further collection could violate install order. 01136 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl; 01137 for_( it, result.transaction().begin(), result.transaction().end() ) 01138 { 01139 if ( makeResObject( *it )->mediaNr() > 1 ) 01140 break; 01141 steps.push_back( *it ); 01142 } 01143 } 01144 else 01145 { 01146 result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() ); 01147 } 01148 MIL << "Todo: " << result << endl; 01149 01151 // First collect and display all messages 01152 // associated with patches to be installed. 01154 if ( ! policy_r.dryRun() ) 01155 { 01156 for_( it, steps.begin(), steps.end() ) 01157 { 01158 if ( ! it->satSolvable().isKind<Patch>() ) 01159 continue; 01160 01161 PoolItem pi( *it ); 01162 if ( ! pi.status().isToBeInstalled() ) 01163 continue; 01164 01165 Patch::constPtr patch( asKind<Patch>(pi.resolvable()) ); 01166 if ( ! patch ||patch->message().empty() ) 01167 continue; 01168 01169 MIL << "Show message for " << patch << endl; 01170 callback::SendReport<target::PatchMessageReport> report; 01171 if ( ! report->show( patch ) ) 01172 { 01173 WAR << "commit aborted by the user" << endl; 01174 ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) ); 01175 } 01176 } 01177 } 01178 else 01179 { 01180 DBG << "dryRun: Not checking patch messages." << endl; 01181 } 01182 01184 // Remove/install packages. 01186 DBG << "commit log file is set to: " << HistoryLog::fname() << endl; 01187 if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly ) 01188 { 01189 // Prepare the package cache. Pass all items requiring download. 01190 repo::RepoMediaAccess access; 01191 RepoProvidePackage repoProvidePackage( access, pool_r ); 01192 CommitPackageCache packageCache( root() / "tmp", repoProvidePackage ); 01193 packageCache.setCommitList( steps.begin(), steps.end() ); 01194 01195 bool miss = false; 01196 if ( policy_r.downloadMode() != DownloadAsNeeded ) 01197 { 01198 // Preload the cache. Until now this means pre-loading all packages. 01199 // Once DownloadInHeaps is fully implemented, this will change and 01200 // we may actually have more than one heap. 01201 for_( it, steps.begin(), steps.end() ) 01202 { 01203 switch ( it->stepType() ) 01204 { 01205 case sat::Transaction::TRANSACTION_INSTALL: 01206 case sat::Transaction::TRANSACTION_MULTIINSTALL: 01207 // proceed: only install actionas may require download. 01208 break; 01209 01210 default: 01211 // next: no download for or non-packages and delete actions. 01212 continue; 01213 break; 01214 } 01215 01216 PoolItem pi( *it ); 01217 if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() ) 01218 { 01219 ManagedFile localfile; 01220 try 01221 { 01222 // TODO: unify packageCache.get for Package and SrcPackage 01223 if ( pi->isKind<Package>() ) 01224 { 01225 localfile = packageCache.get( pi ); 01226 } 01227 else if ( pi->isKind<SrcPackage>() ) 01228 { 01229 repo::RepoMediaAccess access; 01230 repo::SrcPackageProvider prov( access ); 01231 localfile = prov.provideSrcPackage( pi->asKind<SrcPackage>() ); 01232 } 01233 else 01234 { 01235 INT << "Don't know howto cache: Neither Package nor SrcPackage: " << pi << endl; 01236 continue; 01237 } 01238 localfile.resetDispose(); // keep the package file in the cache 01239 } 01240 catch ( const AbortRequestException & exp ) 01241 { 01242 it->stepStage( sat::Transaction::STEP_ERROR ); 01243 miss = true; 01244 WAR << "commit cache preload aborted by the user" << endl; 01245 ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) ); 01246 break; 01247 } 01248 catch ( const SkipRequestException & exp ) 01249 { 01250 ZYPP_CAUGHT( exp ); 01251 it->stepStage( sat::Transaction::STEP_ERROR ); 01252 miss = true; 01253 WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl; 01254 continue; 01255 } 01256 catch ( const Exception & exp ) 01257 { 01258 // bnc #395704: missing catch causes abort. 01259 // TODO see if packageCache fails to handle errors correctly. 01260 ZYPP_CAUGHT( exp ); 01261 it->stepStage( sat::Transaction::STEP_ERROR ); 01262 miss = true; 01263 INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl; 01264 continue; 01265 } 01266 } 01267 } 01268 } 01269 01270 if ( miss ) 01271 { 01272 ERR << "Some packages could not be provided. Aborting commit."<< endl; 01273 } 01274 else if ( ! policy_r.dryRun() ) 01275 { 01276 commit( policy_r, packageCache, result ); 01277 } 01278 else 01279 { 01280 DBG << "dryRun: Not installing/deleting anything." << endl; 01281 } 01282 } 01283 else 01284 { 01285 DBG << "dryRun: Not downloading/installing/deleting anything." << endl; 01286 } 01287 01289 // Try to rebuild solv file while rpm database is still in cache 01291 if ( ! policy_r.dryRun() ) 01292 { 01293 buildCache(); 01294 } 01295 01296 // for DEPRECATED old ZyppCommitResult results: 01298 // build return statistics 01300 result._errors.clear(); 01301 result._remaining.clear(); 01302 result._srcremaining.clear(); 01303 unsigned toInstall = 0; 01304 for_( step, steps.begin(), steps.end() ) 01305 { 01306 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE ) 01307 { 01308 // For non-packages only products might have beed installed. 01309 // All the rest is ignored. 01310 if ( step->satSolvable().isSystem() || ! step->satSolvable().isKind<Product>() ) 01311 continue; 01312 } 01313 else if ( step->stepType() == sat::Transaction::TRANSACTION_ERASE ) 01314 { 01315 continue; 01316 } 01317 // to be installed: 01318 ++toInstall; 01319 switch ( step->stepStage() ) 01320 { 01321 case sat::Transaction::STEP_TODO: 01322 if ( step->satSolvable().isKind<Package>() ) 01323 result._remaining.push_back( PoolItem( *step ) ); 01324 else if ( step->satSolvable().isKind<SrcPackage>() ) 01325 result._srcremaining.push_back( PoolItem( *step ) ); 01326 break; 01327 case sat::Transaction::STEP_DONE: 01328 // NOOP 01329 break; 01330 case sat::Transaction::STEP_ERROR: 01331 result._errors.push_back( PoolItem( *step ) ); 01332 break; 01333 } 01334 } 01335 result._result = (toInstall - result._remaining.size()); 01337 01338 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl; 01339 return result; 01340 } 01341 01343 // 01344 // COMMIT internal 01345 // 01347 void TargetImpl::commit( const ZYppCommitPolicy & policy_r, 01348 CommitPackageCache & packageCache_r, 01349 ZYppCommitResult & result_r ) 01350 { 01351 // steps: this is our todo-list 01352 ZYppCommitResult::TransactionStepList & steps( result_r.rTransactionStepList() ); 01353 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl; 01354 01355 bool abort = false; 01356 std::vector<sat::Solvable> successfullyInstalledPackages; 01357 TargetImpl::PoolItemList remaining; 01358 01359 for_( step, steps.begin(), steps.end() ) 01360 { 01361 PoolItem citem( *step ); 01362 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE ) 01363 { 01364 if ( citem->isKind<Package>() ) 01365 { 01366 // for packages this means being obsoleted (by rpm) 01367 // thius no additional action is needed. 01368 step->stepStage( sat::Transaction::STEP_DONE ); 01369 continue; 01370 } 01371 } 01372 01373 if ( citem->isKind<Package>() ) 01374 { 01375 Package::constPtr p = citem->asKind<Package>(); 01376 if ( citem.status().isToBeInstalled() ) 01377 { 01378 ManagedFile localfile; 01379 try 01380 { 01381 localfile = packageCache_r.get( citem ); 01382 } 01383 catch ( const AbortRequestException &e ) 01384 { 01385 WAR << "commit aborted by the user" << endl; 01386 abort = true; 01387 step->stepStage( sat::Transaction::STEP_ERROR ); 01388 break; 01389 } 01390 catch ( const SkipRequestException &e ) 01391 { 01392 ZYPP_CAUGHT( e ); 01393 WAR << "Skipping package " << p << " in commit" << endl; 01394 step->stepStage( sat::Transaction::STEP_ERROR ); 01395 continue; 01396 } 01397 catch ( const Exception &e ) 01398 { 01399 // bnc #395704: missing catch causes abort. 01400 // TODO see if packageCache fails to handle errors correctly. 01401 ZYPP_CAUGHT( e ); 01402 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl; 01403 step->stepStage( sat::Transaction::STEP_ERROR ); 01404 continue; 01405 } 01406 01407 #warning Exception handling 01408 // create a installation progress report proxy 01409 RpmInstallPackageReceiver progress( citem.resolvable() ); 01410 progress.connect(); // disconnected on destruction. 01411 01412 bool success = false; 01413 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB ); 01414 // Why force and nodeps? 01415 // 01416 // Because zypp builds the transaction and the resolver asserts that 01417 // everything is fine. 01418 // We use rpm just to unpack and register the package in the database. 01419 // We do this step by step, so rpm is not aware of the bigger context. 01420 // So we turn off rpms internal checks, because we do it inside zypp. 01421 flags |= rpm::RPMINST_NODEPS; 01422 flags |= rpm::RPMINST_FORCE; 01423 // 01424 if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE; 01425 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST; 01426 if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS; 01427 if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE; 01428 01429 try 01430 { 01431 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE ); 01432 rpm().installPackage( localfile, flags ); 01433 HistoryLog().install(citem); 01434 01435 if ( progress.aborted() ) 01436 { 01437 WAR << "commit aborted by the user" << endl; 01438 localfile.resetDispose(); // keep the package file in the cache 01439 abort = true; 01440 step->stepStage( sat::Transaction::STEP_ERROR ); 01441 break; 01442 } 01443 else 01444 { 01445 success = true; 01446 step->stepStage( sat::Transaction::STEP_DONE ); 01447 } 01448 } 01449 catch ( Exception & excpt_r ) 01450 { 01451 ZYPP_CAUGHT(excpt_r); 01452 localfile.resetDispose(); // keep the package file in the cache 01453 01454 if ( policy_r.dryRun() ) 01455 { 01456 WAR << "dry run failed" << endl; 01457 step->stepStage( sat::Transaction::STEP_ERROR ); 01458 break; 01459 } 01460 // else 01461 if ( progress.aborted() ) 01462 { 01463 WAR << "commit aborted by the user" << endl; 01464 abort = true; 01465 } 01466 else 01467 { 01468 WAR << "Install failed" << endl; 01469 } 01470 step->stepStage( sat::Transaction::STEP_ERROR ); 01471 break; // stop 01472 } 01473 01474 if ( success && !policy_r.dryRun() ) 01475 { 01476 citem.status().resetTransact( ResStatus::USER ); 01477 successfullyInstalledPackages.push_back( citem.satSolvable() ); 01478 step->stepStage( sat::Transaction::STEP_DONE ); 01479 } 01480 } 01481 else 01482 { 01483 RpmRemovePackageReceiver progress( citem.resolvable() ); 01484 progress.connect(); // disconnected on destruction. 01485 01486 bool success = false; 01487 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB ); 01488 flags |= rpm::RPMINST_NODEPS; 01489 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST; 01490 try 01491 { 01492 rpm().removePackage( p, flags ); 01493 HistoryLog().remove(citem); 01494 01495 if ( progress.aborted() ) 01496 { 01497 WAR << "commit aborted by the user" << endl; 01498 abort = true; 01499 step->stepStage( sat::Transaction::STEP_ERROR ); 01500 break; 01501 } 01502 else 01503 { 01504 success = true; 01505 step->stepStage( sat::Transaction::STEP_DONE ); 01506 } 01507 } 01508 catch (Exception & excpt_r) 01509 { 01510 ZYPP_CAUGHT( excpt_r ); 01511 if ( progress.aborted() ) 01512 { 01513 WAR << "commit aborted by the user" << endl; 01514 abort = true; 01515 step->stepStage( sat::Transaction::STEP_ERROR ); 01516 break; 01517 } 01518 // else 01519 WAR << "removal of " << p << " failed"; 01520 step->stepStage( sat::Transaction::STEP_ERROR ); 01521 } 01522 if ( success && !policy_r.dryRun() ) 01523 { 01524 citem.status().resetTransact( ResStatus::USER ); 01525 step->stepStage( sat::Transaction::STEP_DONE ); 01526 } 01527 } 01528 } 01529 else if ( ! policy_r.dryRun() ) // other resolvables (non-Package) 01530 { 01531 // Status is changed as the buddy package buddy 01532 // gets installed/deleted. Handle non-buddies only. 01533 if ( ! citem.buddy() ) 01534 { 01535 if ( citem->isKind<Product>() ) 01536 { 01537 Product::constPtr p = citem->asKind<Product>(); 01538 if ( citem.status().isToBeInstalled() ) 01539 { 01540 ERR << "Can't install orphan product without release-package! " << citem << endl; 01541 } 01542 else 01543 { 01544 // Deleting the corresponding product entry is all we con do. 01545 // So the product will no longer be visible as installed. 01546 std::string referenceFilename( p->referenceFilename() ); 01547 if ( referenceFilename.empty() ) 01548 { 01549 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl; 01550 } 01551 else 01552 { 01553 PathInfo referenceFile( Pathname::assertprefix( _root, Pathname( "/etc/products.d" ) ) / referenceFilename ); 01554 if ( ! referenceFile.isFile() || filesystem::unlink( referenceFile.path() ) != 0 ) 01555 { 01556 ERR << "Delete orphan product failed: " << referenceFile << endl; 01557 } 01558 } 01559 } 01560 } 01561 else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) 01562 { 01563 // SrcPackage is install-only 01564 SrcPackage::constPtr p = citem->asKind<SrcPackage>(); 01565 installSrcPackage( p ); 01566 } 01567 01568 citem.status().resetTransact( ResStatus::USER ); 01569 step->stepStage( sat::Transaction::STEP_DONE ); 01570 } 01571 01572 } // other resolvables 01573 01574 } // for 01575 01576 // Check presence of update scripts/messages. If aborting, 01577 // at least log omitted scripts. 01578 if ( ! successfullyInstalledPackages.empty() ) 01579 { 01580 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(), 01581 successfullyInstalledPackages, abort ) ) 01582 { 01583 WAR << "Commit aborted by the user" << endl; 01584 abort = true; 01585 } 01586 // send messages after scripts in case some script generates output, 01587 // that should be kept in t %ghost message file. 01588 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(), 01589 successfullyInstalledPackages, 01590 result_r ); 01591 } 01592 01593 if ( abort ) 01594 { 01595 ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) ); 01596 } 01597 } 01598 01600 01601 rpm::RpmDb & TargetImpl::rpm() 01602 { 01603 return _rpm; 01604 } 01605 01606 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const 01607 { 01608 return _rpm.hasFile(path_str, name_str); 01609 } 01610 01611 01612 Date TargetImpl::timestamp() const 01613 { 01614 return _rpm.timestamp(); 01615 } 01616 01618 namespace 01619 { 01620 parser::ProductFileData baseproductdata( const Pathname & root_r ) 01621 { 01622 PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) ); 01623 if ( baseproduct.isFile() ) 01624 { 01625 try 01626 { 01627 return parser::ProductFileReader::scanFile( baseproduct.path() ); 01628 } 01629 catch ( const Exception & excpt ) 01630 { 01631 ZYPP_CAUGHT( excpt ); 01632 } 01633 } 01634 return parser::ProductFileData(); 01635 } 01636 01637 inline Pathname staticGuessRoot( const Pathname & root_r ) 01638 { 01639 if ( root_r.empty() ) 01640 { 01641 // empty root: use existing Target or assume "/" 01642 Pathname ret ( ZConfig::instance().systemRoot() ); 01643 if ( ret.empty() ) 01644 return Pathname("/"); 01645 return ret; 01646 } 01647 return root_r; 01648 } 01649 01650 inline std::string firstNonEmptyLineIn( const Pathname & file_r ) 01651 { 01652 std::ifstream idfile( file_r.c_str() ); 01653 for( iostr::EachLine in( idfile ); in; in.next() ) 01654 { 01655 std::string line( str::trim( *in ) ); 01656 if ( ! line.empty() ) 01657 return line; 01658 } 01659 return std::string(); 01660 } 01661 } // namescpace 01663 01664 Product::constPtr TargetImpl::baseProduct() const 01665 { 01666 ResPool pool(ResPool::instance()); 01667 for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() ) 01668 { 01669 Product::constPtr p = (*it)->asKind<Product>(); 01670 if ( p->isTargetDistribution() ) 01671 return p; 01672 } 01673 return nullptr; 01674 } 01675 01676 LocaleSet TargetImpl::requestedLocales( const Pathname & root_r ) 01677 { 01678 const Pathname needroot( staticGuessRoot(root_r) ); 01679 const Target_constPtr target( getZYpp()->getTarget() ); 01680 if ( target && target->root() == needroot ) 01681 return target->requestedLocales(); 01682 return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales(); 01683 } 01684 01685 std::string TargetImpl::targetDistribution() const 01686 { return baseproductdata( _root ).registerTarget(); } 01687 // static version: 01688 std::string TargetImpl::targetDistribution( const Pathname & root_r ) 01689 { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); } 01690 01691 std::string TargetImpl::targetDistributionRelease() const 01692 { return baseproductdata( _root ).registerRelease(); } 01693 // static version: 01694 std::string TargetImpl::targetDistributionRelease( const Pathname & root_r ) 01695 { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();} 01696 01697 Target::DistributionLabel TargetImpl::distributionLabel() const 01698 { 01699 Target::DistributionLabel ret; 01700 parser::ProductFileData pdata( baseproductdata( _root ) ); 01701 ret.shortName = pdata.shortName(); 01702 ret.summary = pdata.summary(); 01703 return ret; 01704 } 01705 // static version: 01706 Target::DistributionLabel TargetImpl::distributionLabel( const Pathname & root_r ) 01707 { 01708 Target::DistributionLabel ret; 01709 parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) ); 01710 ret.shortName = pdata.shortName(); 01711 ret.summary = pdata.summary(); 01712 return ret; 01713 } 01714 01715 std::string TargetImpl::distributionVersion() const 01716 { 01717 if ( _distributionVersion.empty() ) 01718 { 01719 _distributionVersion = TargetImpl::distributionVersion(root()); 01720 if ( !_distributionVersion.empty() ) 01721 MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl; 01722 } 01723 return _distributionVersion; 01724 } 01725 // static version 01726 std::string TargetImpl::distributionVersion( const Pathname & root_r ) 01727 { 01728 std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version(); 01729 if ( distributionVersion.empty() ) 01730 { 01731 // ...But the baseproduct method is not expected to work on RedHat derivatives. 01732 // On RHEL, Fedora and others the "product version" is determined by the first package 01733 // providing 'redhat-release'. This value is not hardcoded in YUM and can be configured 01734 // with the $distroverpkg variable. 01735 scoped_ptr<rpm::RpmDb> tmprpmdb; 01736 if ( ZConfig::instance().systemRoot() == Pathname() ) 01737 { 01738 try 01739 { 01740 tmprpmdb.reset( new rpm::RpmDb ); 01741 tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ ); 01742 } 01743 catch( ... ) 01744 { 01745 return ""; 01746 } 01747 } 01748 rpm::librpmDb::db_const_iterator it; 01749 if ( it.findByProvides( ZConfig::instance().distroverpkg() ) ) 01750 distributionVersion = it->tag_version(); 01751 } 01752 return distributionVersion; 01753 } 01754 01755 01756 std::string TargetImpl::distributionFlavor() const 01757 { 01758 return firstNonEmptyLineIn( home() / "LastDistributionFlavor" ); 01759 } 01760 // static version: 01761 std::string TargetImpl::distributionFlavor( const Pathname & root_r ) 01762 { 01763 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" ); 01764 } 01765 01767 01768 std::string TargetImpl::anonymousUniqueId() const 01769 { 01770 return firstNonEmptyLineIn( home() / "AnonymousUniqueId" ); 01771 } 01772 // static version: 01773 std::string TargetImpl::anonymousUniqueId( const Pathname & root_r ) 01774 { 01775 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/AnonymousUniqueId" ); 01776 } 01777 01779 01780 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r ) 01781 { 01782 // provide on local disk 01783 repo::RepoMediaAccess access_r; 01784 repo::SrcPackageProvider prov( access_r ); 01785 ManagedFile localfile = prov.provideSrcPackage( srcPackage_r ); 01786 // install it 01787 rpm().installPackage ( localfile ); 01788 } 01789 01791 } // namespace target 01794 } // namespace zypp