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

Generated on Tue May 5 14:43:20 2015 for libzypp by  doxygen 1.5.6