libzypp 17.37.17
repomanagerwf.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
9#include "repomanagerwf.h"
10
12
15#include <zypp-core/zyppng/io/Process>
16#include <zypp-core/zyppng/pipelines/MTry>
17#include <zypp-core/zyppng/pipelines/Algorithm>
18#include <zypp-media/MediaException>
19#include <zypp-media/ng/Provide>
20#include <zypp-media/ng/ProvideSpec>
21
23#include <zypp/HistoryLog.h>
24#include <zypp/base/Algorithm.h>
25#include <zypp/ng/Context>
29#include <zypp/ng/repomanager.h>
30
31#include <utility>
32#include <fstream>
33
34#undef ZYPP_BASE_LOGGER_LOGGROUP
35#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::repomanager"
36
38
39 using namespace zyppng::operators;
40
41 namespace {
42
43 template <class Executor, class OpType>
44 struct ProbeRepoLogic : public LogicBase<Executor, OpType>
45 {
46 protected:
47 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
48
49 public:
50 using ZyppContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, ContextRef, SyncContextRef >;
51 using ProvideType = typename remove_smart_ptr_t<ZyppContextRefType>::ProvideType;
52 using MediaHandle = typename ProvideType::MediaHandle;
53 using LazyMediaHandle = typename ProvideType::LazyMediaHandle;
54 using ProvideRes = typename ProvideType::Res;
55
56 ProbeRepoLogic(ZyppContextRefType zyppCtx, LazyMediaHandle &&medium, zypp::Pathname &&path, std::optional<zypp::Pathname> &&targetPath )
57 : _zyppContext(std::move(zyppCtx))
58 , _medium(std::move(medium))
59 , _path(std::move(path))
60 , _targetPath(std::move(targetPath))
61 {}
62
63 MaybeAsyncRef<expected<zypp::repo::RepoType>> execute( ) {
64 const auto &url = _medium.baseUrl();
65 MIL << "going to probe the repo type at " << url << " (" << _path << ")" << std::endl;
66
67 if ( url.getScheme() == "dir" && ! zypp::PathInfo( url.getPathName()/_path ).isDir() ) {
68 // Handle non existing local directory in advance
69 MIL << "Probed type NONE (not exists) at " << url << " (" << _path << ")" << std::endl;
71 }
72
73 // prepare exception to be thrown if the type could not be determined
74 // due to a media exception. We can't throw right away, because of some
75 // problems with proxy servers returning an incorrect error
76 // on ftp file-not-found(bnc #335906). Instead we'll check another types
77 // before throwing.
78
79 std::shared_ptr<ProvideType> providerRef = _zyppContext->provider();
80
81 // TranslatorExplanation '%s' is an URL
82 _error = zypp::repo::RepoException (zypp::str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
83
84 return providerRef->attachMediaIfNeeded( _medium )
85 | and_then([this, providerRef]( MediaHandle medium )
86 {
87 // first try rpmmd
88 return providerRef->provide( medium, _path/"repodata/repomd.xml", ProvideFileSpec().setCheckExistsOnly( !_targetPath.has_value() ).setMirrorsAllowed(false) )
89 | and_then( maybeCopyResultToDest("repodata/repomd.xml") )
91 // try susetags if rpmmd fails and remember the error
92 | or_else( [this, providerRef, medium]( std::exception_ptr err ) {
93 try {
94 std::rethrow_exception (err);
95 } catch ( const zypp::media::MediaFileNotFoundException &e ) {
96 // do nothing
97 ;
98 } catch( const zypp::media::MediaException &e ) {
99 DBG << "problem checking for repodata/repomd.xml file" << std::endl;
100 _error.remember ( err );
101 _gotMediaError = true;
102 } catch( ... ) {
103 // any other error, we give up
105 }
106 return providerRef->provide( medium, _path/"content", ProvideFileSpec().setCheckExistsOnly( !_targetPath.has_value() ).setMirrorsAllowed(false) )
107 | and_then( maybeCopyResultToDest("content") )
109 })
110 // no rpmmd and no susetags!
111 | or_else( [this, medium]( std::exception_ptr err ) {
112
113 try {
114 std::rethrow_exception (err);
115 } catch ( const zypp::media::MediaFileNotFoundException &e ) {
116 // do nothing
117 ;
118 } catch( const zypp::media::MediaException &e ) {
119 DBG << "problem checking for content file" << std::endl;
120 _error.remember ( err );
121 _gotMediaError = true;
122 } catch( zypp::Exception &e ) {
123 _error.remember(e);
124 // any other error, we give up
126 } catch(...) {
127 // any other error, we give up
129 }
130
131 const auto &url = medium.baseUrl();
132
133 // if it is a non-downloading URL denoting a directory (bsc#1191286: and no plugin)
134 if ( ! ( url.schemeIsDownloading() || url.schemeIsPlugin() ) ) {
135
136 if ( medium.localPath() && zypp::PathInfo(medium.localPath().value()/_path).isDir() ) {
137 // allow empty dirs for now
138 MIL << "Probed type RPMPLAINDIR at " << url << " (" << _path << ")" << std::endl;
140 }
141 }
142
143 if( _gotMediaError )
145
146 MIL << "Probed type NONE at " << url << " (" << _path << ")" << std::endl;
148 })
149 ;
150 });
151 }
152
153 private:
158 auto maybeCopyResultToDest ( std::string &&subPath ) {
159 return [this, subPath = std::move(subPath)]( ProvideRes file ) -> MaybeAsyncRef<expected<void>> {
160 if ( _targetPath ) {
161 MIL << "Target path is set, copying " << file.file() << " to " << *_targetPath/subPath << std::endl;
162 return std::move(file)
163 | ProvideType::copyResultToDest( _zyppContext->provider(), *_targetPath/subPath)
164 | and_then([]( zypp::ManagedFile file ){ file.resetDispose(); return expected<void>::success(); } );
165 }
167 };
168 }
169
170 private:
171 ZyppContextRefType _zyppContext;
172 LazyMediaHandle _medium;
173 zypp::Pathname _path;
174 std::optional<zypp::Pathname> _targetPath;
175
177 bool _gotMediaError = false;
178 };
179
180 template <class RefreshContextRef>
181 auto probeRepoLogic( RefreshContextRef ctx, RepoInfo repo, std::optional<zypp::Pathname> targetPath)
182 {
183 using namespace zyppng::operators;
184 return ctx->provider()->prepareMedia( repo.url(), zyppng::ProvideMediaSpec() )
185 | and_then( [ctx, path = repo.path() ]( auto &&mediaHandle ) {
186 return probeRepoType( ctx, std::forward<decltype(mediaHandle)>(mediaHandle), path );
187 });
188 }
189
190 }
191
192 AsyncOpRef<expected<zypp::repo::RepoType> > probeRepoType(ContextRef ctx, AsyncLazyMediaHandle medium, zypp::Pathname path, std::optional<zypp::Pathname> targetPath)
193 {
194 return SimpleExecutor< ProbeRepoLogic, AsyncOp<expected<zypp::repo::RepoType>> >::run( std::move(ctx), std::move(medium), std::move(path), std::move(targetPath) );
195 }
196
197 expected<zypp::repo::RepoType> probeRepoType(SyncContextRef ctx, SyncLazyMediaHandle medium, zypp::Pathname path, std::optional<zypp::Pathname> targetPath )
198 {
199 return SimpleExecutor< ProbeRepoLogic, SyncOp<expected<zypp::repo::RepoType>> >::run( std::move(ctx), std::move(medium), std::move(path), std::move(targetPath) );
200 }
201
202 AsyncOpRef<expected<zypp::repo::RepoType> > probeRepoType( ContextRef ctx, RepoInfo repo, std::optional<zypp::Pathname> targetPath )
203 {
204 return probeRepoLogic( std::move(ctx), std::move(repo), std::move(targetPath) );
205 }
206
207 expected<zypp::repo::RepoType> probeRepoType ( SyncContextRef ctx, RepoInfo repo, std::optional<zypp::Pathname> targetPath )
208 {
209 return probeRepoLogic( std::move(ctx), std::move(repo), std::move(targetPath) );
210 }
211
212
213 namespace {
214 template <class ZyppContextRef>
215 auto readRepoFileLogic( ZyppContextRef ctx, zypp::Url repoFileUrl )
216 {
217 using namespace zyppng::operators;
218 return ctx->provider()->provide( repoFileUrl, ProvideFileSpec() )
219 | and_then([repoFileUrl]( auto local ){
220 DBG << "reading repo file " << repoFileUrl << ", local path: " << local.file() << std::endl;
221 return repositories_in_file( local.file() );
222 });
223 }
224 }
225
227 {
228 return readRepoFileLogic( std::move(ctx), std::move(repoFileUrl) );
229 }
230
231 expected<std::list<RepoInfo> > readRepoFile(SyncContextRef ctx, zypp::Url repoFileUrl)
232 {
233 return readRepoFileLogic( std::move(ctx), std::move(repoFileUrl) );
234 }
235
236 namespace {
237
238 template<typename Executor, class OpType>
239 struct CheckIfToRefreshMetadataLogic : public LogicBase<Executor, OpType> {
240
241 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
242 public:
243
244 using RefreshContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, repo::AsyncRefreshContextRef, repo::SyncRefreshContextRef>;
245 using ZyppContextRefType = typename RefreshContextRefType::element_type::ContextRefType;
246 using ZyppContextType = typename RefreshContextRefType::element_type::ContextType;
247 using ProvideType = typename ZyppContextType::ProvideType;
248 using LazyMediaHandle = typename ProvideType::LazyMediaHandle;
249 using MediaHandle = typename ProvideType::MediaHandle;
250 using ProvideRes = typename ProvideType::Res;
251
252 CheckIfToRefreshMetadataLogic( RefreshContextRefType refCtx, LazyMediaHandle &&medium, ProgressObserverRef progressObserver )
253 : _refreshContext(std::move(refCtx))
254 , _progress(std::move( progressObserver ))
255 , _medium(std::move( medium ))
256 {}
257
258 MaybeAsyncRef<expected<repo::RefreshCheckStatus>> execute( ) {
259
260 MIL << "Going to CheckIfToRefreshMetadata" << std::endl;
261
262 return assert_alias( _refreshContext->repoInfo() )
263 | and_then( [this] {
264
265 const auto &info = _refreshContext->repoInfo();
266 MIL << "Check if to refresh repo " << _refreshContext->repoInfo().alias() << " at " << _medium.baseUrl() << " (" << info.type() << ")" << std::endl;
267
268 // first check old (cached) metadata
269 return zyppng::RepoManager<ZyppContextRefType>::metadataStatus( info, _refreshContext->repoManagerOptions() );
270 })
271 | and_then( [this](zypp::RepoStatus oldstatus) {
272
273 const auto &info = _refreshContext->repoInfo();
274
275 if ( oldstatus.empty() ) {
276 MIL << "No cached metadata, going to refresh" << std::endl;
278 }
279
280 if ( _medium.baseUrl().schemeIsVolatile() ) {
281 MIL << "Never refresh CD/DVD" << std::endl;
283 }
284
285 if ( _refreshContext->policy() == zypp::RepoManagerFlags::RefreshForced ) {
286 MIL << "Forced refresh!" << std::endl;
288 }
289
290 if ( _medium.baseUrl().schemeIsLocal() ) {
291 _refreshContext->setPolicy( zypp::RepoManagerFlags::RefreshIfNeededIgnoreDelay );
292 }
293
294 // Check whether repo.refresh.delay applies...
295 if ( _refreshContext->policy() != zypp::RepoManagerFlags::RefreshIfNeededIgnoreDelay )
296 {
297 // bsc#1174016: Prerequisite to skipping the refresh is that metadata
298 // and solv cache status match. They will not, if the repos URL was
299 // changed e.g. due to changed repovars.
300 expected<zypp::RepoStatus> cachestatus = zyppng::RepoManager<ZyppContextRefType>::cacheStatus( info, _refreshContext->repoManagerOptions() );
301 if ( !cachestatus ) return makeReadyResult( expected<repo::RefreshCheckStatus>::error(cachestatus.error()) );
302
303 if ( oldstatus == *cachestatus ) {
304 // difference in seconds
305 double diff = ::difftime( (zypp::Date::ValueType)zypp::Date::now(), (zypp::Date::ValueType)oldstatus.timestamp() ) / 60;
306 const auto refDelay = _refreshContext->zyppContext()->config().repo_refresh_delay();
307 if ( diff < refDelay ) {
308 if ( diff < 0 ) {
309 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << std::endl;
310 }
311 else {
312 MIL << "Repository '" << info.alias()
313 << "' has been refreshed less than repo.refresh.delay ("
314 << refDelay
315 << ") minutes ago. Advising to skip refresh" << std::endl;
317 }
318 }
319 }
320 else {
321 MIL << "Metadata and solv cache don't match. Check data on server..." << std::endl;
322 }
323 }
324
325 return info.type() | [this]( zypp::repo::RepoType repokind ) {
326 // if unknown: probe it
327 if ( repokind == zypp::repo::RepoType::NONE )
328 return probeRepoType( _refreshContext->zyppContext(), _medium, _refreshContext->repoInfo().path()/*, _refreshContext->targetDir()*/ );
330 } | and_then([this, oldstatus]( zypp::repo::RepoType repokind ) {
331
332 // make sure to remember the repo type
333 _refreshContext->repoInfo().setProbedType( repokind );
334
335 auto dlContext = std::make_shared<repo::DownloadContext<ZyppContextRefType>>( _refreshContext->zyppContext(), _refreshContext->repoInfo(), _refreshContext->targetDir() );
336 return RepoDownloaderWorkflow::repoStatus ( dlContext, _medium )
337 | and_then( [this, dlContext, oldstatus]( zypp::RepoStatus newstatus ){
338 // check status
339 if ( oldstatus == newstatus ) {
340 MIL << "repo has not changed" << std::endl;
341 return zyppng::RepoManager<ZyppContextRefType>::touchIndexFile( _refreshContext->repoInfo(), _refreshContext->repoManagerOptions() )
343 }
344 else { // includes newstatus.empty() if e.g. repo format changed
345 MIL << "repo has changed, going to refresh" << std::endl;
346 MIL << "Old status: " << oldstatus << " New Status: " << newstatus << std::endl;
348 }
349 });
350 });
351 });
352 }
353
354 protected:
355 RefreshContextRefType _refreshContext;
356 ProgressObserverRef _progress;
357 LazyMediaHandle _medium;
358 };
359 }
360
361 AsyncOpRef<expected<repo::RefreshCheckStatus> > checkIfToRefreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle<Provide> medium, ProgressObserverRef progressObserver)
362 {
363 return SimpleExecutor< CheckIfToRefreshMetadataLogic , AsyncOp<expected<repo::RefreshCheckStatus>> >::run( std::move(refCtx), std::move(medium), std::move(progressObserver) );
364 }
365
366 expected<repo::RefreshCheckStatus> checkIfToRefreshMetadata(repo::SyncRefreshContextRef refCtx, LazyMediaHandle<MediaSyncFacade> medium, ProgressObserverRef progressObserver)
367 {
368 return SimpleExecutor< CheckIfToRefreshMetadataLogic , SyncOp<expected<repo::RefreshCheckStatus>> >::run( std::move(refCtx), std::move(medium), std::move(progressObserver) );
369 }
370
371
372 namespace {
373
374 template<typename Executor, class OpType>
375 struct RefreshMetadataLogic : public LogicBase<Executor, OpType>{
376
377 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
378
379 public:
380
381 using RefreshContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, repo::AsyncRefreshContextRef, repo::SyncRefreshContextRef>;
382 using ZyppContextRefType = typename RefreshContextRefType::element_type::ContextRefType;
383 using ZyppContextType = typename RefreshContextRefType::element_type::ContextType;
384 using ProvideType = typename ZyppContextType::ProvideType;
385 using MediaHandle = typename ProvideType::MediaHandle;
386 using LazyMediaHandle = typename ProvideType::LazyMediaHandle;
387 using ProvideRes = typename ProvideType::Res;
388
389 using DlContextType = repo::DownloadContext<ZyppContextRefType>;
390 using DlContextRefType = std::shared_ptr<DlContextType>;
391
392 RefreshMetadataLogic( RefreshContextRefType refCtx, LazyMediaHandle &&medium, ProgressObserverRef progressObserver )
393 : _refreshContext(std::move(refCtx))
394 , _progress ( std::move( progressObserver ) )
395 , _medium ( std::move( medium ) )
396 { }
397
398 MaybeAsyncRef<expected<RefreshContextRefType>> execute() {
399
400 return assert_alias( _refreshContext->repoInfo() )
401 | and_then( [this](){ return assert_urls( _refreshContext->repoInfo() ); })
402 | and_then( [this](){ return checkIfToRefreshMetadata ( _refreshContext, _medium, _progress ); })
403 | and_then( [this]( repo::RefreshCheckStatus status ){
404
405 MIL << "RefreshCheckStatus returned: " << status << std::endl;
406
407 // check whether to refresh metadata
408 // if the check fails for this url, it throws, so another url will be checked
410 return makeReadyResult ( expected<RefreshContextRefType>::success( std::move(_refreshContext) ) );
411
412 // if REFRESH_NEEDED but we don't have the permission to write the cache, stop here.
413 if ( zypp::IamNotRoot() && not zypp::PathInfo(_refreshContext->rawCachePath().dirname()).userMayWX() ) {
414 WAR << "No permision to write cache " << zypp::PathInfo(_refreshContext->rawCachePath().dirname()) << std::endl;
415 auto exception = ZYPP_EXCPT_PTR( zypp::repo::RepoNoPermissionException( _refreshContext->repoInfo() ) );
416 return makeReadyResult( expected<RefreshContextRefType>::error( std::move(exception) ) );
417 }
418
419 MIL << "Going to refresh metadata from " << _medium.baseUrl() << std::endl;
420
421 // bsc#1048315: Always re-probe in case of repo format change.
422 // TODO: Would be sufficient to verify the type and re-probe
423 // if verification failed (or type is RepoType::NONE)
424 return probeRepoType ( _refreshContext->zyppContext(), _medium, _refreshContext->repoInfo().path() /*, _refreshContext->targetDir()*/ )
425 | and_then([this]( zypp::repo::RepoType repokind ) {
426
427 auto &info = _refreshContext->repoInfo();
428
429 if ( info.type() != repokind ) {
430 _refreshContext->setProbedType( repokind );
431 // Adjust the probed type in RepoInfo
432 info.setProbedType( repokind ); // lazy init!
433 }
434
435 // no need to continue with an unknown type
436 if ( repokind.toEnum() == zypp::repo::RepoType::NONE_e )
437 return makeReadyResult( expected<DlContextRefType>::error( ZYPP_EXCPT_PTR ( zypp::repo::RepoUnknownTypeException( info ))) );
438
439 const zypp::Pathname &mediarootpath = _refreshContext->rawCachePath();
440 if( zypp::filesystem::assert_dir(mediarootpath) ) {
441 auto exception = ZYPP_EXCPT_PTR (zypp::Exception(zypp::str::form( _("Can't create %s"), mediarootpath.c_str() )));
442 return makeReadyResult( expected<DlContextRefType>::error( std::move(exception) ));
443 }
444
445 auto dlContext = std::make_shared<DlContextType>( _refreshContext->zyppContext(), _refreshContext->repoInfo(), _refreshContext->targetDir() );
446 dlContext->setPluginRepoverification( _refreshContext->pluginRepoverification() );
447
448 return RepoDownloaderWorkflow::download ( dlContext, _medium, _progress );
449
450 })
451 | and_then([this]( DlContextRefType && ) {
452
453 // ok we have the metadata, now exchange
454 // the contents
455 _refreshContext->saveToRawCache();
456 // if ( ! isTmpRepo( info ) )
457 // reposManip(); // remember to trigger appdata refresh
458
459 // we are done.
460 return expected<RefreshContextRefType>::success( std::move(_refreshContext) );
461 });
462 });
463 }
464
465 RefreshContextRefType _refreshContext;
466 ProgressObserverRef _progress;
467 LazyMediaHandle _medium;
468 zypp::Pathname _mediarootpath;
469
470 };
471 }
472
473 AsyncOpRef<expected<repo::AsyncRefreshContextRef> > refreshMetadata( repo::AsyncRefreshContextRef refCtx, LazyMediaHandle<Provide> medium, ProgressObserverRef progressObserver )
474 {
475 return SimpleExecutor<RefreshMetadataLogic, AsyncOp<expected<repo::AsyncRefreshContextRef>>>::run( std::move(refCtx), std::move(medium), std::move(progressObserver));
476 }
477
478 expected<repo::SyncRefreshContextRef> refreshMetadata( repo::SyncRefreshContextRef refCtx, LazyMediaHandle<MediaSyncFacade> medium, ProgressObserverRef progressObserver )
479 {
480 return SimpleExecutor<RefreshMetadataLogic, SyncOp<expected<repo::SyncRefreshContextRef>>>::run( std::move(refCtx), std::move(medium), std::move(progressObserver));
481 }
482
483 namespace {
484 template <class RefreshContextRef>
485 auto refreshMetadataLogic( RefreshContextRef refCtx, ProgressObserverRef progressObserver)
486 {
487 // small shared helper struct to pass around the exception and to remember that we tried the first URL
488 struct ExHelper
489 {
490 // We will throw this later if no URL checks out fine.
491 // The first exception will be remembered, further exceptions just added to the history.
492 ExHelper( const RepoInfo & info_r )
493 : rexception { info_r, _("Failed to retrieve new repository metadata.") }
494 {}
495 void remember( const zypp::Exception & old_r )
496 {
497 if ( rexception.historyEmpty() ) {
498 rexception.remember( old_r );
499 } else {
500 rexception.addHistory( old_r.asUserString() );
501 }
502 }
503 zypp::repo::RepoException rexception;
504 };
505
506 auto helper = std::make_shared<ExHelper>( ExHelper{ refCtx->repoInfo() } );
507
508 // the actual logic pipeline, attaches the medium and tries to refresh from it
509 auto refreshPipeline = [ refCtx, progressObserver ]( zypp::MirroredOrigin origin ){
510 return refCtx->zyppContext()->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() )
511 | and_then( [ refCtx , progressObserver]( auto mediaHandle ) mutable { return refreshMetadata ( std::move(refCtx), std::move(mediaHandle), progressObserver ); } );
512 };
513
514 // predicate that accepts only valid results, and in addition collects all errors in rexception
515 auto predicate = [ info = refCtx->repoInfo(), helper ]( const expected<RefreshContextRef> &res ) -> bool{
516 if ( !res ) {
517 try {
518 ZYPP_RETHROW( res.error() );
519 } catch ( const zypp::repo::RepoNoPermissionException &e ) {
520 // We deliver the Exception caught here (no permission to write chache) and give up.
521 ERR << "Giving up..." << std::endl;
522 helper->remember( e );
523 return true; // stop processing
524 } catch ( const zypp::Exception &e ) {
525 ERR << "Trying another url..." << std::endl;
526 helper->remember( e );
527 }
528 return false;
529 }
530 return true;
531 };
532
533
534 // now go over the url groups until we find one that works
535 return refCtx->repoInfo().repoOrigins()
536 | firstOf( std::move(refreshPipeline), expected<RefreshContextRef>::error( std::make_exception_ptr(NotFoundException()) ), std::move(predicate) )
537 | [helper]( expected<RefreshContextRef> result ) {
538 if ( !result ) {
539 // none of the URLs worked
540 ERR << "No more urls..." << std::endl;
541 return expected<RefreshContextRef>::error( ZYPP_EXCPT_PTR(helper->rexception) );
542 }
543 // we are done.
544 return result;
545 };
546 }
547 }
548
549 AsyncOpRef<expected<repo::AsyncRefreshContextRef> > refreshMetadata( repo::AsyncRefreshContextRef refCtx, ProgressObserverRef progressObserver) {
550 return refreshMetadataLogic ( std::move(refCtx), std::move(progressObserver) );
551 }
552
553 expected<repo::SyncRefreshContextRef> refreshMetadata( repo::SyncRefreshContextRef refCtx, ProgressObserverRef progressObserver) {
554 return refreshMetadataLogic ( std::move(refCtx), std::move(progressObserver) );
555 }
556
557
558 namespace {
559
560 template <typename ZyppCtxRef> struct Repo2SolvOp;
561
562 template <>
563 struct Repo2SolvOp<ContextRef> : public AsyncOp<expected<void>>
564 {
565 Repo2SolvOp() { }
566
568 MIL << "Starting repo2solv for repo " << repo.alias () << std::endl;
569 auto me = std::make_shared<Repo2SolvOp<ContextRef>>();
570 me->_repo = std::move(repo);
571 me->_proc = Process::create();
572 me->_proc->connect( &Process::sigFinished, *me, &Repo2SolvOp<ContextRef>::procFinished );
573 me->_proc->connect( &Process::sigReadyRead, *me, &Repo2SolvOp<ContextRef>::readyRead );
574
575 std::vector<const char *> argsIn;
576 argsIn.reserve ( args.size() );
577 std::for_each( args.begin (), args.end(), [&]( const std::string &s ) { argsIn.push_back(s.data()); });
578 argsIn.push_back (nullptr);
579 me->_proc->setOutputChannelMode ( Process::Merged );
580 if (!me->_proc->start( argsIn.data() )) {
581 return makeReadyResult( expected<void>::error(ZYPP_EXCPT_PTR(zypp::repo::RepoException ( me->_repo, _("Failed to cache repo ( unable to start repo2solv ).") ))) );
582 }
583 return me;
584 }
585
586 void readyRead (){
587 const ByteArray &data = _proc->readLine();
588 const std::string &line = data.asString();
589 WAR << " " << line;
590 _errdetail += line;
591 }
592
593 void procFinished( int ret ) {
594
595 while ( _proc->canReadLine() )
596 readyRead();
597
598 if ( ret != 0 ) {
599 zypp::repo::RepoException ex( _repo, zypp::str::form( _("Failed to cache repo (%d)."), ret ));
600 ex.addHistory( zypp::str::Str() << _proc->executedCommand() << std::endl << _errdetail << _proc->execError() ); // errdetail lines are NL-terminaled!
601 setReady( expected<void>::error(ZYPP_EXCPT_PTR(ex)) );
602 return;
603 }
604 setReady( expected<void>::success() );
605 }
606
607 private:
608 ProcessRef _proc;
609 zypp::RepoInfo _repo;
610 std::string _errdetail;
611 };
612
613 template <>
614 struct Repo2SolvOp<SyncContextRef>
615 {
616 static expected<void> run( zypp::RepoInfo repo, zypp::ExternalProgram::Arguments args ) {
617 zypp::ExternalProgram prog( args, zypp::ExternalProgram::Stderr_To_Stdout );
618 std::string errdetail;
619
620 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
621 WAR << " " << output;
622 errdetail += output;
623 }
624
625 int ret = prog.close();
626 if ( ret != 0 )
627 {
628 zypp::repo::RepoException ex(repo, zypp::str::form( _("Failed to cache repo (%d)."), ret ));
629 ex.addHistory( zypp::str::Str() << prog.command() << std::endl << errdetail << prog.execError() ); // errdetail lines are NL-terminaled!
631 }
633 }
634 };
635
636 template<typename Executor, class OpType>
637 struct BuildCacheLogic : public LogicBase<Executor, OpType>{
638
639 using RefreshContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, repo::AsyncRefreshContextRef, repo::SyncRefreshContextRef>;
640 using ZyppContextRefType = typename RefreshContextRefType::element_type::ContextRefType;
641 using ZyppContextType = typename RefreshContextRefType::element_type::ContextType;
642 using ProvideType = typename ZyppContextType::ProvideType;
643 using MediaHandle = typename ProvideType::MediaHandle;
644 using ProvideRes = typename ProvideType::Res;
645
646 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
647
648 BuildCacheLogic( RefreshContextRefType &&refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef &&progressObserver )
649 : _refCtx( std::move(refCtx) )
650 , _policy( policy )
651 , _progressObserver( std::move(progressObserver) )
652 {}
653
654 MaybeAsyncRef<expected<RefreshContextRefType>> execute() {
655
656 ProgressObserver::setup ( _progressObserver, zypp::str::form(_("Building repository '%s' cache"), _refCtx->repoInfo().label().c_str()), 100 );
657
658 return assert_alias(_refCtx->repoInfo() )
659 | and_then( mtry( [this] {
660 _mediarootpath = rawcache_path_for_repoinfo( _refCtx->repoManagerOptions(), _refCtx->repoInfo() ).unwrap();
661 _productdatapath = rawproductdata_path_for_repoinfo( _refCtx->repoManagerOptions(), _refCtx->repoInfo() ).unwrap();
662 }))
663 | and_then( [this] {
664
665 const auto &options = _refCtx->repoManagerOptions();
666
667 if( zypp::filesystem::assert_dir( options.repoCachePath ) ) {
668 auto ex = ZYPP_EXCPT_PTR( zypp::Exception (zypp::str::form( _("Can't create %s"), options.repoCachePath.c_str()) ) );
669 return expected<RepoStatus>::error( std::move(ex) );
670 }
671
672 return RepoManager<ZyppContextRefType>::metadataStatus( _refCtx->repoInfo(), options );
673
674 }) | and_then( [this](RepoStatus raw_metadata_status ) {
675
676 if ( raw_metadata_status.empty() )
677 {
678 // If there is no raw cache at this point, we refresh the raw metadata.
679 // This may happen if no autorefresh is configured and no explicit
680 // refresh was called.
681 //
682 zypp::Pathname mediarootParent { _mediarootpath.dirname() };
683
684 if ( zypp::filesystem::assert_dir( mediarootParent ) == 0
685 && ( zypp::IamRoot() || zypp::PathInfo(mediarootParent).userMayWX() ) ) {
686
687 return refreshMetadata( _refCtx, ProgressObserver::makeSubTask( _progressObserver ) )
688 | and_then([this]( auto /*refCtx*/) { return RepoManager<ZyppContextRefType>::metadataStatus( _refCtx->repoInfo(), _refCtx->repoManagerOptions() ); } );
689
690 } else {
691 // Non-root user is not allowed to write the raw cache.
692 WAR << "No permission to write raw cache " << mediarootParent << std::endl;
693 auto exception = ZYPP_EXCPT_PTR( zypp::repo::RepoNoPermissionException( _refCtx->repoInfo() ) );
694 return makeReadyResult( expected<zypp::RepoStatus>::error( std::move(exception) ) );
695 }
696 }
697 return makeReadyResult( make_expected_success (raw_metadata_status) );
698
699 }) | and_then( [this]( RepoStatus raw_metadata_status ) {
700
701 bool needs_cleaning = false;
702 const auto &info = _refCtx->repoInfo();
703 if ( _refCtx->repoManager()->isCached( info ) )
704 {
705 MIL << info.alias() << " is already cached." << std::endl;
706 expected<RepoStatus> cache_status = RepoManager<ZyppContextRefType>::cacheStatus( info, _refCtx->repoManagerOptions() );
707 if ( !cache_status )
708 return makeReadyResult( expected<void>::error(cache_status.error()) );
709
710 if ( *cache_status == raw_metadata_status )
711 {
712 MIL << info.alias() << " cache is up to date with metadata." << std::endl;
714 {
715 // On the fly add missing solv.idx files for bash completion.
716 return makeReadyResult(
717 solv_path_for_repoinfo( _refCtx->repoManagerOptions(), info)
718 | and_then([this]( zypp::Pathname base ){
719 if ( ! zypp::PathInfo(base/"solv.idx").isExist() )
720 return mtry( zypp::sat::updateSolvFileIndex, base/"solv" );
721 return expected<void>::success ();
722 })
723 );
724 }
725 else {
726 MIL << info.alias() << " cache rebuild is forced" << std::endl;
727 }
728 }
729
730 needs_cleaning = true;
731 }
732
733 ProgressObserver::start( _progressObserver );
734
735 if (needs_cleaning)
736 {
737 auto r = _refCtx->repoManager()->cleanCache(info);
738 if ( !r )
739 return makeReadyResult( expected<void>::error(r.error()) );
740 }
741
742 MIL << info.alias() << " building cache..." << info.type() << std::endl;
743
744 expected<zypp::Pathname> base = solv_path_for_repoinfo( _refCtx->repoManagerOptions(), info);
745 if ( !base )
747
749 {
750 zypp::Exception ex(zypp::str::form( _("Can't create %s"), base->c_str()) );
752 }
753
754 if( zypp::IamNotRoot() && not zypp::PathInfo(*base).userMayW() )
755 {
756 zypp::Exception ex(zypp::str::form( _("Can't create cache at %s - no writing permissions."), base->c_str()) );
758 }
759
760 zypp::Pathname solvfile = *base / "solv";
761
762 // do we have type?
763 zypp::repo::RepoType repokind = info.type();
764
765 // if the type is unknown, try probing.
766 switch ( repokind.toEnum() )
767 {
769 // unknown, probe the local metadata
770 repokind = RepoManager<ZyppContextRefType>::probeCache( _productdatapath );
771 break;
772 default:
773 break;
774 }
775
776 MIL << "repo type is " << repokind << std::endl;
777
778 return mountIfRequired( repokind, info )
779 | and_then([this, repokind, solvfile = std::move(solvfile) ]( std::optional<MediaHandle> forPlainDirs ) mutable {
780
781 const auto &info = _refCtx->repoInfo();
782
783 switch ( repokind.toEnum() )
784 {
788 {
789 // Take care we unlink the solvfile on error
791
793#ifdef ZYPP_REPO2SOLV_PATH
794 cmd.push_back( ZYPP_REPO2SOLV_PATH );
795#else
796 cmd.push_back( zypp::PathInfo( "/usr/bin/repo2solv" ).isFile() ? "repo2solv" : "repo2solv.sh" );
797#endif
798 // repo2solv expects -o as 1st arg!
799 cmd.push_back( "-o" );
800 cmd.push_back( solvfile.asString() );
801 cmd.push_back( "-X" ); // autogenerate pattern from pattern-package
802 // bsc#1104415: no more application support // cmd.push_back( "-A" ); // autogenerate application pseudo packages
803
804 if ( repokind == zypp::repo::RepoType::RPMPLAINDIR )
805 {
806 // recusive for plaindir as 2nd arg!
807 cmd.push_back( "-R" );
808
809 std::optional<zypp::Pathname> localPath = forPlainDirs.has_value() ? forPlainDirs->localPath() : zypp::Pathname();
810 if ( !localPath )
811 return makeReadyResult( expected<void>::error( ZYPP_EXCPT_PTR( zypp::repo::RepoException( zypp::str::Format(_("Failed to cache repo %1%")) % _refCtx->repoInfo() ))) );
812
813 // FIXME this does only work for dir: URLs
814 cmd.push_back( (*localPath / info.path().absolutename()).c_str() );
815 }
816 else
817 cmd.push_back( _productdatapath.asString() );
818
819 return Repo2SolvOp<ZyppContextRefType>::run( info, std::move(cmd) )
820 | and_then( [this, guard = std::move(guard), solvfile = std::move(solvfile) ]() mutable {
821 // We keep it.
822 guard.resetDispose();
823 return mtry( zypp::sat::updateSolvFileIndex, solvfile ); // content digest for zypper bash completion
824 });
825 }
826 break;
827 default:
828 return makeReadyResult( expected<void>::error( ZYPP_EXCPT_PTR(zypp::repo::RepoUnknownTypeException( info, _("Unhandled repository type") )) ) );
829 break;
830 }
831 })
832 | and_then([this, raw_metadata_status](){
833 // update timestamp and checksum
834 return _refCtx->repoManager()->setCacheStatus( _refCtx->repoInfo(), raw_metadata_status );
835 });
836 })
837 | and_then( [this](){
838 MIL << "Commit cache.." << std::endl;
840 return make_expected_success ( _refCtx );
841
842 })
843 | or_else ( [this]( std::exception_ptr e ) {
846 });
847 }
848
849 private:
850 MaybeAsyncRef<expected<std::optional<MediaHandle>>> mountIfRequired ( zypp::repo::RepoType repokind, zypp::RepoInfo info ) {
851 if ( repokind != zypp::repo::RepoType::RPMPLAINDIR )
852 return makeReadyResult( make_expected_success( std::optional<MediaHandle>() ));
853
854 return _refCtx->zyppContext()->provider()->attachMedia( info.url(), ProvideMediaSpec() )
855 | and_then( [this]( MediaHandle handle ) {
856 return makeReadyResult( make_expected_success( std::optional<MediaHandle>( std::move(handle)) ));
857 });
858 }
859
860 private:
861 RefreshContextRefType _refCtx;
863 ProgressObserverRef _progressObserver;
864
865 zypp::Pathname _mediarootpath;
866 zypp::Pathname _productdatapath;
867 };
868 }
869
870 AsyncOpRef<expected<repo::AsyncRefreshContextRef> > buildCache(repo::AsyncRefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
871 {
872 return SimpleExecutor<BuildCacheLogic, AsyncOp<expected<repo::AsyncRefreshContextRef>>>::run( std::move(refCtx), policy, std::move(progressObserver));
873 }
874
875 expected<repo::SyncRefreshContextRef> buildCache(repo::SyncRefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
876 {
877 return SimpleExecutor<BuildCacheLogic, SyncOp<expected<repo::SyncRefreshContextRef>>>::run( std::move(refCtx), policy, std::move(progressObserver));
878 }
879
880
881 // Add repository logic
882 namespace {
883
884 template<typename Executor, class OpType>
885 struct AddRepoLogic : public LogicBase<Executor, OpType>{
886
888
889 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
890
891 AddRepoLogic( RepoManagerPtrType &&repoMgrRef, RepoInfo &&info, ProgressObserverRef &&myProgress, const zypp::TriBool & forcedProbe )
892 : _repoMgrRef( std::move(repoMgrRef) )
893 , _doProbeUrl( zypp::indeterminate(forcedProbe) ? _repoMgrRef->options().probe : bool(forcedProbe) )
894 , _info( std::move(info) )
895 , _myProgress ( std::move(myProgress) )
896 {}
897
898 MaybeAsyncRef<expected<RepoInfo> > execute() {
899 using namespace zyppng::operators;
900
901 return assert_alias(_info)
902 | and_then([this]( ) {
903
904 MIL << "Try adding repo " << _info << std::endl;
905 ProgressObserver::setup( _myProgress, zypp::str::form(_("Adding repository '%s'"), _info.label().c_str()) );
906 ProgressObserver::start( _myProgress );
907
908 if ( _repoMgrRef->repos().find(_info) != _repoMgrRef->repos().end() )
910
911 // check the first url for now
912 if ( _doProbeUrl )
913 {
914 DBG << "unknown repository type, probing" << std::endl;
915 return assert_urls(_info)
916 | and_then([this]{ return probeRepoType( _repoMgrRef->zyppContext(), _info ); })
917 | and_then([this]( zypp::repo::RepoType probedtype ) {
918
919 if ( probedtype == zypp::repo::RepoType::NONE )
920 return expected<RepoInfo>::error( ZYPP_EXCPT_PTR(zypp::repo::RepoUnknownTypeException(_info)) );
921
922 RepoInfo tosave = _info;
923 tosave.setType(probedtype);
924 return make_expected_success(tosave);
925 });
926 }
927 return makeReadyResult( make_expected_success(_info) );
928 })
929 | inspect( operators::setProgress( _myProgress, 50 ) )
930 | and_then( [this]( RepoInfo tosave ){ return _repoMgrRef->addProbedRepository( tosave, tosave.type() ); })
931 | and_then( [this]( RepoInfo updated ) {
932 ProgressObserver::finish( _myProgress );
933 MIL << "done" << std::endl;
934 return expected<RepoInfo>::success( updated );
935 })
936 | or_else( [this]( std::exception_ptr e) {
938 MIL << "done" << std::endl;
940 })
941 ;
942 }
943
944 RepoManagerPtrType _repoMgrRef;
945 bool _doProbeUrl;
946 RepoInfo _info;
947 ProgressObserverRef _myProgress;
948 };
949 };
950
951 AsyncOpRef<expected<RepoInfo> > addRepository( AsyncRepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress, const zypp::TriBool & forcedProbe )
952 {
953 return SimpleExecutor<AddRepoLogic, AsyncOp<expected<RepoInfo>>>::run( std::move(mgr), std::move(info), std::move(myProgress), forcedProbe );
954 }
955
956 expected<RepoInfo> addRepository( SyncRepoManagerRef mgr, const RepoInfo &info, ProgressObserverRef myProgress, const zypp::TriBool & forcedProbe )
957 {
958 return SimpleExecutor<AddRepoLogic, SyncOp<expected<RepoInfo>>>::run( std::move(mgr), RepoInfo(info), std::move(myProgress), forcedProbe );
959 }
960
961 namespace {
962
963 template<typename Executor, class OpType>
964 struct AddReposLogic : public LogicBase<Executor, OpType>{
965
967 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
968
969 AddReposLogic( RepoManagerPtrType &&repoMgrRef, zypp::Url &&url, ProgressObserverRef &&myProgress )
970 : _repoMgrRef( std::move(repoMgrRef) )
971 , _url( std::move(url) )
972 , _myProgress ( std::move(myProgress) )
973 {}
974
975 MaybeAsyncRef<expected<void>> execute() {
976 using namespace zyppng::operators;
977
979 | and_then([this]( zypp::Url repoFileUrl ) { return readRepoFile( _repoMgrRef->zyppContext(), std::move(repoFileUrl) ); } )
980 | and_then([this]( std::list<RepoInfo> repos ) {
981
982 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
983 it != repos.end();
984 ++it )
985 {
986 // look if the alias is in the known repos.
987 for_ ( kit, _repoMgrRef->repoBegin(), _repoMgrRef->repoEnd() )
988 {
989 if ( (*it).alias() == (*kit).alias() )
990 {
991 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << std::endl;
992 return expected<void>::error(ZYPP_EXCPT_PTR(zypp::repo::RepoAlreadyExistsException(*it)));
993 }
994 }
995 }
996
997 std::string filename = zypp::Pathname(_url.getPathName()).basename();
998 if ( filename == zypp::Pathname() )
999 {
1000 // TranslatorExplanation '%s' is an URL
1001 return expected<void>::error(ZYPP_EXCPT_PTR(zypp::repo::RepoException(zypp::str::form( _("Invalid repo file name at '%s'"), _url.asString().c_str() ))));
1002 }
1003
1004 const auto &options = _repoMgrRef->options();
1005
1006 // assert the directory exists
1007 zypp::filesystem::assert_dir( options.knownReposPath );
1008
1009 zypp::Pathname repofile = _repoMgrRef->generateNonExistingName( options.knownReposPath, filename );
1010 // now we have a filename that does not exists
1011 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << std::endl;
1012
1013 std::ofstream file(repofile.c_str());
1014 if (!file)
1015 {
1016 // TranslatorExplanation '%s' is a filename
1017 return expected<void>::error(ZYPP_EXCPT_PTR( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))));
1018 }
1019
1020 for ( std::list<RepoInfo>::iterator it = repos.begin();
1021 it != repos.end();
1022 ++it )
1023 {
1024 MIL << "Saving " << (*it).alias() << std::endl;
1025
1026 const auto &rawCachePath = rawcache_path_for_repoinfo( options, *it );
1027 if ( !rawCachePath ) return expected<void>::error(rawCachePath.error());
1028
1029 const auto &pckCachePath = packagescache_path_for_repoinfo( options, *it ) ;
1030 if ( !pckCachePath ) return expected<void>::error(pckCachePath.error());
1031
1032 it->dumpAsIniOn(file);
1033 it->setFilepath(repofile);
1034 it->setMetadataPath( *rawCachePath );
1035 it->setPackagesPath( *pckCachePath );
1036 _repoMgrRef->reposManip().insert(*it);
1037
1038 zypp::HistoryLog( _repoMgrRef->options().rootDir).addRepository(*it);
1039 }
1040
1041 MIL << "done" << std::endl;
1042 return expected<void>::success();
1043 });
1044 }
1045
1046 private:
1047 RepoManagerPtrType _repoMgrRef;
1048 zypp::Url _url;
1049 ProgressObserverRef _myProgress;
1050 };
1051
1052 }
1053
1055 {
1056 return SimpleExecutor<AddReposLogic, AsyncOp<expected<void>>>::run( std::move(mgr), std::move(url), std::move(myProgress) );
1057 }
1058
1059 expected<void> addRepositories( SyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
1060 {
1061 return SimpleExecutor<AddReposLogic, SyncOp<expected<void>>>::run( std::move(mgr), std::move(url), std::move(myProgress) );
1062 }
1063
1064
1065 namespace {
1066
1067 template<typename Executor, class OpType>
1068 struct RefreshGeoIpLogic : public LogicBase<Executor, OpType>{
1069 protected:
1070 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
1071
1072 public:
1073 using ZyppContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, ContextRef, SyncContextRef >;
1074 using ZyppContextType = typename ZyppContextRefType::element_type;
1075 using ProvideType = typename ZyppContextType::ProvideType;
1076 using MediaHandle = typename ProvideType::MediaHandle;
1077 using ProvideRes = typename ProvideType::Res;
1078
1079
1080 RefreshGeoIpLogic( ZyppContextRefType &&zyppCtx, zypp::MirroredOriginSet &&origins )
1081 : _zyppCtx( std::move(zyppCtx) )
1082 , _origins( std::move(origins) )
1083 { }
1084
1085 MaybeAsyncRef<expected<void>> execute() {
1086
1087 using namespace zyppng::operators;
1088
1089 if ( !_zyppCtx->config().geoipEnabled() ) {
1090 MIL << "GeoIp disabled via ZConfig, not refreshing the GeoIP information." << std::endl;
1092 }
1093
1094 std::vector<std::string> hosts;
1095 for ( const zypp::MirroredOrigin &origin : _origins ){
1096 if ( !origin.schemeIsDownloading () )
1097 continue;
1098
1099 for ( const auto &originEndpoint : origin ) {
1100 const auto &host = originEndpoint.url().getHost();
1101 if ( zypp::any_of( _zyppCtx->config().geoipHostnames(), [&host]( const auto &elem ){ return ( zypp::str::compareCI( host, elem ) == 0 ); } ) ) {
1102 hosts.push_back( host );
1103 break;
1104 }
1105 }
1106 }
1107
1108 if ( hosts.empty() ) {
1109 MIL << "No configured geoip URL found, not updating geoip data" << std::endl;
1111 }
1112
1113 _geoIPCache = _zyppCtx->config().geoipCachePath();
1114
1115 if ( zypp::filesystem::assert_dir( _geoIPCache ) != 0 ) {
1116 MIL << "Unable to create cache directory for GeoIP." << std::endl;
1118 }
1119
1120 if ( zypp::IamNotRoot() && not zypp::PathInfo(_geoIPCache).userMayRWX() ) {
1121 MIL << "No access rights for the GeoIP cache directory." << std::endl;
1123 }
1124
1125 // remove all older cache entries
1126 zypp::filesystem::dirForEachExt( _geoIPCache, []( const zypp::Pathname &dir, const zypp::filesystem::DirEntry &entry ) {
1127 if ( entry.type != zypp::filesystem::FT_FILE )
1128 return true;
1129
1130 zypp::PathInfo pi( dir/entry.name );
1131 auto age = std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t( pi.mtime() );
1132 if ( age < std::chrono::hours(24) )
1133 return true;
1134
1135 MIL << "Removing GeoIP file for " << entry.name << " since it's older than 24hrs." << std::endl;
1136 zypp::filesystem::unlink( dir/entry.name );
1137 return true;
1138 });
1139
1140 auto firstOfCb = [this]( std::string hostname ) {
1141
1142 // do not query files that are still there
1143 if ( zypp::PathInfo( _geoIPCache / hostname ).isExist() ) {
1144 MIL << "Skipping GeoIP request for " << hostname << " since a valid cache entry exists." << std::endl;
1145 return makeReadyResult(false);
1146 }
1147
1148 MIL << "Query GeoIP for " << hostname << std::endl;
1149
1150 zypp::Url url;
1151 try {
1152 url.setHost(hostname);
1153 url.setScheme("https");
1154
1155 } catch(const zypp::Exception &e ) {
1156 ZYPP_CAUGHT(e);
1157 MIL << "Ignoring invalid GeoIP hostname: " << hostname << std::endl;
1158 return makeReadyResult(false);
1159 }
1160
1161 // always https ,but attaching makes things easier
1162 return _zyppCtx->provider()->attachMedia( url, ProvideMediaSpec() )
1163 | and_then( [this]( MediaHandle provideHdl ) { return _zyppCtx->provider()->provide( provideHdl, "/geoip", ProvideFileSpec() ); })
1164 | inspect_err( [hostname]( const std::exception_ptr& ){ MIL << "Failed to query GeoIP from hostname: " << hostname << std::endl; } )
1165 | and_then( [hostname, this]( ProvideRes provideRes ) {
1166
1167 // here we got something from the server, we will stop after this hostname and mark the process as success()
1168
1169 constexpr auto writeHostToFile = []( const zypp::Pathname &fName, const std::string &host ){
1170 std::ofstream out;
1171 out.open( fName.asString(), std::ios_base::trunc );
1172 if ( out.is_open() ) {
1173 out << host << std::endl;
1174 } else {
1175 MIL << "Failed to create/open GeoIP cache file " << fName << std::endl;
1176 }
1177 };
1178
1179 std::string geoipMirror;
1180 try {
1181 zypp::xml::Reader reader( provideRes.file() );
1182 if ( reader.seekToNode( 1, "host" ) ) {
1183 const auto &str = reader.nodeText().asString();
1184
1185 // make a dummy URL to ensure the hostname is valid
1186 zypp::Url testUrl;
1187 testUrl.setHost(str);
1188 testUrl.setScheme("https");
1189
1190 if ( testUrl.isValid() ) {
1191 MIL << "Storing geoIP redirection: " << hostname << " -> " << str << std::endl;
1192 geoipMirror = str;
1193 }
1194
1195 } else {
1196 MIL << "No host entry or empty file returned for GeoIP, remembering for 24hrs" << std::endl;
1197 }
1198 } catch ( const zypp::Exception &e ) {
1199 ZYPP_CAUGHT(e);
1200 MIL << "Empty or invalid GeoIP file, not requesting again for 24hrs" << std::endl;
1201 }
1202
1203 writeHostToFile( _geoIPCache / hostname, geoipMirror );
1204 return expected<void>::success(); // need to return a expected<> due to and_then requirements
1205 })
1206 | []( expected<void> res ) { return res.is_valid(); };
1207 };
1208
1209 return std::move(hosts)
1210 | firstOf( std::move(firstOfCb), false, zyppng::detail::ContinueUntilValidPredicate() )
1211 | []( bool foundGeoIP ) {
1212
1213 if ( foundGeoIP ) {
1214 MIL << "Successfully queried GeoIP data." << std::endl;
1215 return expected<void>::success ();
1216 }
1217
1218 MIL << "Failed to query GeoIP data." << std::endl;
1219 return expected<void>::error( std::make_exception_ptr( zypp::Exception("No valid geoIP url found" )) );
1220
1221 };
1222 }
1223
1224 private:
1225 ZyppContextRefType _zyppCtx;
1226 zypp::MirroredOriginSet _origins;
1227 zypp::Pathname _geoIPCache;
1228
1229 };
1230 }
1231
1236
1238 {
1240 }
1241
1242
1244 {
1245 return SimpleExecutor<RefreshGeoIpLogic, AsyncOp<expected<void>>>::run( std::move(ctx), std::move(origins) );
1246 }
1247
1249 {
1250 return SimpleExecutor<RefreshGeoIpLogic, SyncOp<expected<void>>>::run( std::move(ctx), std::move(origins) );
1251 }
1252
1253
1254}
void resetDispose()
Set no dispose function.
std::string asString() const
Definition ByteArray.h:24
time_t ValueType
Definition Date.h:38
static Date now()
Return the current time.
Definition Date.h:78
Base class for Exception.
Definition Exception.h:153
std::string asUserString() const
Translated error message as string suitable for the user.
Definition Exception.cc:131
std::vector< std::string > Arguments
A smart container that manages a collection of MirroredOrigin objects, automatically grouping endpoin...
What is known about a repository.
Definition RepoInfo.h:72
repo::RepoType type() const
Type of repository,.
Definition RepoInfo.cc:750
Url url() const
Pars pro toto: The first repository url, this is either baseUrls().front() or if no baseUrl is define...
Definition RepoInfo.cc:813
void setType(const repo::RepoType &t)
set the repository type
Definition RepoInfo.cc:710
std::list< Url > url_set
Definition RepoInfo.h:108
Date timestamp() const
The time the data were changed the last time.
bool empty() const
Whether the status is empty (empty checksum)
Url manipulation class.
Definition Url.h:93
void setHost(const std::string &host)
Set the hostname or IP in the URL authority.
Definition Url.cc:766
bool isValid() const
Verifies the Url.
Definition Url.cc:507
void setScheme(const std::string &scheme)
Set the scheme name in the URL.
Definition Url.cc:686
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
Just inherits Exception to separate media exceptions.
Repository already exists and some unique attribute can't be duplicated.
Exception for repository handling.
SignalProxy< void()> sigReadyRead()
Definition iodevice.cc:368
static Ptr create()
Definition process.cpp:49
SignalProxy< void(int)> sigFinished()
Definition process.cpp:294
static ProgressObserverRef makeSubTask(ProgressObserverRef parentProgress, float weight=1.0, const std::string &label=std::string(), int steps=100)
static void setup(ProgressObserverRef progress, const std::string &label=std::string(), int steps=100)
static void finish(ProgressObserverRef progress, ProgressObserver::FinishResult result=ProgressObserver::Success)
ProvideFileSpec & setMirrorsAllowed(bool set=true)
Enables or disables the use of mirrors when fetching this file.
A ProvideRes object is a reference counted ownership of a resource in the cache provided by a Provide...
Definition provideres.h:36
const zypp::Pathname file() const
Definition provideres.cc:21
expected< RepoStatus > cacheStatus(const RepoInfo &info) const
static expected< void > touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
static zypp::repo::RepoType probeCache(const zypp::Pathname &path_r)
Probe Metadata in a local cache directory.
static expected< RepoStatus > metadataStatus(const RepoInfo &info, const RepoManagerOptions &options)
static expected success(ConsParams &&...params)
Definition expected.h:115
static expected error(ConsParams &&...params)
Definition expected.h:126
bool is_valid() const
Definition expected.h:141
boost::logic::tribool TriBool
3-state boolean logic (true, false and indeterminate).
Definition String.h:31
#define ZYPP_ENABLE_LOGIC_BASE(Executor, OpType)
Definition Arch.h:364
typename conditional< B, T, F >::type conditional_t
Definition TypeTraits.h:39
@ REFRESH_NEEDED
refresh is needed
@ REPO_UP_TO_DATE
repository not changed
@ REPO_CHECK_DELAYED
refresh is delayed due to settings
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:705
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:324
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
Definition PathInfo.cc:598
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition Pool.cc:286
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:39
Url details namespace.
Definition UrlBase.cc:58
Easy-to use interface to the ZYPP dependency resolver.
bool IamRoot()
Definition PathInfo.h:41
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition ManagedFile.h:27
bool IamNotRoot()
Definition PathInfo.h:42
bool any_of(const Container &c, Fnc &&cb)
Definition Algorithm.h:76
AsyncOpRef< expected< repo::AsyncDownloadContextRef > > download(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle, ProgressObserverRef progressObserver=nullptr)
AsyncOpRef< expected< zypp::RepoStatus > > repoStatus(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle)
AsyncOpRef< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, AsyncLazyMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath)
AsyncOpRef< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
AsyncOpRef< expected< std::list< RepoInfo > > > readRepoFile(ContextRef ctx, zypp::Url repoFileUrl)
AsyncOpRef< expected< void > > refreshGeoIPData(ContextRef ctx, RepoInfo::url_set urls)
AsyncOpRef< expected< void > > addRepositories(AsyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > refreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
AsyncOpRef< expected< RepoInfo > > addRepository(AsyncRepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress, const zypp::TriBool &forcedProbe)
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > buildCache(repo::AsyncRefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
auto or_else(Fun &&function)
Definition expected.h:630
auto and_then(Fun &&function)
Definition expected.h:623
auto setProgress(ProgressObserverRef progressObserver, double progrValue, std::optional< std::string > newStr={})
auto inspect_err(Fun &&function)
Definition expected.h:644
auto inspect(Fun &&function)
Definition expected.h:637
auto mtry(Fun &&function)
Definition mtry.h:58
zypp::RepoManagerFlags::RefreshCheckStatus RefreshCheckStatus
Definition refresh.h:34
expected< void > assert_urls(const RepoInfo &info)
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition asyncop.h:297
std::shared_ptr< AsyncOp< T > > AsyncOpRef
Definition asyncop.h:255
typename remove_smart_ptr< T >::type remove_smart_ptr_t
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition expected.h:397
zypp::RepoStatus RepoStatus
Definition repomanager.h:38
LazyMediaHandle< Provide > AsyncLazyMediaHandle
expected< zypp::Pathname > rawcache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw cache path for a repository, this is usually /var/cache/zypp/alias.
expected< void > assert_alias(const RepoInfo &info)
Definition repomanager.h:58
LazyMediaHandle< MediaSyncFacade > SyncLazyMediaHandle
auto firstOf(Transformation &&transformFunc, DefaultType &&def, Predicate &&predicate=detail::ContinueUntilValidPredicate())
Definition algorithm.h:149
RepoManagerRef< SyncContextRef > SyncRepoManagerRef
Definition repomanager.h:49
expected< zypp::Pathname > solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
zypp::RepoInfo RepoInfo
Definition repomanager.h:37
expected< std::list< RepoInfo > > repositories_in_file(const zypp::Pathname &file)
Reads RepoInfo's from a repo file.
RepoManagerRef< ContextRef > AsyncRepoManagerRef
Definition repomanager.h:52
expected< zypp::Pathname > packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
zypp::ByteArray ByteArray
Definition bytearray.h:21
expected< zypp::Pathname > rawproductdata_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw product metadata path for a repository, this is inside the raw cache dir,...
static const RepoType YAST2
Definition RepoType.h:31
Type toEnum() const
Definition RepoType.h:49
static const RepoType RPMMD
Definition RepoType.h:30
static const RepoType NONE
Definition RepoType.h:33
static const RepoType RPMPLAINDIR
Definition RepoType.h:32
Functor replacing repository variables.
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:27
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition Exception.h:479
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:463
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:471
#define _(MSG)
Definition Gettext.h:39
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101