~thopiekar/zypper/libzypp-manual-import

« back to all changes in this revision

Viewing changes to zypp/repo/PackageProvider.cc

  • Committer: Thomas-Karl Pietrowski
  • Date: 2014-01-29 22:44:28 UTC
  • Revision ID: thopiekar@googlemail.com-20140129224428-gpcqnsdakby362n8
firstĀ import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*---------------------------------------------------------------------\
 
2
|                          ____ _   __ __ ___                          |
 
3
|                         |__  / \ / / . \ . \                         |
 
4
|                           / / \ V /|  _/  _/                         |
 
5
|                          / /__ | | | | | |                           |
 
6
|                         /_____||_| |_| |_|                           |
 
7
|                                                                      |
 
8
\---------------------------------------------------------------------*/
 
9
/** \file       zypp/repo/PackageProvider.cc
 
10
 *
 
11
*/
 
12
#include <iostream>
 
13
#include <sstream>
 
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"
 
22
 
 
23
#include "zypp/TmpPath.h"
 
24
#include "zypp/ZConfig.h"
 
25
#include "zypp/RepoInfo.h"
 
26
 
 
27
using std::endl;
 
28
 
 
29
///////////////////////////////////////////////////////////////////
 
30
namespace zypp
 
31
{
 
32
  ///////////////////////////////////////////////////////////////////
 
33
  namespace repo
 
34
  {
 
35
    ///////////////////////////////////////////////////////////////////
 
36
    //  class PackageProviderPolicy
 
37
    ///////////////////////////////////////////////////////////////////
 
38
 
 
39
    bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
 
40
                                                const Edition &     ed_r,
 
41
                                                const Arch &        arch_r ) const
 
42
    {
 
43
      if ( _queryInstalledCB )
 
44
        return _queryInstalledCB( name_r, ed_r, arch_r );
 
45
      return false;
 
46
    }
 
47
 
 
48
 
 
49
    ///////////////////////////////////////////////////////////////////
 
50
    /// \class PackageProvider::Impl
 
51
    /// \brief PackageProvider implementation.
 
52
    ///////////////////////////////////////////////////////////////////
 
53
    class PackageProvider::Impl : private base::NonCopyable
 
54
    {
 
55
    public:
 
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 )
 
61
      : _policy( policy_r )
 
62
      , _package( package_r )
 
63
      , _deltas( deltas_r )
 
64
      , _access( access_r )
 
65
      , _retry(false)
 
66
      {}
 
67
 
 
68
      virtual ~Impl() {}
 
69
 
 
70
      /** Factory method providing the appropriate implementation.
 
71
       * Called by PackageProvider ctor. Returned pointer should be
 
72
       * immediately wrapped into a smartpointer.
 
73
       */
 
74
      static Impl * factoryMake( RepoMediaAccess & access_r,
 
75
                                 const Package::constPtr & package_r,
 
76
                                 const DeltaCandidates & deltas_r,
 
77
                                 const PackageProviderPolicy & policy_r );
 
78
 
 
79
    public:
 
80
      /** Provide the package.
 
81
       * The basic workflow.
 
82
       * \throws Exception.
 
83
       */
 
84
      ManagedFile providePackage() const;
 
85
 
 
86
      /** Provide the package if it is cached. */
 
87
      ManagedFile providePackageFromCache() const
 
88
      {
 
89
        ManagedFile ret( doProvidePackageFromCache() );
 
90
        if ( ! ( ret->empty() ||  _package->repoInfo().keepPackages() ) )
 
91
          ret.setDispose( filesystem::unlink );
 
92
        return ret;
 
93
      }
 
94
 
 
95
      /** Whether the package is cached. */
 
96
      bool isCached() const
 
97
      { return ! doProvidePackageFromCache()->empty(); }
 
98
 
 
99
    protected:
 
100
      typedef PackageProvider::Impl     Base;
 
101
      typedef callback::SendReport<repo::DownloadResolvableReport>      Report;
 
102
 
 
103
      /** Lookup the final rpm in cache.
 
104
       *
 
105
       * A non empty ManagedFile will be returned to the caller.
 
106
       *
 
107
       * \note File disposal depending on the repos keepPackages setting
 
108
       * are not set here, but in \ref providePackage or \ref providePackageFromCache.
 
109
       *
 
110
       * \note The provoided default implementation returns an empty ManagedFile
 
111
       * (cache miss).
 
112
       */
 
113
      virtual ManagedFile doProvidePackageFromCache() const = 0;
 
114
 
 
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.
 
118
       *
 
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:
 
122
       * \code
 
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 );
 
127
       * \endcode
 
128
       *
 
129
       * \note The provoided default implementation retrieves the packages default
 
130
       * location.
 
131
       */
 
132
      virtual ManagedFile doProvidePackage() const = 0;
 
133
 
 
134
    protected:
 
135
      /** Access to the DownloadResolvableReport */
 
136
      Report & report() const
 
137
      { return *_report; }
 
138
 
 
139
      /** Redirect ProvideFilePolicy package download progress to this. */
 
140
      bool progressPackageDownload( int value ) const
 
141
      { return report()->progress( value, _package ); }
 
142
 
 
143
      /** Redirect ProvideFilePolicy failOnChecksumError to this if needed. */
 
144
      bool failOnChecksumError() const
 
145
      {
 
146
        std::string package_str = _package->name() + "-" + _package->edition().asString();
 
147
 
 
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() ) ) )
 
150
        {
 
151
          case repo::DownloadResolvableReport::RETRY:
 
152
            _retry = true;
 
153
            break;
 
154
          case repo::DownloadResolvableReport::IGNORE:
 
155
            ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
 
156
            break;
 
157
          case repo::DownloadResolvableReport::ABORT:
 
158
            ZYPP_THROW(AbortRequestException("User requested to abort"));
 
159
            break;
 
160
          default:
 
161
            break;
 
162
        }
 
163
        return true; // anyway a failure
 
164
      }
 
165
 
 
166
    protected:
 
167
      PackageProviderPolicy     _policy;
 
168
      Package::constPtr         _package;
 
169
      DeltaCandidates           _deltas;
 
170
      RepoMediaAccess &         _access;
 
171
 
 
172
    private:
 
173
      typedef shared_ptr<void>  ScopedGuard;
 
174
 
 
175
      ScopedGuard newReport() const
 
176
      {
 
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) ),
 
183
                                       ref(_report) ) );
 
184
      }
 
185
 
 
186
      mutable bool               _retry;
 
187
      mutable shared_ptr<Report> _report;
 
188
    };
 
189
    ///////////////////////////////////////////////////////////////////
 
190
 
 
191
    /** Default implementation (cache miss). */
 
192
    ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
 
193
    { return ManagedFile(); }
 
194
 
 
195
    /** Default implementation (provide full package) */
 
196
    ManagedFile PackageProvider::Impl::doProvidePackage() const
 
197
    {
 
198
      ManagedFile ret;
 
199
      OnMediaLocation loc = _package->location();
 
200
 
 
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 );
 
205
    }
 
206
 
 
207
    ///////////////////////////////////////////////////////////////////
 
208
 
 
209
    ManagedFile PackageProvider::Impl::providePackage() const
 
210
    {
 
211
      // check for cache hit:
 
212
      ManagedFile ret( providePackageFromCache() );
 
213
      if ( ! ret->empty() )
 
214
      {
 
215
        MIL << "provided Package from cache " << _package << " at " << ret << endl;
 
216
        return ret; // <-- cache hit
 
217
      }
 
218
 
 
219
      // HERE: cache misss, do download:
 
220
      Url url;
 
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."));
 
225
      else
 
226
        url = * info.baseUrlsBegin();
 
227
 
 
228
      MIL << "provide Package " << _package << endl;
 
229
      ScopedGuard guardReport( newReport() );
 
230
      do {
 
231
        _retry = false;
 
232
        report()->start( _package, url );
 
233
        try  // ELIMINATE try/catch by providing a log-guard
 
234
          {
 
235
            ret = doProvidePackage();
 
236
          }
 
237
        catch ( const UserRequestException & excpt )
 
238
          {
 
239
            // UserRequestException e.g. from failOnChecksumError was already reported.
 
240
            ERR << "Failed to provide Package " << _package << endl;
 
241
            if ( ! _retry )
 
242
              {
 
243
                ZYPP_RETHROW( excpt );
 
244
              }
 
245
          }
 
246
        catch ( const Exception & excpt )
 
247
          {
 
248
            ERR << "Failed to provide Package " << _package << endl;
 
249
            if ( ! _retry )
 
250
              {
 
251
                // Aything else gets reported
 
252
                std::string package_str = _package->name() + "-" + _package->edition().asString();
 
253
 
 
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() );
 
257
 
 
258
                switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
 
259
                {
 
260
                      case repo::DownloadResolvableReport::RETRY:
 
261
                        _retry = true;
 
262
                        break;
 
263
                      case repo::DownloadResolvableReport::IGNORE:
 
264
                        ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
 
265
                        break;
 
266
                      case repo::DownloadResolvableReport::ABORT:
 
267
                        ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
 
268
                        break;
 
269
                      default:
 
270
                        ZYPP_RETHROW( excpt );
 
271
                        break;
 
272
                }
 
273
              }
 
274
          }
 
275
      } while ( _retry );
 
276
 
 
277
      report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
 
278
      MIL << "provided Package " << _package << " at " << ret << endl;
 
279
      return ret;
 
280
    }
 
281
 
 
282
 
 
283
    ///////////////////////////////////////////////////////////////////
 
284
    /// \class RpmPackageProvider
 
285
    /// \brief RPM PackageProvider implementation.
 
286
    ///////////////////////////////////////////////////////////////////
 
287
    class RpmPackageProvider : public PackageProvider::Impl
 
288
    {
 
289
    public:
 
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 )
 
295
      {}
 
296
 
 
297
    protected:
 
298
      virtual ManagedFile doProvidePackageFromCache() const;
 
299
 
 
300
      virtual ManagedFile doProvidePackage() const;
 
301
 
 
302
    private:
 
303
      typedef packagedelta::DeltaRpm    DeltaRpm;
 
304
 
 
305
      ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
 
306
 
 
307
      bool progressDeltaDownload( int value ) const
 
308
      { return report()->progressDeltaDownload( value ); }
 
309
 
 
310
      void progressDeltaApply( int value ) const
 
311
      { return report()->progressDeltaApply( value ); }
 
312
 
 
313
      bool queryInstalled( const Edition & ed_r = Edition() ) const
 
314
      { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
 
315
    };
 
316
    ///////////////////////////////////////////////////////////////////
 
317
 
 
318
    ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
 
319
    {
 
320
      return ManagedFile( _package->cachedLocation() );
 
321
    }
 
322
 
 
323
    ManagedFile RpmPackageProvider::doProvidePackage() const
 
324
    {
 
325
      Url url;
 
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."));
 
330
      else
 
331
        url = * info.baseUrlsBegin();
 
332
 
 
333
      // check whether to process patch/delta rpms
 
334
      if ( ZConfig::instance().download_use_deltarpm()
 
335
        && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
 
336
      {
 
337
        std::list<DeltaRpm> deltaRpms;
 
338
        _deltas.deltaRpms( _package ).swap( deltaRpms );
 
339
 
 
340
        if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
 
341
        {
 
342
          for_( it, deltaRpms.begin(), deltaRpms.end())
 
343
          {
 
344
            DBG << "tryDelta " << *it << endl;
 
345
            ManagedFile ret( tryDelta( *it ) );
 
346
            if ( ! ret->empty() )
 
347
              return ret;
 
348
          }
 
349
        }
 
350
      }
 
351
 
 
352
      // no patch/delta -> provide full package
 
353
      return Base::doProvidePackage();
 
354
    }
 
355
 
 
356
    ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
 
357
    {
 
358
      if ( delta_r.baseversion().edition() != Edition::noedition
 
359
           && ! queryInstalled( delta_r.baseversion().edition() ) )
 
360
        return ManagedFile();
 
361
 
 
362
      if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
 
363
        return ManagedFile();
 
364
 
 
365
      report()->startDeltaDownload( delta_r.location().filename(),
 
366
                                    delta_r.location().downloadSize() );
 
367
      ManagedFile delta;
 
368
      try
 
369
        {
 
370
          ProvideFilePolicy policy;
 
371
          policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
 
372
          delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
 
373
        }
 
374
      catch ( const Exception & excpt )
 
375
        {
 
376
          report()->problemDeltaDownload( excpt.asUserHistory() );
 
377
          return ManagedFile();
 
378
        }
 
379
      report()->finishDeltaDownload();
 
380
 
 
381
      report()->startDeltaApply( delta );
 
382
      if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
 
383
        {
 
384
          report()->problemDeltaApply( _("applydeltarpm check failed.") );
 
385
          return ManagedFile();
 
386
        }
 
387
 
 
388
      // build the package and put it into the cache
 
389
      Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
 
390
 
 
391
      if ( ! applydeltarpm::provide( delta, destination,
 
392
                                     bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
 
393
        {
 
394
          report()->problemDeltaApply( _("applydeltarpm failed.") );
 
395
          return ManagedFile();
 
396
        }
 
397
      report()->finishDeltaApply();
 
398
 
 
399
      return ManagedFile( destination, filesystem::unlink );
 
400
    }
 
401
 
 
402
#if 0
 
403
    ///////////////////////////////////////////////////////////////////
 
404
    /// \class PluginPackageProvider
 
405
    /// \brief Plugin PackageProvider implementation.
 
406
    ///
 
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
 
411
    {
 
412
    public:
 
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 )
 
419
      {}
 
420
 
 
421
    protected:
 
422
      virtual ManagedFile doProvidePackageFromCache() const
 
423
      {
 
424
        return Base::doProvidePackageFromCache();
 
425
      }
 
426
 
 
427
      virtual ManagedFile doProvidePackage() const
 
428
      {
 
429
        return Base::doProvidePackage();
 
430
      }
 
431
    };
 
432
    ///////////////////////////////////////////////////////////////////
 
433
#endif
 
434
 
 
435
    ///////////////////////////////////////////////////////////////////
 
436
    //  class PackageProvider
 
437
    ///////////////////////////////////////////////////////////////////
 
438
 
 
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 )
 
443
    {
 
444
      return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
 
445
    }
 
446
 
 
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 ) )
 
452
    {}
 
453
 
 
454
    PackageProvider::~PackageProvider()
 
455
    {}
 
456
 
 
457
    ManagedFile PackageProvider::providePackage() const
 
458
    { return _pimpl->providePackage(); }
 
459
 
 
460
    ManagedFile PackageProvider::providePackageFromCache() const
 
461
    { return _pimpl->providePackageFromCache(); }
 
462
 
 
463
    bool PackageProvider::isCached() const
 
464
    { return _pimpl->isCached(); }
 
465
 
 
466
  } // namespace repo
 
467
  ///////////////////////////////////////////////////////////////////
 
468
} // namespace zypp
 
469
///////////////////////////////////////////////////////////////////