1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/source/RepoProvideFile.cc
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/String.h"
20 #include "zypp/base/UserRequestException.h"
21 #include "zypp/repo/RepoProvideFile.h"
22 #include "zypp/ZYppCallbacks.h"
23 #include "zypp/MediaSetAccess.h"
24 #include "zypp/ZConfig.h"
25 #include "zypp/ZYppFactory.h"
26 #include "zypp/repo/SUSEMediaVerifier.h"
27 #include "zypp/repo/RepoException.h"
29 #include "zypp/repo/SUSEMediaVerifier.h"
30 #include "zypp/repo/RepoException.h"
31 #include "zypp/FileChecker.h"
32 #include "zypp/Fetcher.h"
37 ///////////////////////////////////////////////////////////////////
39 { /////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////
42 { /////////////////////////////////////////////////////////////////
44 ///////////////////////////////////////////////////////////////////
48 ///////////////////////////////////////////////////////////////////
50 ///////////////////////////////////////////////////////////////////
52 { /////////////////////////////////////////////////////////////////
54 /** Hack to extract progress information from media::DownloadProgressReport.
55 * We redirect the static report triggered from RepoInfo::provideFile
56 * to feed the ProvideFilePolicy callbacks in addition to any connected
57 * media::DownloadProgressReport.
59 struct DownloadFileReportHack : public callback::ReceiveReport<media::DownloadProgressReport>
61 typedef callback::ReceiveReport<ReportType> BaseType;
62 typedef function<bool(int)> RedirectType;
64 DownloadFileReportHack( RedirectType redirect_r )
65 : _oldRec( Distributor::instance().getReceiver() )
66 , _redirect( redirect_r )
68 ~DownloadFileReportHack()
69 { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
71 virtual void start( const Url & file, Pathname localfile )
74 _oldRec->start( file, localfile );
76 BaseType::start( file, localfile );
79 virtual bool progress( int value, const Url & file, double dbps_avg = -1, double dbps_current = -1 )
83 ret &= _oldRec->progress( value, file, dbps_avg, dbps_current );
85 ret &= _redirect( value );
89 virtual Action problem( const Url & file, Error error, const std::string & description )
92 return _oldRec->problem( file, error, description );
93 return BaseType::problem( file, error, description );
95 virtual void finish( const Url & file, Error error, const std::string & reason )
98 _oldRec->finish( file, error, reason );
100 BaseType::finish( file, error, reason );
105 RedirectType _redirect;
108 /////////////////////////////////////////////////////////////////
110 ///////////////////////////////////////////////////////////////////
112 ManagedFile provideFile( RepoInfo repo_r,
113 const OnMediaLocation & loc_r,
114 const ProvideFilePolicy & policy_r )
116 RepoMediaAccess access;
117 return access.provideFile(repo_r, loc_r, policy_r );
120 ///////////////////////////////////////////////////////////////////
121 class RepoMediaAccess::Impl
124 Impl( const ProvideFilePolicy & defaultPolicy_r )
125 : _defaultPolicy( defaultPolicy_r )
130 std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
131 for ( it = _medias.begin();
135 it->second->release();
139 /** Provide a MediaSetAccess for \c url with label and verifyer adjusted.
141 * As the same url (e.g. \c 'dvd:///' ) might be used for multiple repos
142 * we must always adjust the repo specific data (label,verifyer).
144 * \todo This mixture of media and repos specific data is fragile.
146 shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url, RepoInfo repo )
148 std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
149 it = _medias.find(url);
150 shared_ptr<MediaSetAccess> media;
151 if ( it != _medias.end() )
157 media.reset( new MediaSetAccess(url) );
158 _medias[url] = media;
160 setVerifierForRepo( repo, media );
165 void setVerifierForRepo( RepoInfo repo, shared_ptr<MediaSetAccess> media )
167 // Always set the MediaSetAccess label.
168 media->setLabel( repo.name() );
170 // set a verifier if the repository has it
172 Pathname mediafile = repo.metadataPath() + "/media.1/media";
173 if ( ! repo.metadataPath().empty() )
175 if ( PathInfo(mediafile).isExist() )
177 std::map<shared_ptr<MediaSetAccess>, RepoInfo>::const_iterator it;
178 it = _verifier.find(media);
179 if ( it != _verifier.end() )
181 if ( it->second.alias() == repo.alias() )
183 // this media is already using this repo verifier
188 std::ifstream str(mediafile.asString().c_str());
194 getline(str, vendor);
195 getline(str, mediaid);
196 getline(str, buffer);
198 unsigned media_nr = str::strtonum<unsigned>(buffer);
199 MIL << "Repository '" << repo.alias() << "' has " << media_nr << " medias"<< endl;
201 for ( unsigned i=1; i <= media_nr; ++i )
203 media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
205 media->setVerifier( i, verifier);
207 _verifier[media] = repo;
211 ZYPP_THROW(RepoMetadataException(repo));
216 WAR << "No media verifier for repo '" << repo.alias() << "' media/media.1 does not exist in '" << repo.metadataPath() << "'" << endl;
221 WAR << "'" << repo.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
226 std::map<shared_ptr<MediaSetAccess>, RepoInfo> _verifier;
227 std::map<Url, shared_ptr<MediaSetAccess> > _medias;
230 ProvideFilePolicy _defaultPolicy;
232 ///////////////////////////////////////////////////////////////////
235 RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
236 : _impl( new Impl( defaultPolicy_r ) )
239 RepoMediaAccess::~RepoMediaAccess()
242 void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
243 { _impl->_defaultPolicy = policy_r; }
245 const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
246 { return _impl->_defaultPolicy; }
248 ManagedFile RepoMediaAccess::provideFile( RepoInfo repo_r,
249 const OnMediaLocation & loc_r,
250 const ProvideFilePolicy & policy_r )
252 MIL << loc_r << endl;
253 // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
254 // and redirect download progress triggers to call the ProvideFilePolicy
256 DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
258 RepoException repo_excpt(repo_r,
259 str::form(_("Can't provide file '%s' from repository '%s'"),
260 loc_r.filename().c_str(),
261 repo_r.alias().c_str() ) );
263 if ( repo_r.baseUrlsEmpty() )
265 repo_excpt.remember(RepoException(_("No url in repository.")));
266 ZYPP_THROW(repo_excpt);
270 fetcher.addCachePath( repo_r.packagesPath() );
271 MIL << "Added cache path " << repo_r.packagesPath() << endl;
273 // Test whether download destination is writable, if not
274 // switch into the tmpspace (e.g. bnc#755239, download and
275 // install srpms as user).
276 Pathname destinationDir( repo_r.packagesPath() );
278 PathInfo pi( destinationDir );
279 if ( ! pi.isExist() )
281 // try to create it...
282 assert_dir( destinationDir );
285 if ( geteuid() != 0 && ! pi.userMayW() )
287 WAR << "Destination dir '" << destinationDir << "' is not user writable, using tmp space." << endl;
288 destinationDir = getZYpp()->tmpPath() / destinationDir;
289 assert_dir( destinationDir );
290 fetcher.addCachePath( destinationDir );
291 MIL << "Added cache path " << destinationDir << endl;
294 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
295 media::ScopedDisableMediaChangeReport guard( repo_r.baseUrlsSize() > 1 );
297 for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
298 it != repo_r.baseUrlsEnd();
299 /* incremented in the loop */ )
305 MIL << "Providing file of repo '" << repo_r.alias() << "' from " << url << endl;
306 shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
308 fetcher.enqueue( loc_r );
309 fetcher.start( destinationDir, *access );
311 // reached if no exception has been thrown, so this is the correct file
312 ManagedFile ret( destinationDir + loc_r.filename() );
313 if ( !repo_r.keepPackages() )
315 ret.setDispose( filesystem::unlink );
318 MIL << "provideFile at " << ret << endl;
321 catch ( const UserRequestException & excpt )
323 ZYPP_CAUGHT( excpt );
324 ZYPP_RETHROW( excpt );
326 catch ( const FileCheckException & excpt )
328 ZYPP_CAUGHT( excpt );
329 ZYPP_RETHROW( excpt );
331 catch ( const Exception &e )
335 repo_excpt.remember(e);
337 WAR << "Trying next url" << endl;
340 } // iteration over urls
342 ZYPP_THROW(repo_excpt);
343 return ManagedFile(); // not reached
346 /////////////////////////////////////////////////////////////////
348 ///////////////////////////////////////////////////////////////////
349 /////////////////////////////////////////////////////////////////
351 ///////////////////////////////////////////////////////////////////