1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/repo/PackageProvider.cc
14 #include "zypp/repo/PackageDelta.h"
15 #include "zypp/base/Logger.h"
16 #include "zypp/base/Gettext.h"
17 #include "zypp/base/UserRequestException.h"
18 #include "zypp/base/NonCopyable.h"
19 #include "zypp/repo/PackageProvider.h"
20 #include "zypp/repo/Applydeltarpm.h"
21 #include "zypp/repo/PackageDelta.h"
23 #include "zypp/TmpPath.h"
24 #include "zypp/ZConfig.h"
25 #include "zypp/RepoInfo.h"
29 ///////////////////////////////////////////////////////////////////
32 ///////////////////////////////////////////////////////////////////
35 ///////////////////////////////////////////////////////////////////
36 // class PackageProviderPolicy
37 ///////////////////////////////////////////////////////////////////
39 bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
41 const Arch & arch_r ) const
43 if ( _queryInstalledCB )
44 return _queryInstalledCB( name_r, ed_r, arch_r );
49 ///////////////////////////////////////////////////////////////////
50 /// \class PackageProvider::Impl
51 /// \brief PackageProvider implementation.
52 ///////////////////////////////////////////////////////////////////
53 class PackageProvider::Impl : private base::NonCopyable
56 /** Ctor taking the Package to provide. */
57 Impl( RepoMediaAccess & access_r,
58 const Package::constPtr & package_r,
59 const DeltaCandidates & deltas_r,
60 const PackageProviderPolicy & policy_r )
62 , _package( package_r )
70 /** Factory method providing the appropriate implementation.
71 * Called by PackageProvider ctor. Returned pointer should be
72 * immediately wrapped into a smartpointer.
74 static Impl * factoryMake( RepoMediaAccess & access_r,
75 const Package::constPtr & package_r,
76 const DeltaCandidates & deltas_r,
77 const PackageProviderPolicy & policy_r );
80 /** Provide the package.
84 ManagedFile providePackage() const;
86 /** Provide the package if it is cached. */
87 ManagedFile providePackageFromCache() const
89 ManagedFile ret( doProvidePackageFromCache() );
90 if ( ! ( ret->empty() || _package->repoInfo().keepPackages() ) )
91 ret.setDispose( filesystem::unlink );
95 /** Whether the package is cached. */
97 { return ! doProvidePackageFromCache()->empty(); }
100 typedef PackageProvider::Impl Base;
101 typedef callback::SendReport<repo::DownloadResolvableReport> Report;
103 /** Lookup the final rpm in cache.
105 * A non empty ManagedFile will be returned to the caller.
107 * \note File disposal depending on the repos keepPackages setting
108 * are not set here, but in \ref providePackage or \ref providePackageFromCache.
110 * \note The provoided default implementation returns an empty ManagedFile
113 virtual ManagedFile doProvidePackageFromCache() const = 0;
115 /** Actually provide the final rpm.
116 * Report start/problem/finish and retry loop are hadled by \ref providePackage.
117 * Here you trigger just progress and delta/plugin callbacks as needed.
119 * Proxy methods for progressPackageDownload and failOnChecksum are provided here.
120 * Create similar proxies for other progress callbacks in derived classes and link
121 * it to ProvideFilePolicy for download:
123 * ProvideFilePolicy policy;
124 * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
125 * policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
126 * return _access.provideFile( _package->repoInfo(), loc, policy );
129 * \note The provoided default implementation retrieves the packages default
132 virtual ManagedFile doProvidePackage() const = 0;
135 /** Access to the DownloadResolvableReport */
136 Report & report() const
139 /** Redirect ProvideFilePolicy package download progress to this. */
140 bool progressPackageDownload( int value ) const
141 { return report()->progress( value, _package ); }
143 /** Redirect ProvideFilePolicy failOnChecksumError to this if needed. */
144 bool failOnChecksumError() const
146 std::string package_str = _package->name() + "-" + _package->edition().asString();
148 // TranslatorExplanation %s = package being checked for integrity
149 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() ) ) )
151 case repo::DownloadResolvableReport::RETRY:
154 case repo::DownloadResolvableReport::IGNORE:
155 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
157 case repo::DownloadResolvableReport::ABORT:
158 ZYPP_THROW(AbortRequestException("User requested to abort"));
163 return true; // anyway a failure
167 PackageProviderPolicy _policy;
168 Package::constPtr _package;
169 DeltaCandidates _deltas;
170 RepoMediaAccess & _access;
173 typedef shared_ptr<void> ScopedGuard;
175 ScopedGuard newReport() const
177 _report.reset( new Report );
178 // Use a custom deleter calling _report.reset() when guard goes out of
179 // scope (cast required as reset is overloaded). We want report to end
180 // when leaving providePackage and not wait for *this going out of scope.
181 return shared_ptr<void>( static_cast<void*>(0),
182 bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
187 mutable shared_ptr<Report> _report;
189 ///////////////////////////////////////////////////////////////////
191 /** Default implementation (cache miss). */
192 ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
193 { return ManagedFile(); }
195 /** Default implementation (provide full package) */
196 ManagedFile PackageProvider::Impl::doProvidePackage() const
199 OnMediaLocation loc = _package->location();
201 ProvideFilePolicy policy;
202 policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
203 policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
204 return _access.provideFile( _package->repoInfo(), loc, policy );
207 ///////////////////////////////////////////////////////////////////
209 ManagedFile PackageProvider::Impl::providePackage() const
211 // check for cache hit:
212 ManagedFile ret( providePackageFromCache() );
213 if ( ! ret->empty() )
215 MIL << "provided Package from cache " << _package << " at " << ret << endl;
216 return ret; // <-- cache hit
219 // HERE: cache misss, do download:
221 RepoInfo info = _package->repoInfo();
222 // FIXME we only support the first url for now.
223 if ( info.baseUrlsEmpty() )
224 ZYPP_THROW(Exception("No url in repository."));
226 url = * info.baseUrlsBegin();
228 MIL << "provide Package " << _package << endl;
229 ScopedGuard guardReport( newReport() );
232 report()->start( _package, url );
233 try // ELIMINATE try/catch by providing a log-guard
235 ret = doProvidePackage();
237 catch ( const UserRequestException & excpt )
239 // UserRequestException e.g. from failOnChecksumError was already reported.
240 ERR << "Failed to provide Package " << _package << endl;
243 ZYPP_RETHROW( excpt );
246 catch ( const Exception & excpt )
248 ERR << "Failed to provide Package " << _package << endl;
251 // Aything else gets reported
252 std::string package_str = _package->name() + "-" + _package->edition().asString();
254 // TranslatorExplanation %s = name of the package being processed.
255 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
256 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
258 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
260 case repo::DownloadResolvableReport::RETRY:
263 case repo::DownloadResolvableReport::IGNORE:
264 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
266 case repo::DownloadResolvableReport::ABORT:
267 ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
270 ZYPP_RETHROW( excpt );
277 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
278 MIL << "provided Package " << _package << " at " << ret << endl;
283 ///////////////////////////////////////////////////////////////////
284 /// \class RpmPackageProvider
285 /// \brief RPM PackageProvider implementation.
286 ///////////////////////////////////////////////////////////////////
287 class RpmPackageProvider : public PackageProvider::Impl
290 RpmPackageProvider( RepoMediaAccess & access_r,
291 const Package::constPtr & package_r,
292 const DeltaCandidates & deltas_r,
293 const PackageProviderPolicy & policy_r )
294 : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
298 virtual ManagedFile doProvidePackageFromCache() const;
300 virtual ManagedFile doProvidePackage() const;
303 typedef packagedelta::DeltaRpm DeltaRpm;
305 ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
307 bool progressDeltaDownload( int value ) const
308 { return report()->progressDeltaDownload( value ); }
310 void progressDeltaApply( int value ) const
311 { return report()->progressDeltaApply( value ); }
313 bool queryInstalled( const Edition & ed_r = Edition() ) const
314 { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
316 ///////////////////////////////////////////////////////////////////
318 ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
320 return ManagedFile( _package->cachedLocation() );
323 ManagedFile RpmPackageProvider::doProvidePackage() const
326 RepoInfo info = _package->repoInfo();
327 // FIXME we only support the first url for now.
328 if ( info.baseUrlsEmpty() )
329 ZYPP_THROW(Exception("No url in repository."));
331 url = * info.baseUrlsBegin();
333 // check whether to process patch/delta rpms
334 if ( ZConfig::instance().download_use_deltarpm()
335 && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
337 std::list<DeltaRpm> deltaRpms;
338 _deltas.deltaRpms( _package ).swap( deltaRpms );
340 if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
342 for_( it, deltaRpms.begin(), deltaRpms.end())
344 DBG << "tryDelta " << *it << endl;
345 ManagedFile ret( tryDelta( *it ) );
346 if ( ! ret->empty() )
352 // no patch/delta -> provide full package
353 return Base::doProvidePackage();
356 ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
358 if ( delta_r.baseversion().edition() != Edition::noedition
359 && ! queryInstalled( delta_r.baseversion().edition() ) )
360 return ManagedFile();
362 if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
363 return ManagedFile();
365 report()->startDeltaDownload( delta_r.location().filename(),
366 delta_r.location().downloadSize() );
370 ProvideFilePolicy policy;
371 policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
372 delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
374 catch ( const Exception & excpt )
376 report()->problemDeltaDownload( excpt.asUserHistory() );
377 return ManagedFile();
379 report()->finishDeltaDownload();
381 report()->startDeltaApply( delta );
382 if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
384 report()->problemDeltaApply( _("applydeltarpm check failed.") );
385 return ManagedFile();
388 // build the package and put it into the cache
389 Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
391 if ( ! applydeltarpm::provide( delta, destination,
392 bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
394 report()->problemDeltaApply( _("applydeltarpm failed.") );
395 return ManagedFile();
397 report()->finishDeltaApply();
399 return ManagedFile( destination, filesystem::unlink );
403 ///////////////////////////////////////////////////////////////////
404 /// \class PluginPackageProvider
405 /// \brief Plugin PackageProvider implementation.
407 /// Basically downloads the default package and calls a
408 /// 'stem'2rpm plugin to cteate the final .rpm package.
409 ///////////////////////////////////////////////////////////////////
410 class PluginPackageProvider : public PackageProvider::Impl
413 PluginPackageProvider( const std::string & stem_r,
414 RepoMediaAccess & access_r,
415 const Package::constPtr & package_r,
416 const DeltaCandidates & deltas_r,
417 const PackageProviderPolicy & policy_r )
418 : Base( access_r, package_r, deltas_r, policy_r )
422 virtual ManagedFile doProvidePackageFromCache() const
424 return Base::doProvidePackageFromCache();
427 virtual ManagedFile doProvidePackage() const
429 return Base::doProvidePackage();
432 ///////////////////////////////////////////////////////////////////
435 ///////////////////////////////////////////////////////////////////
436 // class PackageProvider
437 ///////////////////////////////////////////////////////////////////
439 PackageProvider::Impl * PackageProvider::Impl::factoryMake( RepoMediaAccess & access_r,
440 const Package::constPtr & package_r,
441 const DeltaCandidates & deltas_r,
442 const PackageProviderPolicy & policy_r )
444 return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
447 PackageProvider::PackageProvider( RepoMediaAccess & access_r,
448 const Package::constPtr & package_r,
449 const DeltaCandidates & deltas_r,
450 const PackageProviderPolicy & policy_r )
451 : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
454 PackageProvider::~PackageProvider()
457 ManagedFile PackageProvider::providePackage() const
458 { return _pimpl->providePackage(); }
460 ManagedFile PackageProvider::providePackageFromCache() const
461 { return _pimpl->providePackageFromCache(); }
463 bool PackageProvider::isCached() const
464 { return _pimpl->isCached(); }
467 ///////////////////////////////////////////////////////////////////
469 ///////////////////////////////////////////////////////////////////