1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/repo/PackageProvider.cc
15 #include "zypp/repo/PackageDelta.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/UserRequestException.h"
19 #include "zypp/base/NonCopyable.h"
20 #include "zypp/repo/PackageProvider.h"
21 #include "zypp/repo/Applydeltarpm.h"
22 #include "zypp/repo/PackageDelta.h"
24 #include "zypp/TmpPath.h"
25 #include "zypp/ZConfig.h"
26 #include "zypp/RepoInfo.h"
27 #include "zypp/RepoManager.h"
31 ///////////////////////////////////////////////////////////////////
34 ///////////////////////////////////////////////////////////////////
37 ///////////////////////////////////////////////////////////////////
38 // class PackageProviderPolicy
39 ///////////////////////////////////////////////////////////////////
41 bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
43 const Arch & arch_r ) const
45 if ( _queryInstalledCB )
46 return _queryInstalledCB( name_r, ed_r, arch_r );
51 ///////////////////////////////////////////////////////////////////
52 /// \class PackageProvider::Impl
53 /// \brief PackageProvider implementation.
54 ///////////////////////////////////////////////////////////////////
55 class PackageProvider::Impl : private base::NonCopyable
58 /** Ctor taking the Package to provide. */
59 Impl( RepoMediaAccess & access_r,
60 const Package::constPtr & package_r,
61 const DeltaCandidates & deltas_r,
62 const PackageProviderPolicy & policy_r )
64 , _package( package_r )
72 /** Factory method providing the appropriate implementation.
73 * Called by PackageProvider ctor. Returned pointer should be
74 * immediately wrapped into a smartpointer.
76 static Impl * factoryMake( RepoMediaAccess & access_r,
77 const Package::constPtr & package_r,
78 const DeltaCandidates & deltas_r,
79 const PackageProviderPolicy & policy_r );
82 /** Provide the package.
86 ManagedFile providePackage() const;
88 /** Provide the package if it is cached. */
89 ManagedFile providePackageFromCache() const
91 ManagedFile ret( doProvidePackageFromCache() );
92 if ( ! ( ret->empty() || _package->repoInfo().keepPackages() ) )
93 ret.setDispose( filesystem::unlink );
97 /** Whether the package is cached. */
99 { return ! doProvidePackageFromCache()->empty(); }
102 typedef PackageProvider::Impl Base;
103 typedef callback::SendReport<repo::DownloadResolvableReport> Report;
105 /** Lookup the final rpm in cache.
107 * A non empty ManagedFile will be returned to the caller.
109 * \note File disposal depending on the repos keepPackages setting
110 * are not set here, but in \ref providePackage or \ref providePackageFromCache.
112 * \note The provoided default implementation returns an empty ManagedFile
115 virtual ManagedFile doProvidePackageFromCache() const = 0;
117 /** Actually provide the final rpm.
118 * Report start/problem/finish and retry loop are hadled by \ref providePackage.
119 * Here you trigger just progress and delta/plugin callbacks as needed.
121 * Proxy methods for progressPackageDownload and failOnChecksum are provided here.
122 * Create similar proxies for other progress callbacks in derived classes and link
123 * it to ProvideFilePolicy for download:
125 * ProvideFilePolicy policy;
126 * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
127 * policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
128 * return _access.provideFile( _package->repoInfo(), loc, policy );
131 * \note The provoided default implementation retrieves the packages default
134 virtual ManagedFile doProvidePackage() const = 0;
137 /** Access to the DownloadResolvableReport */
138 Report & report() const
141 /** Redirect ProvideFilePolicy package download progress to this. */
142 bool progressPackageDownload( int value ) const
143 { return report()->progress( value, _package ); }
145 /** Redirect ProvideFilePolicy failOnChecksumError to this if needed. */
146 bool failOnChecksumError() const
148 std::string package_str = _package->name() + "-" + _package->edition().asString();
150 // TranslatorExplanation %s = package being checked for integrity
151 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() ) ) )
153 case repo::DownloadResolvableReport::RETRY:
156 case repo::DownloadResolvableReport::IGNORE:
157 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
159 case repo::DownloadResolvableReport::ABORT:
160 ZYPP_THROW(AbortRequestException("User requested to abort"));
165 return true; // anyway a failure
169 PackageProviderPolicy _policy;
170 Package::constPtr _package;
171 DeltaCandidates _deltas;
172 RepoMediaAccess & _access;
175 typedef shared_ptr<void> ScopedGuard;
177 ScopedGuard newReport() const
179 _report.reset( new Report );
180 // Use a custom deleter calling _report.reset() when guard goes out of
181 // scope (cast required as reset is overloaded). We want report to end
182 // when leaving providePackage and not wait for *this going out of scope.
183 return shared_ptr<void>( static_cast<void*>(0),
184 bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
189 mutable shared_ptr<Report> _report;
191 ///////////////////////////////////////////////////////////////////
193 /** Default implementation (cache miss). */
194 ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
195 { return ManagedFile(); }
197 /** Default implementation (provide full package) */
198 ManagedFile PackageProvider::Impl::doProvidePackage() const
201 OnMediaLocation loc = _package->location();
203 ProvideFilePolicy policy;
204 policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
205 policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
206 return _access.provideFile( _package->repoInfo(), loc, policy );
209 ///////////////////////////////////////////////////////////////////
211 ManagedFile PackageProvider::Impl::providePackage() const
213 ScopedGuard guardReport( newReport() );
215 // check for cache hit:
216 ManagedFile ret( providePackageFromCache() );
217 if ( ! ret->empty() )
219 MIL << "provided Package from cache " << _package << " at " << ret << endl;
220 report()->infoInCache( _package, ret );
221 return ret; // <-- cache hit
224 // HERE: cache misss, check toplevel cache or do download:
225 RepoInfo info = _package->repoInfo();
227 // Check toplevel cache
229 RepoManagerOptions topCache;
230 if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath ) // not using toplevel cache
232 const OnMediaLocation & loc( _package->location() );
233 if ( ! loc.checksum().empty() ) // no cache hit without checksum
235 PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
236 if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
238 report()->start( _package, pi.path().asFileUrl() );
239 const Pathname & dest( info.packagesPath() / loc.filename() );
240 if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
242 ret = ManagedFile( dest );
243 if ( ! info.keepPackages() )
244 ret.setDispose( filesystem::unlink );
246 MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
247 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
248 return ret; // <-- toplevel cache hit
255 // FIXME we only support the first url for now.
256 if ( info.baseUrlsEmpty() )
257 ZYPP_THROW(Exception("No url in repository."));
259 MIL << "provide Package " << _package << endl;
260 Url url = * info.baseUrlsBegin();
263 report()->start( _package, url );
264 try // ELIMINATE try/catch by providing a log-guard
266 ret = doProvidePackage();
268 catch ( const UserRequestException & excpt )
270 // UserRequestException e.g. from failOnChecksumError was already reported.
271 ERR << "Failed to provide Package " << _package << endl;
274 ZYPP_RETHROW( excpt );
277 catch ( const Exception & excpt )
279 ERR << "Failed to provide Package " << _package << endl;
282 // Aything else gets reported
283 std::string package_str = _package->name() + "-" + _package->edition().asString();
285 // TranslatorExplanation %s = name of the package being processed.
286 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
287 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
289 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
291 case repo::DownloadResolvableReport::RETRY:
294 case repo::DownloadResolvableReport::IGNORE:
295 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
297 case repo::DownloadResolvableReport::ABORT:
298 ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
301 ZYPP_RETHROW( excpt );
308 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
309 MIL << "provided Package " << _package << " at " << ret << endl;
314 ///////////////////////////////////////////////////////////////////
315 /// \class RpmPackageProvider
316 /// \brief RPM PackageProvider implementation.
317 ///////////////////////////////////////////////////////////////////
318 class RpmPackageProvider : public PackageProvider::Impl
321 RpmPackageProvider( RepoMediaAccess & access_r,
322 const Package::constPtr & package_r,
323 const DeltaCandidates & deltas_r,
324 const PackageProviderPolicy & policy_r )
325 : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
329 virtual ManagedFile doProvidePackageFromCache() const;
331 virtual ManagedFile doProvidePackage() const;
334 typedef packagedelta::DeltaRpm DeltaRpm;
336 ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
338 bool progressDeltaDownload( int value ) const
339 { return report()->progressDeltaDownload( value ); }
341 void progressDeltaApply( int value ) const
342 { return report()->progressDeltaApply( value ); }
344 bool queryInstalled( const Edition & ed_r = Edition() ) const
345 { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
347 ///////////////////////////////////////////////////////////////////
349 ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
351 return ManagedFile( _package->cachedLocation() );
354 ManagedFile RpmPackageProvider::doProvidePackage() const
357 RepoInfo info = _package->repoInfo();
358 // FIXME we only support the first url for now.
359 if ( info.baseUrlsEmpty() )
360 ZYPP_THROW(Exception("No url in repository."));
362 url = * info.baseUrlsBegin();
364 // check whether to process patch/delta rpms
365 if ( ZConfig::instance().download_use_deltarpm()
366 && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
368 std::list<DeltaRpm> deltaRpms;
369 _deltas.deltaRpms( _package ).swap( deltaRpms );
371 if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
373 for_( it, deltaRpms.begin(), deltaRpms.end())
375 DBG << "tryDelta " << *it << endl;
376 ManagedFile ret( tryDelta( *it ) );
377 if ( ! ret->empty() )
383 // no patch/delta -> provide full package
384 return Base::doProvidePackage();
387 ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
389 if ( delta_r.baseversion().edition() != Edition::noedition
390 && ! queryInstalled( delta_r.baseversion().edition() ) )
391 return ManagedFile();
393 if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
394 return ManagedFile();
396 report()->startDeltaDownload( delta_r.location().filename(),
397 delta_r.location().downloadSize() );
401 ProvideFilePolicy policy;
402 policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
403 delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
405 catch ( const Exception & excpt )
407 report()->problemDeltaDownload( excpt.asUserHistory() );
408 return ManagedFile();
410 report()->finishDeltaDownload();
412 report()->startDeltaApply( delta );
413 if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
415 report()->problemDeltaApply( _("applydeltarpm check failed.") );
416 return ManagedFile();
419 // build the package and put it into the cache
420 Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
422 if ( ! applydeltarpm::provide( delta, destination,
423 bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
425 report()->problemDeltaApply( _("applydeltarpm failed.") );
426 return ManagedFile();
428 report()->finishDeltaApply();
430 return ManagedFile( destination, filesystem::unlink );
434 ///////////////////////////////////////////////////////////////////
435 /// \class PluginPackageProvider
436 /// \brief Plugin PackageProvider implementation.
438 /// Basically downloads the default package and calls a
439 /// 'stem'2rpm plugin to cteate the final .rpm package.
440 ///////////////////////////////////////////////////////////////////
441 class PluginPackageProvider : public PackageProvider::Impl
444 PluginPackageProvider( const std::string & stem_r,
445 RepoMediaAccess & access_r,
446 const Package::constPtr & package_r,
447 const DeltaCandidates & deltas_r,
448 const PackageProviderPolicy & policy_r )
449 : Base( access_r, package_r, deltas_r, policy_r )
453 virtual ManagedFile doProvidePackageFromCache() const
455 return Base::doProvidePackageFromCache();
458 virtual ManagedFile doProvidePackage() const
460 return Base::doProvidePackage();
463 ///////////////////////////////////////////////////////////////////
466 ///////////////////////////////////////////////////////////////////
467 // class PackageProvider
468 ///////////////////////////////////////////////////////////////////
470 PackageProvider::Impl * PackageProvider::Impl::factoryMake( RepoMediaAccess & access_r,
471 const Package::constPtr & package_r,
472 const DeltaCandidates & deltas_r,
473 const PackageProviderPolicy & policy_r )
475 return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
478 PackageProvider::PackageProvider( RepoMediaAccess & access_r,
479 const Package::constPtr & package_r,
480 const DeltaCandidates & deltas_r,
481 const PackageProviderPolicy & policy_r )
482 : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
485 PackageProvider::~PackageProvider()
488 ManagedFile PackageProvider::providePackage() const
489 { return _pimpl->providePackage(); }
491 ManagedFile PackageProvider::providePackageFromCache() const
492 { return _pimpl->providePackageFromCache(); }
494 bool PackageProvider::isCached() const
495 { return _pimpl->isCached(); }
498 ///////////////////////////////////////////////////////////////////
500 ///////////////////////////////////////////////////////////////////