libzypp  10.5.0
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 #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