PackageProvider.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include <sstream>
00014 #include "zypp/repo/PackageDelta.h"
00015 #include "zypp/base/Logger.h"
00016 #include "zypp/base/Gettext.h"
00017 #include "zypp/base/UserRequestException.h"
00018 #include "zypp/repo/PackageProvider.h"
00019 #include "zypp/repo/RepoProvideFile.h"
00020 #include "zypp/repo/Applydeltarpm.h"
00021 #include "zypp/repo/PackageDelta.h"
00022 
00023 #include "zypp/TmpPath.h"
00024 #include "zypp/ZConfig.h"
00025 #include "zypp/RepoInfo.h"
00026 
00027 using std::endl;
00028 
00030 namespace zypp
00031 { 
00032 
00033   namespace repo
00034   { 
00035 
00037     //
00038     //  CLASS NAME : PackageProviderPolicy
00039     //
00041 
00042     bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
00043                                                 const Edition &     ed_r,
00044                                                 const Arch &        arch_r ) const
00045     {
00046       if ( _queryInstalledCB )
00047         return _queryInstalledCB( name_r, ed_r, arch_r );
00048       return false;
00049     }
00050 
00052     //
00053     //  CLASS NAME : PackageProvider
00054     //
00056 
00058     namespace
00059     { 
00060 
00061       inline std::string defRpmFileName( const Package::constPtr & package )
00062       {
00063         std::ostringstream ret;
00064         ret << package->name() << '-' << package->edition() << '.' << package->arch() << ".rpm";
00065         return ret.str();
00066       }
00067 
00069     } // namespace source
00071     PackageProvider::PackageProvider(  RepoMediaAccess &access,
00072                                       const Package::constPtr & package,
00073                                       const DeltaCandidates & deltas,
00074                                       const PackageProviderPolicy & policy_r )
00075     : _policy( policy_r )
00076     , _package( package )
00077     , _deltas(deltas)
00078     , _access(access)
00079     , _retry(false)
00080     {}
00081 
00082     PackageProvider::~PackageProvider()
00083     {}
00084 
00085     ManagedFile PackageProvider::providePackage() const
00086     {
00087       Url url;
00088       RepoInfo info = _package->repoInfo();
00089       // FIXME we only support the first url for now.
00090       if ( info.baseUrlsEmpty() )
00091         ZYPP_THROW(Exception("No url in repository."));
00092       else
00093         url = * info.baseUrlsBegin();
00094 
00095       { // check for cache hit:
00096         OnMediaLocation loc( _package->location() );
00097         PathInfo cachepath( info.packagesPath() / loc.filename() );
00098 
00099         if ( cachepath.isFile() && ! loc.checksum().empty() ) // accept cache hit with matching checksum only!
00100              // Tempting to do a quick check for matching .rpm-filesize before computing checksum,
00101              // but real life shows that loc.downloadSize() and the .rpm-filesize frequently do not
00102              // match, even if loc.checksum() and the .rpm-files checksum do. Blame the metadata generator(s).
00103         {
00104           CheckSum cachechecksum( loc.checksum().type(), filesystem::checksum( cachepath.path(), loc.checksum().type() ) );
00105           USR << cachechecksum << endl;
00106           if ( cachechecksum == loc.checksum() )
00107           {
00108             ManagedFile ret( cachepath.path() );
00109             if ( ! info.keepPackages() )
00110             {
00111               ret.setDispose( filesystem::unlink );
00112             }
00113             MIL << "provided Package from cache " << _package << " at " << ret << endl;
00114             return ret; // <-- cache hit
00115           }
00116         }
00117       }
00118 
00119       // HERE: cache misss, do download:
00120       MIL << "provide Package " << _package << endl;
00121       ScopedGuard guardReport( newReport() );
00122       ManagedFile ret;
00123       do {
00124         _retry = false;
00125         report()->start( _package, url );
00126         try  // ELIMINATE try/catch by providing a log-guard
00127           {
00128             ret = doProvidePackage();
00129           }
00130         catch ( const UserRequestException & excpt )
00131           {
00132             // UserRequestException e.g. from failOnChecksumError was already reported.
00133             ERR << "Failed to provide Package " << _package << endl;
00134             if ( ! _retry )
00135               {
00136                 ZYPP_RETHROW( excpt );
00137               }
00138           }
00139         catch ( const Exception & excpt )
00140           {
00141             ERR << "Failed to provide Package " << _package << endl;
00142             if ( ! _retry )
00143               {
00144                 // Aything else gets reported
00145                 std::string package_str = _package->name() + "-" + _package->edition().asString();
00146 
00147                 // TranslatorExplanation %s = name of the package being processed.
00148                 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
00149                 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
00150 
00151                 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
00152                 {
00153                       case repo::DownloadResolvableReport::RETRY:
00154                         _retry = true;
00155                         break;
00156                       case repo::DownloadResolvableReport::IGNORE:
00157                         ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
00158                         break;
00159                       case repo::DownloadResolvableReport::ABORT:
00160                         ZYPP_THROW(AbortRequestException("User requested to abort"));
00161                         break;
00162                       default:
00163                         ZYPP_RETHROW( excpt );
00164                         break;
00165                 }
00166               }
00167           }
00168       } while ( _retry );
00169 
00170       report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
00171       MIL << "provided Package " << _package << " at " << ret << endl;
00172       return ret;
00173     }
00174 
00175     ManagedFile PackageProvider::doProvidePackage() const
00176     {
00177       Url url;
00178       RepoInfo info = _package->repoInfo();
00179       // FIXME we only support the first url for now.
00180       if ( info.baseUrlsEmpty() )
00181         ZYPP_THROW(Exception("No url in repository."));
00182       else
00183         url = * info.baseUrlsBegin();
00184 
00185       // check whether to process patch/delta rpms
00186       if ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() )
00187         {
00188           std::list<DeltaRpm> deltaRpms;
00189           if ( ZConfig::instance().download_use_deltarpm() )
00190           {
00191             _deltas.deltaRpms( _package ).swap( deltaRpms );
00192           }
00193 
00194           if ( ! ( deltaRpms.empty() )
00195                && queryInstalled() )
00196             {
00197               if ( ! deltaRpms.empty() && applydeltarpm::haveApplydeltarpm() )
00198                 {
00199                   for( std::list<DeltaRpm>::const_iterator it = deltaRpms.begin();
00200                        it != deltaRpms.end(); ++it )
00201                     {
00202                       DBG << "tryDelta " << *it << endl;
00203                       ManagedFile ret( tryDelta( *it ) );
00204                       if ( ! ret->empty() )
00205                         return ret;
00206                     }
00207                 }
00208             }
00209         }
00210 
00211       // no patch/delta -> provide full package
00212       ManagedFile ret;
00213       OnMediaLocation loc = _package->location();
00214 
00215       ProvideFilePolicy policy;
00216       policy.progressCB( bind( &PackageProvider::progressPackageDownload, this, _1 ) );
00217       policy.failOnChecksumErrorCB( bind( &PackageProvider::failOnChecksumError, this ) );
00218       return _access.provideFile( _package->repoInfo(), loc, policy );
00219     }
00220 
00221     ManagedFile PackageProvider::tryDelta( const DeltaRpm & delta_r ) const
00222     {
00223       if ( delta_r.baseversion().edition() != Edition::noedition
00224            && ! queryInstalled( delta_r.baseversion().edition() ) )
00225         return ManagedFile();
00226 
00227       if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
00228         return ManagedFile();
00229 
00230       report()->startDeltaDownload( delta_r.location().filename(),
00231                                     delta_r.location().downloadSize() );
00232       ManagedFile delta;
00233       try
00234         {
00235           ProvideFilePolicy policy;
00236           policy.progressCB( bind( &PackageProvider::progressDeltaDownload, this, _1 ) );
00237           delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
00238         }
00239       catch ( const Exception & excpt )
00240         {
00241           report()->problemDeltaDownload( excpt.asUserHistory() );
00242           return ManagedFile();
00243         }
00244       report()->finishDeltaDownload();
00245 
00246       report()->startDeltaApply( delta );
00247       if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
00248         {
00249           report()->problemDeltaApply( _("applydeltarpm check failed.") );
00250           return ManagedFile();
00251         }
00252 
00253       // build the package and put it into the cache
00254       Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
00255 
00256       if ( ! applydeltarpm::provide( delta, destination,
00257                                      bind( &PackageProvider::progressDeltaApply, this, _1 ) ) )
00258         {
00259           report()->problemDeltaApply( _("applydeltarpm failed.") );
00260           return ManagedFile();
00261         }
00262       report()->finishDeltaApply();
00263 
00264       return ManagedFile( destination, filesystem::unlink );
00265     }
00266 
00267     PackageProvider::ScopedGuard PackageProvider::newReport() const
00268     {
00269       _report.reset( new Report );
00270       return shared_ptr<void>( static_cast<void*>(0),
00271                                // custom deleter calling _report.reset()
00272                                // (cast required as reset is overloaded)
00273                                bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
00274                                      ref(_report) ) );
00275     }
00276 
00277     PackageProvider::Report & PackageProvider::report() const
00278     { return *_report; }
00279 
00280     bool PackageProvider::progressDeltaDownload( int value ) const
00281     { return report()->progressDeltaDownload( value ); }
00282 
00283     void PackageProvider::progressDeltaApply( int value ) const
00284     { return report()->progressDeltaApply( value ); }
00285 
00286     bool PackageProvider::progressPackageDownload( int value ) const
00287     { return report()->progress( value, _package ); }
00288 
00289     bool PackageProvider::failOnChecksumError() const
00290     {
00291       std::string package_str = _package->name() + "-" + _package->edition().asString();
00292 
00293       // TranslatorExplanation %s = package being checked for integrity
00294       switch ( report()->problem( _package, repo::DownloadResolvableReport::INVALID, str::form(_("Package %s seems to be corrupted during transfer. Do you want to retry retrieval?"), package_str.c_str() ) ) )
00295         {
00296         case repo::DownloadResolvableReport::RETRY:
00297           _retry = true;
00298           break;
00299           case repo::DownloadResolvableReport::IGNORE:
00300           ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
00301           break;
00302           case repo::DownloadResolvableReport::ABORT:
00303           ZYPP_THROW(AbortRequestException("User requested to abort"));
00304           break;
00305         default:
00306           break;
00307         }
00308       return true; // anyway a failure
00309     }
00310 
00311     bool PackageProvider::queryInstalled( const Edition & ed_r ) const
00312     { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
00313 
00314 
00316   } // namespace repo
00319 } // namespace zypp

doxygen