TargetImpl.cc

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