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