libzypp: zypp/ng/repo/workflows/repomanagerwf.cc Source File

libzypp 17.38.15

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