libzypp  17.37.10
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>
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 
22 #include <zypp/ExternalProgram.h>
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;
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 
286  MIL << "Forced refresh!" << std::endl;
288  }
289 
290  if ( _medium.baseUrl().schemeIsLocal() ) {
292  }
293 
294  // Check whether repo.refresh.delay applies...
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;
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() ); })
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
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 )
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.
461  });
462  });
463  }
464 
465  RefreshContextRefType _refreshContext;
466  ProgressObserverRef _progress;
467  LazyMediaHandle _medium;
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 
567  static AsyncOpRef<expected<void>> run( zypp::RepoInfo repo, zypp::ExternalProgram::Arguments args ) {
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;
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 ) {
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  }
632  return expected<void>::success();
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 
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 
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 )
746  return makeReadyResult( expected<void>::error(base.error()) );
747 
748  if( zypp::filesystem::assert_dir(*base) )
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
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
790  zypp::ManagedFile guard( solvfile, zypp::filesystem::unlink );
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 
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 )
892  : _repoMgrRef( std::move(repoMgrRef) )
893  , _info( std::move(info) )
894  , _myProgress ( std::move(myProgress) )
895  {}
896 
897  MaybeAsyncRef<expected<RepoInfo> > execute() {
898  using namespace zyppng::operators;
899 
900  return assert_alias(_info)
901  | and_then([this]( ) {
902 
903  MIL << "Try adding repo " << _info << std::endl;
904  ProgressObserver::setup( _myProgress, zypp::str::form(_("Adding repository '%s'"), _info.label().c_str()) );
906 
907  if ( _repoMgrRef->repos().find(_info) != _repoMgrRef->repos().end() )
909 
910  // check the first url for now
911  if ( _repoMgrRef->options().probe )
912  {
913  DBG << "unknown repository type, probing" << std::endl;
914  return assert_urls(_info)
915  | and_then([this]{ return probeRepoType( _repoMgrRef->zyppContext(), _info ); })
916  | and_then([this]( zypp::repo::RepoType probedtype ) {
917 
918  if ( probedtype == zypp::repo::RepoType::NONE )
920 
921  RepoInfo tosave = _info;
922  tosave.setType(probedtype);
923  return make_expected_success(tosave);
924  });
925  }
927  })
929  | and_then( [this]( RepoInfo tosave ){ return _repoMgrRef->addProbedRepository( tosave, tosave.type() ); })
930  | and_then( [this]( RepoInfo updated ) {
932  MIL << "done" << std::endl;
933  return expected<RepoInfo>::success( updated );
934  })
935  | or_else( [this]( std::exception_ptr e) {
937  MIL << "done" << std::endl;
938  return expected<RepoInfo>::error(e);
939  })
940  ;
941  }
942 
943  RepoManagerPtrType _repoMgrRef;
945  ProgressObserverRef _myProgress;
946  };
947  };
948 
949  AsyncOpRef<expected<RepoInfo> > addRepository( AsyncRepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress )
950  {
951  return SimpleExecutor<AddRepoLogic, AsyncOp<expected<RepoInfo>>>::run( std::move(mgr), std::move(info), std::move(myProgress) );
952  }
953 
954  expected<RepoInfo> addRepository( SyncRepoManagerRef mgr, const RepoInfo &info, ProgressObserverRef myProgress )
955  {
956  return SimpleExecutor<AddRepoLogic, SyncOp<expected<RepoInfo>>>::run( std::move(mgr), RepoInfo(info), std::move(myProgress) );
957  }
958 
959  namespace {
960 
961  template<typename Executor, class OpType>
962  struct AddReposLogic : public LogicBase<Executor, OpType>{
963 
965  ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
966 
967  AddReposLogic( RepoManagerPtrType &&repoMgrRef, zypp::Url &&url, ProgressObserverRef &&myProgress )
968  : _repoMgrRef( std::move(repoMgrRef) )
969  , _url( std::move(url) )
970  , _myProgress ( std::move(myProgress) )
971  {}
972 
973  MaybeAsyncRef<expected<void>> execute() {
974  using namespace zyppng::operators;
975 
977  | and_then([this]( zypp::Url repoFileUrl ) { return readRepoFile( _repoMgrRef->zyppContext(), std::move(repoFileUrl) ); } )
978  | and_then([this]( std::list<RepoInfo> repos ) {
979 
980  for ( std::list<RepoInfo>::const_iterator it = repos.begin();
981  it != repos.end();
982  ++it )
983  {
984  // look if the alias is in the known repos.
985  for_ ( kit, _repoMgrRef->repoBegin(), _repoMgrRef->repoEnd() )
986  {
987  if ( (*it).alias() == (*kit).alias() )
988  {
989  ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << std::endl;
991  }
992  }
993  }
994 
995  std::string filename = zypp::Pathname(_url.getPathName()).basename();
996  if ( filename == zypp::Pathname() )
997  {
998  // TranslatorExplanation '%s' is an URL
999  return expected<void>::error(ZYPP_EXCPT_PTR(zypp::repo::RepoException(zypp::str::form( _("Invalid repo file name at '%s'"), _url.asString().c_str() ))));
1000  }
1001 
1002  const auto &options = _repoMgrRef->options();
1003 
1004  // assert the directory exists
1005  zypp::filesystem::assert_dir( options.knownReposPath );
1006 
1007  zypp::Pathname repofile = _repoMgrRef->generateNonExistingName( options.knownReposPath, filename );
1008  // now we have a filename that does not exists
1009  MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << std::endl;
1010 
1011  std::ofstream file(repofile.c_str());
1012  if (!file)
1013  {
1014  // TranslatorExplanation '%s' is a filename
1015  return expected<void>::error(ZYPP_EXCPT_PTR( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))));
1016  }
1017 
1018  for ( std::list<RepoInfo>::iterator it = repos.begin();
1019  it != repos.end();
1020  ++it )
1021  {
1022  MIL << "Saving " << (*it).alias() << std::endl;
1023 
1024  const auto &rawCachePath = rawcache_path_for_repoinfo( options, *it );
1025  if ( !rawCachePath ) return expected<void>::error(rawCachePath.error());
1026 
1027  const auto &pckCachePath = packagescache_path_for_repoinfo( options, *it ) ;
1028  if ( !pckCachePath ) return expected<void>::error(pckCachePath.error());
1029 
1030  it->dumpAsIniOn(file);
1031  it->setFilepath(repofile);
1032  it->setMetadataPath( *rawCachePath );
1033  it->setPackagesPath( *pckCachePath );
1034  _repoMgrRef->reposManip().insert(*it);
1035 
1036  zypp::HistoryLog( _repoMgrRef->options().rootDir).addRepository(*it);
1037  }
1038 
1039  MIL << "done" << std::endl;
1040  return expected<void>::success();
1041  });
1042  }
1043 
1044  private:
1045  RepoManagerPtrType _repoMgrRef;
1047  ProgressObserverRef _myProgress;
1048  };
1049 
1050  }
1051 
1052  AsyncOpRef<expected<void>> addRepositories( AsyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress )
1053  {
1054  return SimpleExecutor<AddReposLogic, AsyncOp<expected<void>>>::run( std::move(mgr), std::move(url), std::move(myProgress) );
1055  }
1056 
1057  expected<void> addRepositories( SyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
1058  {
1059  return SimpleExecutor<AddReposLogic, SyncOp<expected<void>>>::run( std::move(mgr), std::move(url), std::move(myProgress) );
1060  }
1061 
1062 
1063  namespace {
1064 
1065  template<typename Executor, class OpType>
1066  struct RefreshGeoIpLogic : public LogicBase<Executor, OpType>{
1067  protected:
1068  ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
1069 
1070  public:
1071  using ZyppContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, ContextRef, SyncContextRef >;
1072  using ZyppContextType = typename ZyppContextRefType::element_type;
1073  using ProvideType = typename ZyppContextType::ProvideType;
1074  using MediaHandle = typename ProvideType::MediaHandle;
1075  using ProvideRes = typename ProvideType::Res;
1076 
1077 
1078  RefreshGeoIpLogic( ZyppContextRefType &&zyppCtx, zypp::MirroredOriginSet &&origins )
1079  : _zyppCtx( std::move(zyppCtx) )
1080  , _origins( std::move(origins) )
1081  { }
1082 
1083  MaybeAsyncRef<expected<void>> execute() {
1084 
1085  using namespace zyppng::operators;
1086 
1087  if ( !_zyppCtx->config().geoipEnabled() ) {
1088  MIL << "GeoIp disabled via ZConfig, not refreshing the GeoIP information." << std::endl;
1090  }
1091 
1092  std::vector<std::string> hosts;
1093  for ( const zypp::MirroredOrigin &origin : _origins ){
1094  if ( !origin.schemeIsDownloading () )
1095  continue;
1096 
1097  for ( const auto &originEndpoint : origin ) {
1098  const auto &host = originEndpoint.url().getHost();
1099  if ( zypp::any_of( _zyppCtx->config().geoipHostnames(), [&host]( const auto &elem ){ return ( zypp::str::compareCI( host, elem ) == 0 ); } ) ) {
1100  hosts.push_back( host );
1101  break;
1102  }
1103  }
1104  }
1105 
1106  if ( hosts.empty() ) {
1107  MIL << "No configured geoip URL found, not updating geoip data" << std::endl;
1109  }
1110 
1111  _geoIPCache = _zyppCtx->config().geoipCachePath();
1112 
1113  if ( zypp::filesystem::assert_dir( _geoIPCache ) != 0 ) {
1114  MIL << "Unable to create cache directory for GeoIP." << std::endl;
1116  }
1117 
1118  if ( zypp::IamNotRoot() && not zypp::PathInfo(_geoIPCache).userMayRWX() ) {
1119  MIL << "No access rights for the GeoIP cache directory." << std::endl;
1121  }
1122 
1123  // remove all older cache entries
1125  if ( entry.type != zypp::filesystem::FT_FILE )
1126  return true;
1127 
1128  zypp::PathInfo pi( dir/entry.name );
1129  auto age = std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t( pi.mtime() );
1130  if ( age < std::chrono::hours(24) )
1131  return true;
1132 
1133  MIL << "Removing GeoIP file for " << entry.name << " since it's older than 24hrs." << std::endl;
1134  zypp::filesystem::unlink( dir/entry.name );
1135  return true;
1136  });
1137 
1138  auto firstOfCb = [this]( std::string hostname ) {
1139 
1140  // do not query files that are still there
1141  if ( zypp::PathInfo( _geoIPCache / hostname ).isExist() ) {
1142  MIL << "Skipping GeoIP request for " << hostname << " since a valid cache entry exists." << std::endl;
1143  return makeReadyResult(false);
1144  }
1145 
1146  MIL << "Query GeoIP for " << hostname << std::endl;
1147 
1148  zypp::Url url;
1149  try {
1150  url.setHost(hostname);
1151  url.setScheme("https");
1152 
1153  } catch(const zypp::Exception &e ) {
1154  ZYPP_CAUGHT(e);
1155  MIL << "Ignoring invalid GeoIP hostname: " << hostname << std::endl;
1156  return makeReadyResult(false);
1157  }
1158 
1159  // always https ,but attaching makes things easier
1160  return _zyppCtx->provider()->attachMedia( url, ProvideMediaSpec() )
1161  | and_then( [this]( MediaHandle provideHdl ) { return _zyppCtx->provider()->provide( provideHdl, "/geoip", ProvideFileSpec() ); })
1162  | inspect_err( [hostname]( const std::exception_ptr& ){ MIL << "Failed to query GeoIP from hostname: " << hostname << std::endl; } )
1163  | and_then( [hostname, this]( ProvideRes provideRes ) {
1164 
1165  // here we got something from the server, we will stop after this hostname and mark the process as success()
1166 
1167  constexpr auto writeHostToFile = []( const zypp::Pathname &fName, const std::string &host ){
1168  std::ofstream out;
1169  out.open( fName.asString(), std::ios_base::trunc );
1170  if ( out.is_open() ) {
1171  out << host << std::endl;
1172  } else {
1173  MIL << "Failed to create/open GeoIP cache file " << fName << std::endl;
1174  }
1175  };
1176 
1177  std::string geoipMirror;
1178  try {
1179  zypp::xml::Reader reader( provideRes.file() );
1180  if ( reader.seekToNode( 1, "host" ) ) {
1181  const auto &str = reader.nodeText().asString();
1182 
1183  // make a dummy URL to ensure the hostname is valid
1184  zypp::Url testUrl;
1185  testUrl.setHost(str);
1186  testUrl.setScheme("https");
1187 
1188  if ( testUrl.isValid() ) {
1189  MIL << "Storing geoIP redirection: " << hostname << " -> " << str << std::endl;
1190  geoipMirror = str;
1191  }
1192 
1193  } else {
1194  MIL << "No host entry or empty file returned for GeoIP, remembering for 24hrs" << std::endl;
1195  }
1196  } catch ( const zypp::Exception &e ) {
1197  ZYPP_CAUGHT(e);
1198  MIL << "Empty or invalid GeoIP file, not requesting again for 24hrs" << std::endl;
1199  }
1200 
1201  writeHostToFile( _geoIPCache / hostname, geoipMirror );
1202  return expected<void>::success(); // need to return a expected<> due to and_then requirements
1203  })
1204  | []( expected<void> res ) { return res.is_valid(); };
1205  };
1206 
1207  return std::move(hosts)
1208  | firstOf( std::move(firstOfCb), false, zyppng::detail::ContinueUntilValidPredicate() )
1209  | []( bool foundGeoIP ) {
1210 
1211  if ( foundGeoIP ) {
1212  MIL << "Successfully queried GeoIP data." << std::endl;
1213  return expected<void>::success ();
1214  }
1215 
1216  MIL << "Failed to query GeoIP data." << std::endl;
1217  return expected<void>::error( std::make_exception_ptr( zypp::Exception("No valid geoIP url found" )) );
1218 
1219  };
1220  }
1221 
1222  private:
1223  ZyppContextRefType _zyppCtx;
1226 
1227  };
1228  }
1229 
1231  {
1233  }
1234 
1236  {
1238  }
1239 
1240 
1242  {
1243  return SimpleExecutor<RefreshGeoIpLogic, AsyncOp<expected<void>>>::run( std::move(ctx), std::move(origins) );
1244  }
1245 
1247  {
1248  return SimpleExecutor<RefreshGeoIpLogic, SyncOp<expected<void>>>::run( std::move(ctx), std::move(origins) );
1249  }
1250 
1251 
1252 }
RepoInfo _info
RefreshContextRefType _refreshContext
ProgressObserverRef _progressObserver
#define MIL
Definition: Logger.h:100
AsyncOpRef< expected< repo::AsyncDownloadContextRef > > download(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle, ProgressObserverRef progressObserver=nullptr)
zypp::RepoStatus RepoStatus
Definition: repomanager.h:37
LazyMediaHandle _medium
bool empty() const
Whether the status is empty (empty checksum)
Definition: RepoStatus.cc:234
thrown when it was impossible to determine this repo type.
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:324
Listentry returned by readdir.
Definition: PathInfo.h:509
#define _(MSG)
Definition: Gettext.h:39
Type toEnum() const
Definition: RepoType.h:49
zypp::RepoInfo RepoInfo
Definition: repomanager.h:36
bool IamNotRoot()
Definition: PathInfo.h:42
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:531
SignalProxy< void(int)> sigFinished()
Definition: process.cpp:294
ProgressObserverRef _myProgress
A ProvideRes object is a reference counted ownership of a resource in the cache provided by a Provide...
Definition: provideres.h:35
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > refreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
expected< std::list< RepoInfo > > repositories_in_file(const zypp::Pathname &file)
Reads RepoInfo&#39;s from a repo file.
Definition: repomanager.cc:167
const std::string & execError() const
Some detail telling why the execution failed, if it failed.
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
zypp::Pathname _mediarootpath
const char * c_str() const
String representation.
Definition: Pathname.h:112
void addHistory(const std::string &msg_r)
Add some message text to the history.
Definition: Exception.cc:189
String related utilities and Regular expression matching.
zypp::RepoInfo _repo
zypp::Pathname _geoIPCache
Definition: Arch.h:363
What is known about a repository.
Definition: RepoInfo.h:71
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition: expected.h:397
std::optional< zypp::Pathname > _targetPath
void setHost(const std::string &host)
Set the hostname or IP in the URL authority.
Definition: Url.cc:766
std::string receiveLine()
Read one line from the input stream.
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > buildCache(repo::AsyncRefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
Convenient building of std::string with boost::format.
Definition: String.h:253
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:39
ProgressObserverRef _progress
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition: Exception.h:463
ZyppContextRefType _zyppContext
ZyppContextRefType _zyppCtx
zypp::Pathname _path
#define ERR
Definition: Logger.h:102
static zypp::repo::RepoType probeCache(const zypp::Pathname &path_r)
Probe Metadata in a local cache directory.
Definition: repomanager.cc:425
static expected< void > touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
expected< zypp::Pathname > packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
Definition: repomanager.h:195
zypp::Url _url
static expected< RepoStatus > metadataStatus(const RepoInfo &info, const RepoManagerOptions &options)
Definition: repomanager.cc:309
void remember(const Exception &old_r)
Store an other Exception as history.
Definition: Exception.cc:154
time_t ValueType
Definition: Date.h:38
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:463
zypp::MirroredOriginSet _origins
Url url() const
Pars pro toto: The first repository url, this is either baseUrls().front() or if no baseUrl is define...
Definition: RepoInfo.cc:805
Exp mtry(F &&f, Args &&...args)
Definition: mtry.h:28
zypp::repo::RepoException _error
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:479
ProcessRef _proc
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:515
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:186
auto firstOf(Transformation &&transformFunc, DefaultType &&def, Predicate &&predicate=detail::ContinueUntilValidPredicate())
Definition: algorithm.h:149
RepoManagerPtrType _repoMgrRef
static Ptr create()
Definition: process.cpp:49
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:212
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
void setScheme(const std::string &scheme)
Set the scheme name in the URL.
Definition: Url.cc:686
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
const std::string & asString() const
String representation.
Definition: Pathname.h:93
std::string alias() const
unique identifier for this source.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition: expected.h:544
Just inherits Exception to separate media exceptions.
std::string asUserString() const
Translated error message as string suitable for the user.
Definition: Exception.cc:131
RepoManagerRef< SyncContextRef > SyncRepoManagerRef
Definition: repomanager.h:49
std::string _errdetail
expected< void > assert_alias(const RepoInfo &info)
Definition: repomanager.h:58
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:126
#define WAR
Definition: Logger.h:101
zypp::Pathname _productdatapath
int close() override
Wait for the progamm to complete.
#define ZYPP_ENABLE_LOGIC_BASE(Executor, OpType)
Definition: logichelpers.h:223
static void setup(ProgressObserverRef progress, const std::string &label=std::string(), int steps=100)
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
bool isValid() const
Verifies the Url.
Definition: Url.cc:507
typename conditional< B, T, F >::type conditional_t
Definition: TypeTraits.h:39
std::list< Url > url_set
Definition: RepoInfo.h:108
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition: asyncop.h:297
std::vector< std::string > Arguments
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:705
static const RepoType NONE
Definition: RepoType.h:33
static expected success(ConsParams &&...params)
Definition: expected.h:115
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:171
int compareCI(const C_Str &lhs, const C_Str &rhs)
Definition: String.h:1055
thrown if the user has no permission to update(write) the caches.
static const RepoType RPMMD
Definition: RepoType.h:30
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
zypp::RepoManagerFlags::CacheBuildPolicy _policy
std::shared_ptr< AsyncOp< T > > AsyncOpRef
Definition: asyncop.h:255
AsyncOpRef< expected< void > > refreshGeoIPData(ContextRef ctx, RepoInfo::url_set urls)
expected< RepoStatus > cacheStatus(const RepoInfo &info) const
Definition: repomanager.h:356
zypp::RepoManagerFlags::RefreshCheckStatus RefreshCheckStatus
Definition: refresh.h:34
static const RepoType YAST2
Definition: RepoType.h:31
AsyncOpRef< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, AsyncLazyMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath)
bool _gotMediaError
RefreshContextRefType _refCtx
expected< void > assert_urls(const RepoInfo &info)
Definition: repomanager.cc:227
static ProgressObserverRef makeSubTask(ProgressObserverRef parentProgress, float weight=1.0, const std::string &label=std::string(), int steps=100)
AsyncOpRef< expected< zypp::RepoStatus > > repoStatus(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle)
Base class for Exception.
Definition: Exception.h:152
Date timestamp() const
The time the data were changed the last time.
Definition: RepoStatus.cc:237
const std::string & command() const
The command we&#39;re executing.
auto setProgress(ProgressObserverRef progressObserver, double progrValue, std::optional< std::string > newStr={})
Exception for repository handling.
Definition: RepoException.h:37
SignalProxy< void()> sigReadyRead()
Definition: iodevice.cc:368
refresh is delayed due to settings
bool any_of(const Container &c, Fnc &&cb)
Definition: Algorithm.h:76
static Date now()
Return the current time.
Definition: Date.h:78
Predicate predicate
Definition: PoolQuery.cc:314
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition: Url.cc:622
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
AsyncOpRef< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
RepoManagerRef< ContextRef > AsyncRepoManagerRef
Definition: repomanager.h:52
static void finish(ProgressObserverRef progress, ProgressObserver::FinishResult result=ProgressObserver::Success)
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:423
static const RepoType RPMPLAINDIR
Definition: RepoType.h:32
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:172
Track changing files or directories.
Definition: RepoStatus.h:40
AsyncOpRef< expected< RepoInfo > > addRepository(AsyncRepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress)
Repository already exists and some unique attribute can&#39;t be duplicated.
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition: Exception.h:471
Functor replacing repository variables.
AsyncOpRef< expected< std::list< RepoInfo > > > readRepoFile(ContextRef ctx, zypp::Url repoFileUrl)
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
Definition: PathInfo.cc:598
AsyncOpRef< expected< void > > addRepositories(AsyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
typename remove_smart_ptr< T >::type remove_smart_ptr_t
Definition: type_traits.h:133
Url manipulation class.
Definition: Url.h:92
A smart container that manages a collection of MirroredOrigin objects, automatically grouping endpoin...
expected< zypp::Pathname > solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
Definition: repomanager.h:205
#define DBG
Definition: Logger.h:99
Repository type enumeration.
Definition: RepoType.h:28
bool IamRoot()
Definition: PathInfo.h:41
xmlTextReader based interface to iterate xml streams.
Definition: Reader.h:95
zypp::ByteArray ByteArray
Definition: bytearray.h:21