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_rx,
250 const ProvideFilePolicy & policy_r )
252 const OnMediaLocation locWithPath( OnMediaLocation(loc_rx).prependPath( repo_r.path() ) );
254 MIL << locWithPath << endl;
255 // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
256 // and redirect download progress triggers to call the ProvideFilePolicy
258 DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
260 RepoException repo_excpt(repo_r,
261 str::form(_("Can't provide file '%s' from repository '%s'"),
262 locWithPath.filename().c_str(),
263 repo_r.alias().c_str() ) );
265 if ( repo_r.baseUrlsEmpty() )
267 repo_excpt.remember(RepoException(_("No url in repository.")));
268 ZYPP_THROW(repo_excpt);
272 fetcher.addCachePath( repo_r.packagesPath() );
273 MIL << "Added cache path " << repo_r.packagesPath() << endl;
275 // Test whether download destination is writable, if not
276 // switch into the tmpspace (e.g. bnc#755239, download and
277 // install srpms as user).
278 Pathname destinationDir( repo_r.packagesPath() );
280 PathInfo pi( destinationDir );
281 if ( ! pi.isExist() )
283 // try to create it...
284 assert_dir( destinationDir );
287 if ( geteuid() != 0 && ! pi.userMayW() )
289 WAR << "Destination dir '" << destinationDir << "' is not user writable, using tmp space." << endl;
290 destinationDir = getZYpp()->tmpPath() / destinationDir;
291 assert_dir( destinationDir );
292 fetcher.addCachePath( destinationDir );
293 MIL << "Added cache path " << destinationDir << endl;
296 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
297 media::ScopedDisableMediaChangeReport guard( repo_r.baseUrlsSize() > 1 );
299 for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
300 it != repo_r.baseUrlsEnd();
301 /* incremented in the loop */ )
307 MIL << "Providing file of repo '" << repo_r.alias() << "' from " << url << endl;
308 shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
310 fetcher.enqueue( locWithPath, policy_r.fileChecker() );
311 fetcher.start( destinationDir, *access );
313 // reached if no exception has been thrown, so this is the correct file
314 ManagedFile ret( destinationDir + locWithPath.filename() );
315 if ( !repo_r.keepPackages() )
317 ret.setDispose( filesystem::unlink );
320 MIL << "provideFile at " << ret << endl;
323 catch ( const UserRequestException & excpt )
325 ZYPP_RETHROW( excpt );
327 catch ( const FileCheckException & 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 ///////////////////////////////////////////////////////////////////