libzypp  17.38.7
repomanager.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "repomanager.h"
12 
13 #include <solv/solvversion.h>
14 
15 #include <zypp-core/AutoDispose.h>
16 #include <zypp-core/base/Regex.h>
17 #include <zypp-core/fs/PathInfo.h>
18 #include <zypp-core/ng/pipelines/MTry>
19 #include <zypp-core/ng/pipelines/Transform>
21 #include <zypp-core/ng/ui/ProgressObserver>
23 #include <zypp/HistoryLog.h>
24 #include <zypp/ZConfig.h>
25 #include <zypp/ZYppCallbacks.h>
29 #include <zypp/sat/Pool.h>
31 #include <zypp/repo/ServiceType.h>
33 
34 #include <zypp/ng/reporthelper.h>
35 #include <zypp/ng/repo/refresh.h>
38 
39 #include <fstream>
40 #include <utility>
41 
42 #undef ZYPP_BASE_LOGGER_LOGGROUP
43 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::repomanager"
44 
46  bool IGotIt(); // in readonly-mode
47 }
48 
49 
50 namespace zyppng
51 {
52  namespace env
53  {
56  {
57  const char * env = getenv("ZYPP_PLUGIN_APPDATA_FORCE_COLLECT");
58  return( env && zypp::str::strToBool( env, true ) );
59  }
60  } // namespace env
61 
62  namespace {
68  inline void cleanupNonRepoMetadataFolders( const zypp::Pathname & cachePath_r,
69  const zypp::Pathname & defaultCachePath_r,
70  const std::list<std::string> & repoEscAliases_r )
71  {
72  if ( cachePath_r != defaultCachePath_r )
73  return;
74 
75  std::list<std::string> entries;
76  if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
77  {
78  entries.sort();
79  std::set<std::string> oldfiles;
80  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
81  std::inserter( oldfiles, oldfiles.end() ) );
82 
83  // bsc#1178966: Files or symlinks here have been created by the user
84  // for whatever purpose. It's our cache, so we purge them now before
85  // they may later conflict with directories we need.
86  zypp::PathInfo pi;
87  for ( const std::string & old : oldfiles )
88  {
89  if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
90  continue;
91  pi( cachePath_r/old );
92  if ( pi.isDir() )
94  else
96  }
97  }
98  }
99  } // namespace
100 
102  {
103  switch ( obj ) {
104 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
106  OUTS( RefreshForced );
108 #undef OUTS
109  }
110  return str;
111  }
112 
113  std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::RefreshCheckStatus obj )
114  {
115  switch ( obj ) {
116 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
117  OUTS( REFRESH_NEEDED );
120 #undef OUTS
121  }
122  return str;
123  }
124 
125  std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::CacheBuildPolicy obj )
126  {
127  switch ( obj ) {
128 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
129  OUTS( BuildIfNeeded );
130  OUTS( BuildForced );
131 #undef OUTS
132  }
133  return str;
134  }
135 
136 
137  std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
138  {
139  std::string filename( alias_r );
140  // replace slashes with underscores
141  zypp::str::replaceAll( filename, "/", "_" );
142 
143  filename = zypp::Pathname(filename).extend("."+stem_r).asString();
144  MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << std::endl;
145  return filename;
146  }
147 
149  {
150  // skip repositories meant for other distros than specified
151  if (!targetDistro.empty()
152  && !repo.targetDistribution().empty()
153  && repo.targetDistribution() != targetDistro)
154  {
155  MIL
156  << "Skipping repository meant for '" << repo.targetDistribution()
157  << "' distribution (current distro is '"
158  << targetDistro << "')." << std::endl;
159 
160  return true;
161  }
162 
163  repos.push_back(repo);
164  return true;
165  }
166 
168  {
169  try {
170  MIL << "repo file: " << file << std::endl;
172  zypp::parser::RepoFileReader parser( file, std::bind( &RepoCollector::collect, &collector, std::placeholders::_1 ) );
173  return expected<std::list<RepoInfo>>::success( std::move(collector.repos) );
174  } catch ( ... ) {
176  }
177  }
178 
188  template <typename ZContextRef>
189  std::list<RepoInfo> repositories_in_dir( ZContextRef zyppContext, const zypp::Pathname &dir )
190  {
191  MIL << "directory " << dir << std::endl;
192  std::list<RepoInfo> repos;
193  bool nonroot( geteuid() != 0 );
194  if ( nonroot && ! zypp::PathInfo(dir).userMayRX() )
195  {
196  JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo directory '%1%': Permission denied")) % dir );
197  }
198  else
199  {
200  std::list<zypp::Pathname> entries;
201  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
202  {
203  // TranslatorExplanation '%s' is a pathname
204  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
205  }
206 
207  zypp::str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
208  for ( std::list<zypp::Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
209  {
210  if ( zypp::str::regex_match(it->extension(), allowedRepoExt) )
211  {
212  if ( nonroot && ! zypp::PathInfo(*it).userMayR() )
213  {
214  JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo file '%1%': Permission denied")) % *it );
215  }
216  else
217  {
218  const std::list<RepoInfo> tmp( repositories_in_file( *it ).unwrap() );
219  repos.insert( repos.end(), tmp.begin(), tmp.end() );
220  }
221  }
222  }
223  }
224  return repos;
225  }
226 
228  {
229  if ( info.repoOriginsEmpty() )
231  return expected<void>::success();
232  }
233 
234  bool autoPruneInDir(const zypp::Pathname &path_r)
235  { return not zypp::PathInfo(path_r/".no_auto_prune").isExist(); }
236 
237 
238  RepoManager::RepoManager( ZYPP_PRIVATE_CONSTR_ARG, ContextRef zyppCtx, RepoManagerOptions opt )
239  : _zyppContext( std::move(zyppCtx) )
240  , _options( std::move(opt) )
241  , _pluginRepoverification( _options.pluginsPath / "repoverification",
242  _options.rootDir)
243  {
244 
245  }
246 
248  {
249  // trigger appdata refresh if some repos change
250  if ( ( _reposDirty || env::ZYPP_PLUGIN_APPDATA_FORCE_COLLECT() )
251  && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
252  {
253  try {
254  std::list<zypp::Pathname> entries;
255  zypp::filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
256  if ( ! entries.empty() )
257  {
259  cmd.push_back( "<" ); // discard stdin
260  cmd.push_back( ">" ); // discard stdout
261  cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
262  for ( const auto & rinfo : repos() )
263  {
264  if ( ! rinfo.enabled() )
265  continue;
266  cmd.push_back( "-R" );
267  cmd.push_back( rinfo.alias() );
268  cmd.push_back( "-t" );
269  cmd.push_back( rinfo.type().asString() );
270  cmd.push_back( "-p" );
271  cmd.push_back( (rinfo.metadataPath()/rinfo.path()).asString() ); // bsc#1197684: path to the repodata/ directory inside the cache
272  }
273 
274  for_( it, entries.begin(), entries.end() )
275  {
276  zypp::PathInfo pi( *it );
277  //DBG << "/tmp/xx ->" << pi << endl;
278  if ( pi.isFile() && pi.userMayRX() )
279  {
280  // trigger plugin
281  cmd[2] = pi.asString(); // [2] - PROGRAM
283  }
284  }
285  }
286  }
287  catch (...) {} // no throw in dtor
288  }
289  }
290 
291 
292  expected<void> RepoManager::initialize()
293  {
294  using namespace zyppng::operators;
295  return
296  init_knownServices()
297  | and_then( [this](){ return init_knownRepositories(); } );
298  }
299 
300 
301  const RepoManagerOptions &RepoManager::options() const
302  {
303  return _options;
304  }
305 
306 
308  {
309  try {
310  using namespace zyppng::operators;
311 
312  // ATTENTION when making this pipeline async
313  // consider moving it into a workflow object
314  // this var is caputured by ref to modify it from
315  // inside the pipeline, which would break.
316  zypp::Pathname mediarootpath;
317 
318  return rawcache_path_for_repoinfo( options, info )
319  | and_then( [&]( zypp::Pathname mrPath ) {
320  mediarootpath = std::move(mrPath);
321  return rawproductdata_path_for_repoinfo( options, info );
322  })
323  | and_then( [&]( zypp::Pathname productdatapath ) {
324  zypp::repo::RepoType repokind = info.type();
325  // If unknown, probe the local metadata
326  if ( repokind == zypp::repo::RepoType::NONE )
327  repokind = probeCache( productdatapath );
328 
329  // NOTE: The calling code expects an empty RepoStatus being returned
330  // if the metadata cache is empty. So additional components like the
331  // RepoInfos status are joined after the switch IFF the status is not
332  // empty.huhu
333  RepoStatus status;
334  switch ( repokind.toEnum() )
335  {
337  status = RepoStatus( productdatapath/"repodata/repomd.xml");
338  if ( info.requireStatusWithMediaFile() )
339  status = status && RepoStatus( mediarootpath/"media.1/media" );
340  break;
341 
343  status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
344  break;
345 
347  // Dir status at last refresh. Plaindir uses the cookiefile as pseudo metadata index file.
348  // It gets touched if the refresh check finds the data being up-to-date. That's why we use
349  // the files mtime as timestamp (like the RepoStatus ctor in the other cases above).
350  status = RepoStatus::fromCookieFileUseMtime( productdatapath/"cookie" );
351  break;
352 
354  // Return default RepoStatus in case of RepoType::NONE
355  // indicating it should be created?
356  // ZYPP_THROW(RepoUnknownTypeException());
357  break;
358  }
359 
360  if ( ! status.empty() )
361  status = status && RepoStatus( info );
362 
363  return expected<RepoStatus>::success(status);
364  });
365  } catch (...) {
367  }
368  }
369 
370 
372  {
373  return metadataStatus( info, _options );
374  }
375 
376 
377  expected<void> RepoManager::cleanMetadata(const RepoInfo &info, ProgressObserverRef myProgress )
378  {
379  try {
380 
381  ProgressObserver::setup( myProgress, _("Cleaning metadata"), 100 );
382  ProgressObserver::start( myProgress );
383  zypp::filesystem::recursive_rmdir( _zyppContext->config().geoipCachePath() );
384  ProgressObserver::setCurrent ( myProgress, 50 );
386  ProgressObserver::finish ( myProgress );
387 
388  } catch ( ... ) {
389  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
391  }
392  return expected<void>::success();
393  }
394 
395 
396  expected<void> RepoManager::cleanPackages(const RepoInfo &info, ProgressObserverRef myProgress, bool isAutoClean )
397  {
398  try {
399  ProgressObserver::setup( myProgress, _("Cleaning packages"), 100 );
400  ProgressObserver::start( myProgress );
401 
402  // bsc#1204956: Tweak to prevent auto pruning package caches
404  if ( not isAutoClean || autoPruneInDir( rpc.dirname() ) )
406 
407  ProgressObserver::finish ( myProgress );
408 
409  } catch (...) {
410  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
412  }
413 
414  return expected<void>::success();
415  }
416 
423  zypp::repo::RepoType RepoManager::probeCache( const zypp::Pathname & path_r )
424  {
425  MIL << "going to probe the cached repo at " << path_r << std::endl;
426 
428 
429  if ( zypp::PathInfo(path_r/"/repodata/repomd.xml").isFile() )
430  { ret = zypp::repo::RepoType::RPMMD; }
431  else if ( zypp::PathInfo(path_r/"/content").isFile() )
432  { ret = zypp::repo::RepoType::YAST2; }
433  else if ( zypp::PathInfo(path_r/"/cookie").isFile() )
435 
436  MIL << "Probed cached type " << ret << " at " << path_r << std::endl;
437  return ret;
438  }
439 
440 
441  expected<void> RepoManager::cleanCacheDirGarbage( ProgressObserverRef myProgress )
442  {
443  try {
444  MIL << "Going to clean up garbage in cache dirs" << std::endl;
445 
446  std::list<zypp::Pathname> cachedirs;
447  cachedirs.push_back(_options.repoRawCachePath);
448  cachedirs.push_back(_options.repoPackagesCachePath);
449  cachedirs.push_back(_options.repoSolvCachePath);
450 
451  ProgressObserver::setup( myProgress, _("Cleaning up cache dirs"), cachedirs.size() );
452  ProgressObserver::start( myProgress );
453 
454  for( const auto &dir : cachedirs )
455  {
456  // increase progress on end of every iteration
457  zypp_defer {
458  ProgressObserver::increase( myProgress );
459  };
460 
461  if ( zypp::PathInfo(dir).isExist() )
462  {
463  std::list<zypp::Pathname> entries;
464  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
465  // TranslatorExplanation '%s' is a pathname
466  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
467 
468  if ( !entries.size() )
469  continue;
470 
471  auto dirProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Format( _("Cleaning up directory: %1%") ) % dir, entries.size() );
472  for( const auto &subdir : entries )
473  {
474  // if it does not belong known repo, make it disappear
475  bool found = false;
476  for_( r, repoBegin(), repoEnd() )
477  if ( subdir.basename() == r->escaped_alias() )
478  { found = true; break; }
479 
480  if ( ! found && ( zypp::Date::now()-zypp::PathInfo(subdir).mtime() > zypp::Date::day ) )
482 
483  ProgressObserver::increase( dirProgress );
484  }
485  ProgressObserver::finish( dirProgress );
486  }
487  }
488  } catch (...) {
489  // will finish all subprogress children
490  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
492  }
493  ProgressObserver::finish ( myProgress );
494  return expected<void>::success();
495  }
496 
497 
498  expected<void> RepoManager::cleanCache(const RepoInfo &info, ProgressObserverRef myProgress )
499  {
500  try {
501  ProgressObserver::setup( myProgress, _("Cleaning cache"), 100 );
502  ProgressObserver::start( myProgress );
503 
504  MIL << "Removing raw metadata cache for " << info.alias() << std::endl;
506 
507  ProgressObserver::finish( myProgress );
508  return expected<void>::success();
509 
510  } catch (...) {
511  // will finish all subprogress children
512  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
514  }
515  }
516 
517 
518  expected<void> RepoManager::loadFromCache( const RepoInfo & info, ProgressObserverRef myProgress )
519  {
520  using namespace zyppng::operators;
521  return zyppng::mtry( [this, info, myProgress](){
522  ProgressObserver::setup( myProgress, _("Loading from cache"), 3 );
523  ProgressObserver::start( myProgress );
524 
525  assert_alias(info).unwrap();
526  zypp::Pathname solvfile = solv_path_for_repoinfo(_options, info).unwrap() / "solv";
527 
528  if ( ! zypp::PathInfo(solvfile).isExist() )
530 
531  _zyppContext->satPool().reposErase( info.alias() );
532 
533  ProgressObserver::increase ( myProgress );
534 
535  zypp::Repository repo = _zyppContext->satPool().addRepoSolv( solvfile, info );
536 
537  ProgressObserver::increase ( myProgress );
538 
539  // test toolversion in order to rebuild solv file in case
540  // it was written by a different libsolv-tool parser.
541  const std::string & toolversion( zypp::sat::LookupRepoAttr( zypp::sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
542  if ( toolversion != LIBSOLV_TOOLVERSION ) {
543  repo.eraseFromPool();
544  ZYPP_THROW(zypp::Exception(zypp::str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
545  }
546  })
547  | or_else( [this, info, myProgress]( std::exception_ptr exp ) {
548  ZYPP_CAUGHT( exp );
549  MIL << "Try to handle exception by rebuilding the solv-file" << std::endl;
550  return cleanCache( info, ProgressObserver::makeSubTask( myProgress ) )
551  | and_then([this, info, myProgress]{
552  return buildCache ( info, zypp::RepoManagerFlags::BuildIfNeeded, ProgressObserver::makeSubTask( myProgress ) );
553  })
554  | and_then( mtry([this, info = info]{
555  _zyppContext->satPool().addRepoSolv( solv_path_for_repoinfo(_options, info).unwrap() / "solv", info );
556  }));
557  })
558  | and_then([myProgress]{
559  ProgressObserver::finish ( myProgress );
560  return expected<void>::success();
561  })
562  | or_else([myProgress]( auto ex ){
563  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
564  return expected<void>::error(ex);
565  })
566  ;
567  }
568 
569 
570  expected<RepoInfo> RepoManager::addProbedRepository( RepoInfo info, zypp::repo::RepoType probedType )
571  {
572  try {
573  auto tosave = info;
574 
575  // assert the directory exists
576  zypp::filesystem::assert_dir(_options.knownReposPath);
577 
578  zypp::Pathname repofile = generateNonExistingName(
579  _options.knownReposPath, generateFilename(tosave));
580  // now we have a filename that does not exists
581  MIL << "Saving repo in " << repofile << std::endl;
582 
583  std::ofstream file(repofile.c_str());
584  if (!file)
585  {
586  // TranslatorExplanation '%s' is a filename
587  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
588  }
589 
590  tosave.dumpAsIniOn(file);
591  tosave.setFilepath(repofile);
592  tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ).unwrap() );
593  tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ).unwrap() );
594  reposManip().insert(tosave);
595 
596  // check for credentials in base Urls
597  zypp::UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
598 
599  zypp::HistoryLog(_options.rootDir).addRepository(tosave);
600 
601  // return the new repoinfo
602  return expected<RepoInfo>::success( tosave );
603 
604  } catch (...) {
606  }
607  }
608 
609 
610  expected<void> RepoManager::removeRepository( const RepoInfo & info, ProgressObserverRef myProgress )
611  {
612  try {
613  ProgressObserver::setup( myProgress, zypp::str::form(_("Removing repository '%s'"), info.label().c_str()), 1 );
614  ProgressObserver::start( myProgress );
615 
616  MIL << "Going to delete repo " << info.alias() << std::endl;
617 
618  for( const auto &repo : repos() )
619  {
620  // they can be the same only if the provided is empty, that means
621  // the provided repo has no alias
622  // then skip
623  if ( (!info.alias().empty()) && ( info.alias() != repo.alias() ) )
624  continue;
625 
626  // TODO match by url
627 
628  // we have a matching repository, now we need to know
629  // where it does come from.
630  RepoInfo todelete = repo;
631  if (todelete.filepath().empty())
632  {
633  ZYPP_THROW(zypp::repo::RepoException( todelete, _("Can't figure out where the repo is stored.") ));
634  }
635  else
636  {
637  // figure how many repos are there in the file:
638  std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath()).unwrap();
639  if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
640  ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
641  {
642  // easy: file does not exist, contains no or only the repo to delete: delete the file
643  int ret = zypp::filesystem::unlink( todelete.filepath() );
644  if ( ! ( ret == 0 || ret == ENOENT ) )
645  {
646  // TranslatorExplanation '%s' is a filename
647  ZYPP_THROW(zypp::repo::RepoException( todelete, zypp::str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
648  }
649  MIL << todelete.alias() << " successfully deleted." << std::endl;
650  }
651  else
652  {
653  // there are more repos in the same file
654  // write them back except the deleted one.
655  //TmpFile tmp;
656  //std::ofstream file(tmp.path().c_str());
657 
658  // assert the directory exists
660 
661  std::ofstream file(todelete.filepath().c_str());
662  if (!file)
663  {
664  // TranslatorExplanation '%s' is a filename
665  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
666  }
667  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
668  fit != filerepos.end();
669  ++fit )
670  {
671  if ( (*fit).alias() != todelete.alias() )
672  (*fit).dumpAsIniOn(file);
673  }
674  }
675 
676  // now delete it from cache
677  if ( isCached(todelete) )
678  cleanCache( todelete, ProgressObserver::makeSubTask( myProgress, 0.2 )).unwrap();
679  // now delete metadata (#301037)
680  cleanMetadata( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 )).unwrap();
681  cleanPackages( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 ), true/*isAutoClean*/ ).unwrap();
682  reposManip().erase(todelete);
683  MIL << todelete.alias() << " successfully deleted." << std::endl;
684  zypp::HistoryLog(_options.rootDir).removeRepository(todelete);
685 
686  ProgressObserver::finish(myProgress);
687  return expected<void>::success();
688  } // else filepath is empty
689  }
690  // should not be reached on a sucess workflow
692  } catch (...) {
693  ProgressObserver::finish( myProgress, ProgressObserver::Error );
694  return expected<void>::error( std::current_exception () );
695  }
696  }
697 
698 
699  expected<RepoInfo> RepoManager::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, ProgressObserverRef myProgress )
700  {
701  try {
702 
703  ProgressObserver::setup( myProgress, _("Modifying repository"), 5 );
704  ProgressObserver::start( myProgress );
705 
706  RepoInfo toedit = getRepositoryInfo(alias).unwrap();
707  RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
708 
709  // check if the new alias already exists when renaming the repo
710  if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
711  {
713  }
714 
715  if (toedit.filepath().empty())
716  {
717  ZYPP_THROW(zypp::repo::RepoException( toedit, _("Can't figure out where the repo is stored.") ));
718  }
719  else
720  {
721  ProgressObserver::increase( myProgress );
722  // figure how many repos are there in the file:
723  std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath()).unwrap();
724 
725  // there are more repos in the same file
726  // write them back except the deleted one.
727  //TmpFile tmp;
728  //std::ofstream file(tmp.path().c_str());
729 
730  // assert the directory exists
732 
733  std::ofstream file(toedit.filepath().c_str());
734  if (!file)
735  {
736  // TranslatorExplanation '%s' is a filename
737  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
738  }
739  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
740  fit != filerepos.end();
741  ++fit )
742  {
743  // if the alias is different, dump the original
744  // if it is the same, dump the provided one
745  if ( (*fit).alias() != toedit.alias() )
746  (*fit).dumpAsIniOn(file);
747  else
748  newinfo.dumpAsIniOn(file);
749  }
750 
751  ProgressObserver::increase( myProgress );
752 
753  if ( toedit.enabled() && !newinfo.enabled() )
754  {
755  // On the fly remove solv.idx files for bash completion if a repo gets disabled.
756  const zypp::Pathname solvidx = solv_path_for_repoinfo(_options, newinfo).unwrap()/"solv.idx";
757  if ( zypp::PathInfo(solvidx).isExist() )
758  zypp::filesystem::unlink( solvidx );
759  }
760 
761  newinfo.setFilepath(toedit.filepath());
762  newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ).unwrap() );
763  newinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ).unwrap() );
764 
765  ProgressObserver::increase( myProgress );
766 
767  reposManip().erase(toedit);
768  reposManip().insert(newinfo);
769 
770  ProgressObserver::increase( myProgress );
771 
772  // check for credentials in Urls
773  zypp::UrlCredentialExtractor( _options.rootDir ).collect( newinfo.baseUrls() );
774  zypp::HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
775  MIL << "repo " << alias << " modified" << std::endl;
776 
777  ProgressObserver::finish ( myProgress );
778  return expected<RepoInfo>::success( newinfo );
779  }
780 
781  } catch ( ... ) {
782  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
784  }
785  }
786 
787 
789  {
790  try {
791  RepoConstIterator it( findAlias( alias, repos() ) );
792  if ( it != repos().end() )
793  return make_expected_success(*it);
794  RepoInfo info;
795  info.setAlias( alias );
797  } catch ( ... ) {
798  return expected<RepoInfo>::error( std::current_exception () );
799  }
800  }
801 
802 
803 
805  {
806  try {
807 
808  for_( it, repoBegin(), repoEnd() )
809  {
810  for( const auto &origin : it->repoOrigins() )
811  {
812  if ( std::any_of( origin.begin(), origin.end(), [&url, &urlview]( const zypp::OriginEndpoint &ep ){ return (ep.url().asString(urlview) == url.asString(urlview)); }) )
813  return make_expected_success(*it);
814  }
815  }
816  RepoInfo info;
817  info.setBaseUrl( url );
819 
820  } catch ( ... ) {
821  return expected<RepoInfo>::error( std::current_exception () );
822  }
823  }
824 
825 
827  {
828  using namespace zyppng::operators;
829  return joinPipeline( _zyppContext,
831  | [this, info](auto) { return zyppng::repo::RefreshContext::create( _zyppContext, info, shared_this<RepoManager>() ); }
832  | and_then( [this, origin, policy]( zyppng::repo::RefreshContextRef &&refCtx ) {
833  refCtx->setPolicy ( static_cast<zyppng::repo::RawMetadataRefreshPolicy>( policy ) );
834 
835  return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() )
836  | and_then( [ r = std::move(refCtx) ]( auto mediaHandle ) mutable { return zyppng::RepoManagerWorkflow::checkIfToRefreshMetadata ( std::move(r), std::move(mediaHandle), nullptr ); } );
837  })
838  );
839  }
840 
841 
842  expected<void> RepoManager::refreshMetadata( const RepoInfo &info, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
843  {
844  using namespace zyppng::operators;
845  // helper callback in case the repo type changes on the remote
846  // do NOT capture by reference here, since this is possibly executed async
847  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
848  // update probed type only for repos in system
849  for( const auto &repo : repos() ) {
850  if ( info.alias() == repo.alias() )
851  {
852  RepoInfo modifiedrepo = repo;
853  modifiedrepo.setType( repokind );
854  // don't modify .repo in refresh.
855  // modifyRepository( info.alias(), modifiedrepo );
856  break;
857  }
858  }
859  };
860 
861  // the list of URLs we want to have geo ip redirects for
862  auto urls = info.baseUrls ();
863  if ( info.mirrorListUrl ().isValid () )
864  urls.push_back ( info.mirrorListUrl () );
865 
866  return joinPipeline( _zyppContext,
867  // make sure geoIP data is up 2 date, but ignore errors
869  | [this, info = info](auto) { return zyppng::repo::RefreshContext::create( _zyppContext, info, shared_this<RepoManager>()); }
870  | and_then( [policy, myProgress, cb = updateProbedType]( repo::RefreshContextRef refCtx ) {
871  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
872  // in case probe detects a different repokind, update our internal repos
873  refCtx->connectFunc( &repo::RefreshContext::sigProbedTypeChanged, cb );
874 
875  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), myProgress );
876  })
877  | and_then([rMgr = shared_this<RepoManager>()]( repo::RefreshContextRef ctx ) {
878 
879  if ( ! isTmpRepo( ctx->repoInfo() ) )
880  rMgr->reposManip(); // remember to trigger appdata refresh
881 
882  return expected<void>::success ();
883  }));
884  }
885 
886 
887  std::vector<std::pair<RepoInfo, expected<void>>> RepoManager::refreshMetadata( std::vector<RepoInfo> infos, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
888  {
889  using namespace zyppng::operators;
890 
891  ProgressObserver::setup( myProgress, "Refreshing repositories" , 1 );
892 
893  auto r = std::move(infos)
894  | transform( [this, policy, myProgress]( const RepoInfo &info ) {
895 
896  auto subProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Str() << _("Refreshing Repository: ") << info.alias(), 3 );
897 
898  // helper callback in case the repo type changes on the remote
899  // do NOT capture by reference here, since this is possibly executed async
900  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
901  // update probed type only for repos in system
902  for( const auto &repo : repos() ) {
903  if ( info.alias() == repo.alias() )
904  {
905  RepoInfo modifiedrepo = repo;
906  modifiedrepo.setType( repokind );
907  // don't modify .repo in refresh.
908  // modifyRepository( info.alias(), modifiedrepo );
909  break;
910  }
911  }
912  };
913 
914  auto sharedThis = shared_this<RepoManager>();
915 
916  return
917  // make sure geoIP data is up 2 date, but ignore errors
919  | [sharedThis, info = info](auto) { return zyppng::repo::RefreshContext::create( sharedThis->_zyppContext, info, sharedThis); }
920  | inspect( incProgress( subProgress ) )
921  | and_then( [policy, subProgress, cb = updateProbedType]( repo::RefreshContextRef refCtx ) {
922  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
923  // in case probe detects a different repokind, update our internal repos
924  refCtx->connectFunc( &repo::RefreshContext::sigProbedTypeChanged, cb );
925 
926  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), ProgressObserver::makeSubTask( subProgress ) );
927  })
928  | inspect( incProgress( subProgress ) )
929  | and_then([subProgress]( repo::RefreshContextRef ctx ) {
930 
931  if ( ! isTmpRepo( ctx->repoInfo() ) )
932  ctx->repoManager()->reposManip(); // remember to trigger appdata refresh
933 
934  return zyppng::RepoManagerWorkflow::buildCache ( std::move(ctx), CacheBuildPolicy::BuildIfNeeded, ProgressObserver::makeSubTask( subProgress ) );
935  })
936  | inspect( incProgress( subProgress ) )
937  | [ info = info, subProgress ]( expected<repo::RefreshContextRef> result ) {
938  if ( result ) {
939  ProgressObserver::finish( subProgress, ProgressObserver::Success );
940  return std::make_pair(info, expected<void>::success() );
941  } else {
942  ProgressObserver::finish( subProgress, ProgressObserver::Error );
943  return std::make_pair(info, expected<void>::error( result.error() ) );
944  }
945  };
946  }
947  | [myProgress]( auto res ) {
948  ProgressObserver::finish( myProgress, ProgressObserver::Success );
949  return res;
950  }
951  );
952 
953  return joinPipeline( _zyppContext, r );
954  }
955 
964  {
965  using namespace zyppng::operators;
966 
967  RepoInfo::url_set allUrls;
968  std::transform( origin.begin (), origin.end(), std::back_inserter(allUrls), []( const zypp::OriginEndpoint &ep ){ return ep.url(); } );
969 
970  return joinPipeline( _zyppContext,
972  | [this, origin=origin](auto) { return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() ); }
973  | and_then( [this, path = path]( auto mediaHandle ) {
974  return RepoManagerWorkflow::probeRepoType( _zyppContext, std::move(mediaHandle), path );
975  }));
976  }
977 
978 
979  expected<void> RepoManager::buildCache( const RepoInfo &info, CacheBuildPolicy policy, ProgressObserverRef myProgress )
980  {
981  using namespace zyppng::operators;
982  return joinPipeline( _zyppContext,
983  zyppng::repo::RefreshContext::create( _zyppContext, info, shared_this<RepoManager>() )
984  | and_then( [policy, myProgress]( repo::RefreshContextRef refCtx ) {
985  return zyppng::RepoManagerWorkflow::buildCache ( std::move(refCtx), policy, myProgress );
986  })
987  | and_then([]( auto ){ return expected<void>::success(); })
988  );
989  }
990 
991 
992  expected<RepoInfo> RepoManager::addRepository(const RepoInfo &info, ProgressObserverRef myProgress, const zypp::TriBool & forcedProbe )
993  {
994  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepository( shared_this<RepoManager>(), info, std::move(myProgress), forcedProbe ) );
995  }
996 
997 
998  expected<void> RepoManager::addRepositories(const zypp::Url &url, ProgressObserverRef myProgress)
999  {
1000  using namespace zyppng::operators;
1001  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepositories( shared_this<RepoManager>(), url, std::move(myProgress)));
1002  }
1003 
1004 
1006  {
1008  }
1009 
1010 
1012  {
1013  try {
1014 
1015  assert_alias( service ).unwrap();
1016 
1017  // check if service already exists
1018  if ( hasService( service.alias() ) )
1020 
1021  // Writable ServiceInfo is needed to save the location
1022  // of the .service file. Finaly insert into the service list.
1023  ServiceInfo toSave( service );
1024  saveService( toSave ).unwrap();
1025  _services.insert( toSave );
1026 
1027  // check for credentials in Url
1028  zypp::UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
1029 
1030  MIL << "added service " << toSave.alias() << std::endl;
1031 
1032  } catch ( ... ) {
1033  return expected<void>::error( std::current_exception () );
1034  }
1035 
1036  return expected<void>::success();
1037  }
1038 
1039 
1040  expected<void> RepoManager::refreshService( const std::string &alias, const RefreshServiceOptions &options_r )
1041  {
1042  return joinPipeline ( _zyppContext, RepoServicesWorkflow::refreshService( shared_this<RepoManager>(), getService( alias ), options_r ) );
1043  }
1044 
1050  {
1051  using namespace zyppng::operators;
1052  // copy the set of services since refreshService
1053  // can eventually invalidate the iterator
1054  ServiceSet servicesCopy( serviceBegin(), serviceEnd() );
1055 
1056  // convert the set into a vector, transform needs a container with push_back support
1057  std::vector<ServiceInfo> servicesVec;
1058  std::copy( std::make_move_iterator(servicesCopy.begin()), std::make_move_iterator(servicesCopy.end()), std::back_inserter(servicesVec));
1059 
1060  return joinPipeline( _zyppContext,
1061  std::move(servicesVec)
1062  | transform( [options_r, this]( ServiceInfo i ){ return RepoServicesWorkflow::refreshService( shared_this<RepoManager>(), i, options_r ); } )
1063  | join()
1064  | collect()
1065  );
1066  }
1067 
1069 
1070 
1071  expected<void> RepoManager::removeService( const std::string & alias )
1072  {
1073  try {
1074  MIL << "Going to delete service " << alias << std::endl;
1075 
1076  const ServiceInfo & service = getService( alias );
1077 
1078  zypp::Pathname location = service.filepath();
1079  if( location.empty() )
1080  {
1081  ZYPP_THROW(zypp::repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
1082  }
1083 
1084  ServiceSet tmpSet;
1086 
1087  // only one service definition in the file
1088  if ( tmpSet.size() == 1 )
1089  {
1090  if ( zypp::filesystem::unlink(location) != 0 )
1091  {
1092  // TranslatorExplanation '%s' is a filename
1093  ZYPP_THROW(zypp::repo::ServiceException( service, zypp::str::form( _("Can't delete '%s'"), location.c_str() ) ));
1094  }
1095  MIL << alias << " successfully deleted." << std::endl;
1096  }
1097  else
1098  {
1100 
1101  std::ofstream file(location.c_str());
1102  if( !file )
1103  {
1104  // TranslatorExplanation '%s' is a filename
1105  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), location.c_str() )));
1106  }
1107 
1108  for_(it, tmpSet.begin(), tmpSet.end())
1109  {
1110  if( it->alias() != alias )
1111  it->dumpAsIniOn(file);
1112  }
1113 
1114  MIL << alias << " successfully deleted from file " << location << std::endl;
1115  }
1116 
1117  // now remove all repositories added by this service
1118  RepoCollector rcollector;
1119  getRepositoriesInService( alias,
1120  boost::make_function_output_iterator( std::bind( &RepoCollector::collect, &rcollector, std::placeholders::_1 ) ) );
1121  // cannot do this directly in getRepositoriesInService - would invalidate iterators
1122  for_(rit, rcollector.repos.begin(), rcollector.repos.end())
1123  removeRepository(*rit).unwrap();
1124 
1125  return expected<void>::success();
1126 
1127  } catch ( ... ) {
1128  return expected<void>::error( std::current_exception () );
1129  }
1130  }
1131 
1132 
1133  expected<void> RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
1134  {
1135  try {
1136 
1137  MIL << "Going to modify service " << oldAlias << std::endl;
1138 
1139  // we need a writable copy to link it to the file where
1140  // it is saved if we modify it
1141  ServiceInfo service(newService);
1142 
1143  if ( service.type() == zypp::repo::ServiceType::PLUGIN )
1144  {
1146  }
1147 
1148  const ServiceInfo & oldService = getService(oldAlias);
1149 
1150  zypp::Pathname location = oldService.filepath();
1151  if( location.empty() )
1152  {
1153  ZYPP_THROW(zypp::repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
1154  }
1155 
1156  // remember: there may multiple services being defined in one file:
1157  ServiceSet tmpSet;
1159 
1161  std::ofstream file(location.c_str());
1162  for_(it, tmpSet.begin(), tmpSet.end())
1163  {
1164  if( *it != oldAlias )
1165  it->dumpAsIniOn(file);
1166  }
1167  service.dumpAsIniOn(file);
1168  file.close();
1169  service.setFilepath(location);
1170 
1171  _services.erase(oldAlias);
1172  _services.insert(service);
1173  // check for credentials in Urls
1174  zypp::UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
1175 
1176 
1177  // changed properties affecting also repositories
1178  if ( oldAlias != service.alias() // changed alias
1179  || oldService.enabled() != service.enabled() ) // changed enabled status
1180  {
1181  std::vector<RepoInfo> toModify;
1182  getRepositoriesInService(oldAlias, std::back_inserter(toModify));
1183  for_( it, toModify.begin(), toModify.end() )
1184  {
1185  if ( oldService.enabled() != service.enabled() )
1186  {
1187  if ( service.enabled() )
1188  {
1189  // reset to last refreshs state
1190  const auto & last = service.repoStates().find( it->alias() );
1191  if ( last != service.repoStates().end() )
1192  it->setEnabled( last->second.enabled );
1193  }
1194  else
1195  it->setEnabled( false );
1196  }
1197 
1198  if ( oldAlias != service.alias() )
1199  it->setService(service.alias());
1200 
1201  modifyRepository(it->alias(), *it).unwrap();
1202  }
1203  }
1204 
1205  return expected<void>::success();
1206 
1207  } catch ( ... ) {
1208  return expected<void>::error( std::current_exception () );
1209  }
1210 
1212  }
1213 
1214 
1215 
1216  expected<void> RepoManager::saveService( ServiceInfo & service ) const
1217  {
1218  try {
1219 
1220  zypp::filesystem::assert_dir( _options.knownServicesPath );
1221  zypp::Pathname servfile = generateNonExistingName( _options.knownServicesPath,
1222  generateFilename( service ) );
1223  service.setFilepath( servfile );
1224 
1225  MIL << "saving service in " << servfile << std::endl;
1226 
1227  std::ofstream file( servfile.c_str() );
1228  if ( !file )
1229  {
1230  // TranslatorExplanation '%s' is a filename
1231  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
1232  }
1233  service.dumpAsIniOn( file );
1234  MIL << "done" << std::endl;
1235 
1236  return expected<void>::success();
1237 
1238  } catch ( ... ) {
1239  return expected<void>::error( std::current_exception () );
1240  }
1241  }
1242 
1259  zypp::Pathname RepoManager::generateNonExistingName( const zypp::Pathname & dir,
1260  const std::string & basefilename ) const
1261  {
1262  std::string final_filename = basefilename;
1263  int counter = 1;
1264  while ( zypp::PathInfo(dir + final_filename).isExist() )
1265  {
1266  final_filename = basefilename + "_" + zypp::str::numstring(counter);
1267  ++counter;
1268  }
1269  return dir + zypp::Pathname(final_filename);
1270  }
1271 
1272 
1273  expected<void> RepoManager::touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
1274  {
1275  try {
1276  zypp::Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info ).unwrap();
1277 
1278  zypp::repo::RepoType repokind = info.type();
1279  if ( repokind.toEnum() == zypp::repo::RepoType::NONE_e )
1280  // unknown, probe the local metadata
1281  repokind = probeCache( productdatapath );
1282  // if still unknown, just return
1283  if (repokind == zypp::repo::RepoType::NONE_e)
1284  return expected<void>::success();
1285 
1286  zypp::Pathname p;
1287  switch ( repokind.toEnum() )
1288  {
1290  p = zypp::Pathname(productdatapath + "/repodata/repomd.xml");
1291  break;
1292 
1294  p = zypp::Pathname(productdatapath + "/content");
1295  break;
1296 
1298  p = zypp::Pathname(productdatapath + "/cookie");
1299  break;
1300 
1302  default:
1303  break;
1304  }
1305 
1306  // touch the file, ignore error (they are logged anyway)
1308  } catch ( ... ) {
1310  }
1311  return expected<void>::success();
1312  }
1313 
1315  {
1317  }
1318 
1319  expected<void> RepoManager::touchIndexFile( const RepoInfo & info )
1320  {
1321  return touchIndexFile( info, _options );
1322  }
1323 
1324 
1325  expected<void> RepoManager::init_knownServices()
1326  {
1327  try {
1328  zypp::Pathname dir = _options.knownServicesPath;
1329  std::list<zypp::Pathname> entries;
1330  if (zypp::PathInfo(dir).isExist())
1331  {
1332  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
1333  {
1334  // TranslatorExplanation '%s' is a pathname
1335  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
1336  }
1337 
1338  //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1339  for_(it, entries.begin(), entries.end() )
1340  {
1342  }
1343  }
1344 
1345  zypp::repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
1346 
1347  return expected<void>::success();
1348 
1349  } catch ( ... ) {
1350  return expected<void>::error( std::current_exception () );
1351  }
1352 
1353  }
1354 
1355  namespace {
1362  inline void cleanupNonRepoMetadtaFolders( const zypp::Pathname & cachePath_r,
1363  const zypp::Pathname & defaultCachePath_r,
1364  const std::list<std::string> & repoEscAliases_r )
1365  {
1367  return;
1368 
1369  if ( cachePath_r != defaultCachePath_r )
1370  return;
1371 
1372  std::list<std::string> entries;
1373  if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
1374  {
1375  entries.sort();
1376  std::set<std::string> oldfiles;
1377  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
1378  std::inserter( oldfiles, oldfiles.end() ) );
1379 
1380  // bsc#1178966: Files or symlinks here have been created by the user
1381  // for whatever purpose. It's our cache, so we purge them now before
1382  // they may later conflict with directories we need.
1383  zypp::PathInfo pi;
1384  for ( const std::string & old : oldfiles )
1385  {
1386  if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
1387  continue;
1388  pi( cachePath_r/old );
1389  if ( pi.isDir() )
1391  else
1393  }
1394  }
1395  }
1396  } // namespace
1397 
1398 
1399  expected<void> RepoManager::init_knownRepositories()
1400  {
1401  try {
1402 
1403  MIL << "start construct known repos" << std::endl;
1404 
1405  if ( zypp::PathInfo(_options.knownReposPath).isExist() )
1406  {
1407  std::list<std::string> repoEscAliases;
1408  std::list<RepoInfo> orphanedRepos;
1409  for ( RepoInfo & repoInfo : repositories_in_dir( _zyppContext, _options.knownReposPath ) )
1410  {
1411  // set the metadata path for the repo
1412  repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo).unwrap() );
1413  // set the downloaded packages path for the repo
1414  repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo).unwrap() );
1415  // remember it
1416  _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
1417 
1418  // detect orphaned repos belonging to a deleted service
1419  const std::string & serviceAlias( repoInfo.service() );
1420  if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
1421  {
1422  WAR << "Schedule orphaned service repo for deletion: " << repoInfo << std::endl;
1423  orphanedRepos.push_back( repoInfo );
1424  continue; // don't remember it in repoEscAliases
1425  }
1426 
1427  repoEscAliases.push_back(repoInfo.escaped_alias());
1428  }
1429 
1430  // Cleanup orphanded service repos:
1431  if ( ! orphanedRepos.empty() )
1432  {
1433  for ( const auto & repoInfo : orphanedRepos )
1434  {
1435  MIL << "Delete orphaned service repo " << repoInfo.alias() << std::endl;
1436  // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
1437  // %1% = service name
1438  // %2% = repository name
1439  JobReportHelper(_zyppContext).warning( zypp::str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
1440  % repoInfo.service()
1441  % repoInfo.alias() );
1442  try {
1443  removeRepository( repoInfo ).unwrap();
1444  }
1445  catch ( const zypp::Exception & caugth )
1446  {
1448  }
1449  }
1450  }
1451 
1452  // bsc#1210740: Don't cleanup if read-only mode was promised.
1453  if ( not zypp::zypp_readonly_hack::IGotIt() ) {
1454  // delete metadata folders without corresponding repo (e.g. old tmp directories)
1455  //
1456  // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
1457  // we'd need somemagic file to identify zypp cache directories. Without this
1458  // we may easily remove user data (zypper --pkg-cache-dir . download ...)
1459  repoEscAliases.sort();
1460  cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
1461  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoMetadataPath() ),
1462  repoEscAliases );
1463  cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
1464  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoSolvfilesPath() ),
1465  repoEscAliases );
1466  // bsc#1204956: Tweak to prevent auto pruning package caches
1467  if ( autoPruneInDir( _options.repoPackagesCachePath ) )
1468  cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
1469  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoPackagesPath() ),
1470  repoEscAliases );
1471  }
1472  }
1473  MIL << "end construct known repos" << std::endl;
1474 
1475  return expected<void>::success();
1476 
1477  } catch ( ... ) {
1478  return expected<void>::error( std::current_exception () );
1479  }
1480  }
1481 } // namespace zyppng
Pathname filepath() const
File where this repo was read from.
static const ValueType day
Definition: Date.h:44
void setBaseUrl(Url url)
Clears current base URL list and adds url.
Definition: RepoInfo.cc:722
bool error(std::string msg_r, UserData userData_r=UserData())
send error text
void loadFromCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Load resolvables into the pool.
Definition: RepoManager.cc:171
Service data.
Definition: ServiceInfo.h:36
std::ostream & dumpAsIniOn(std::ostream &str) const override
Writes ServiceInfo to stream in ".service" format.
Definition: ServiceInfo.cc:176
thrown when it was impossible to match a repository
#define MIL
Definition: Logger.h:103
zypp::RepoStatus RepoStatus
Definition: repomanager.h:39
MaybeAwaitable< expected< repo::RefreshContextRef > > refreshMetadata(repo::RefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
#define zypp_defer
Definition: AutoDispose.h:293
std::string targetDistro
Definition: repomanager.h:140
bool empty() const
Whether the status is empty (empty checksum)
Definition: RepoStatus.cc:234
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
fill to output iterator repositories in service name.
Definition: RepoManager.h:651
Namespace intended to collect all environment variables we use.
bool hasRepo(const std::string &alias) const
Return whether there is a known repository for alias.
Definition: RepoManager.cc:104
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:338
repo::RepoType probe(const Url &url, const Pathname &path) const
Probe repo metadata type.
Definition: RepoManager.cc:177
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:251
Read service data from a .service file.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:459
Type toEnum() const
Definition: RepoType.h:49
Regular expression.
Definition: Regex.h:94
std::enable_if_t<!std::is_same_v< void, T >, expected< Container< T >, E > > collect(Container< expected< T, E >, CArgs... > &&in)
Definition: expected.h:586
auto joinPipeline(ContextRef ctx, T &&val)
Definition: context.h:52
MirroredOriginSet repoOrigins() const
The repodata origins.
Definition: RepoInfo.cc:735
std::string asString(const Patch::Category &obj)
relates: Patch::Category string representation.
Definition: Patch.cc:122
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:616
void removeService(const std::string &alias)
Removes service specified by its name.
Definition: RepoManager.cc:254
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: repomanager.h:284
MaybeAwaitable< expected< repo::RefreshContextRef > > buildCache(repo::RefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
void cleanCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
clean local cache
Definition: RepoManager.cc:165
ServiceInfo getService(const std::string &alias) const
Finds ServiceInfo by alias or return ServiceInfo::noService.
Definition: RepoManager.cc:239
expected< std::list< RepoInfo > > repositories_in_file(const zypp::Pathname &file)
Reads RepoInfo&#39;s from a repo file.
Definition: repomanager.cc:167
MaybeAwaitable< expected< void > > refreshService(RepoManagerRef repoMgr, ServiceInfo info, zypp::RepoManagerFlags::RefreshServiceOptions options)
Definition: serviceswf.cc:713
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:182
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy=RefreshIfNeeded)
Checks whether to refresh metadata for specified repository and url.
Definition: RepoManager.cc:133
const char * c_str() const
String representation.
Definition: Pathname.h:113
void setAlias(const std::string &alias)
set the repository alias
Definition: RepoInfoBase.cc:95
std::list< RepoInfo > repositories_in_dir(ZContextRef zyppContext, const zypp::Pathname &dir)
List of RepoInfo&#39;s from a directory.
Definition: repomanager.cc:189
String related utilities and Regular expression matching.
Collector< TOutputIterator > collector(TOutputIterator iter_r)
relates: Collector Convenience constructor.
Definition: Collector.h:55
MaybeAwaitable< expected< RepoInfo > > addRepository(RepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress, const zypp::TriBool &forcedProbe)
void setFilepath(const Pathname &filename)
set the path to the .repo file
bool isTmpRepo(const RepoInfo &info_r)
Whether repo is not under RM control and provides its own methadata paths.
Definition: repomanager.h:49
Definition: ansi.h:854
MaybeAwaitable< expected< zypp::repo::ServiceType > > probeServiceType(ContextRef ctx, const zypp::Url &url)
Definition: serviceswf.cc:231
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:470
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
Service already exists and some unique attribute can&#39;t be duplicated.
Convenient building of std::string with boost::format.
Definition: String.h:253
void buildCache(const RepoInfo &info, CacheBuildPolicy policy=BuildIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local cache.
Definition: RepoManager.cc:158
static expected error(ConsParams &&...params)
Definition: expected.h:189
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:39
bool enabled() const
If enabled is false, then this repository must be ignored as if does not exists, except when checking...
repo::ServiceType probeService(const Url &url) const
Probe the type or the service.
Definition: RepoManager.cc:245
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition: Exception.h:463
Url::asString() view options.
Definition: UrlBase.h:40
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:431
endpoint_iterator end()
~RepoManager()
Dtor.
Definition: RepoManager.cc:86
expected< zypp::Pathname > packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
Definition: repomanager.h:189
Extract credentials in Url authority and store them via CredentialManager.
Repo manager settings.
boost::logic::tribool TriBool
3-state boolean logic (true, false and indeterminate).
Definition: String.h:31
void cleanCacheDirGarbage(const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove any subdirectories of cache directories which no longer belong to any of known repositories...
Definition: RepoManager.cc:174
void refreshService(const std::string &alias, const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refresh specific service.
Definition: RepoManager.cc:263
void refreshMetadata(const RepoInfo &info, RawMetadataRefreshPolicy policy=RefreshIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local raw cache.
Definition: RepoManager.cc:142
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:554
Simple callback to collect the results.
Definition: repomanager.h:128
Url mirrorListUrl() const
Url of a file which contains a list of repository urls.
Definition: RepoInfo.cc:794
void modifyRepository(const std::string &alias, const RepoInfo &newinfo, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Modify repository attributes.
Definition: RepoManager.cc:210
bool empty() const
Test for an empty path.
Definition: Pathname.h:117
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:524
expected< zypp::Pathname > rawproductdata_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw product metadata path for a repository, this is inside the raw cache dir...
Definition: repomanager.h:180
static RepoStatus fromCookieFileUseMtime(const Pathname &path)
Reads the status from a cookie file but uses the files mtime.
Definition: RepoStatus.cc:217
void refreshServices(const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refreshes all enabled services.
Definition: RepoManager.cc:260
void cleanPackages(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local package cache.
Definition: RepoManager.cc:152
RepoInfo getRepositoryInfo(const std::string &alias, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Find a matching repository info.
Definition: RepoManager.cc:221
bool collect(const RepoInfo &repo)
Definition: repomanager.cc:148
void removeRepository(const RepoInfo &repo)
Log recently removed repository.
Definition: HistoryLog.cc:301
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1256
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:212
Lightweight repository attribute value lookup.
Definition: LookupAttr.h:264
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
thrown when it was impossible to determine one url for this repo.
Definition: RepoException.h:78
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:94
std::string alias() const
unique identifier for this source.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
void addService(const std::string &alias, const Url &url)
Adds a new service by its alias and URL.
Definition: RepoManager.cc:248
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:140
static const SolvAttr repositoryToolVersion
Definition: SolvAttr.h:193
expected< void > assert_alias(const RepoInfo &info)
Definition: repomanager.h:52
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:133
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition: Pathname.cc:272
#define WAR
Definition: Logger.h:104
void setMetadataPath(const Pathname &path)
Set the path where the local metadata is stored.
Definition: RepoInfo.cc:755
void setType(const repo::RepoType &t)
set the repository type
Definition: RepoInfo.cc:748
MaybeAwaitable< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::RefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
Definition: repomanager.cc:55
bool repoOriginsEmpty() const
whether repo origins are available
Definition: RepoInfo.cc:740
ContextRef _zyppContext
MaybeAwaitable< expected< void > > refreshGeoIPData(ContextRef ctx, RepoInfo::url_set urls)
void addRepository(const RepoInfo &repo)
Log a newly added repository.
Definition: HistoryLog.cc:289
Iterable< RepoConstIterator > repos() const
Iterate the known repositories.
Definition: RepoManager.h:671
static expected< repo::RefreshContextRef > create(ContextRef zyppContext, zypp::RepoInfo info, RepoManagerRef repoManager)
Definition: refresh.cc:31
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
RepoInfoList repos
Definition: repomanager.h:139
Read repository data from a .repo file.
void modifyService(const std::string &oldAlias, const ServiceInfo &service)
Modifies service file (rewrites it with new values) and underlying repositories if needed...
Definition: RepoManager.cc:269
#define _(MSG)
Definition: Gettext.h:39
bool isCached(const RepoInfo &info) const
Whether a repository exists in cache.
Definition: RepoManager.cc:168
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:624
Base Exception for service handling.
bool isValid() const
Verifies the Url.
Definition: Url.cc:516
std::list< Url > url_set
Definition: RepoInfo.h:108
MaybeAwaitable< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, Provide::LazyMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath)
ServiceConstIterator serviceBegin() const
Iterator to first service in internal storage.
Definition: RepoManager.cc:233
bool requireStatusWithMediaFile() const
Returns true if this repository requires the media.1/media file to be included in the metadata status...
Definition: RepoInfo.cc:1172
std::vector< std::string > Arguments
const std::string & asString() const
Return current Pathname as String.
Definition: PathInfo.h:253
RepoSet::const_iterator RepoConstIterator
Definition: repomanager.h:290
std::string numstring(char n, int w=0)
Definition: String.h:290
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:719
static const RepoType NONE
Definition: RepoType.h:33
static expected success(ConsParams &&...params)
Definition: expected.h:178
ServiceConstIterator serviceEnd() const
Iterator to place behind last service in internal storage.
Definition: RepoManager.cc:236
Url url() const
The service url.
Definition: ServiceInfo.cc:102
void setPackagesPath(const Pathname &path)
set the path where the local packages are stored
Definition: RepoInfo.cc:758
std::ostream & copy(std::istream &from_r, std::ostream &to_r)
Copy istream to ostream.
Definition: IOStream.h:51
bool userMayRX() const
Definition: PathInfo.h:358
static const RepoType RPMMD
Definition: RepoType.h:30
bool autoPruneInDir(const zypp::Pathname &path_r)
bsc#1204956: Tweak to prevent auto pruning package caches.
Definition: repomanager.cc:234
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
#define ZYPP_PRIVATE_CONSTR_ARG
Definition: zyppglobal.h:153
static const RepoType YAST2
Definition: RepoType.h:31
expected< void > assert_urls(const RepoInfo &info)
Definition: repomanager.cc:227
void addRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds a repository to the list of known repositories.
Definition: RepoManager.cc:197
const RepoStates & repoStates() const
Access the remembered repository states.
Definition: ServiceInfo.cc:164
Base class for Exception.
Definition: Exception.h:152
std::ostream & dumpAsIniOn(std::ostream &str) const override
Write this RepoInfo object into str in a .repo file format.
Definition: RepoInfo.cc:1044
Exception for repository handling.
Definition: RepoException.h:37
RepoConstIterator repoBegin() const
Definition: RepoManager.cc:95
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
MaybeAwaitable< expected< void > > addRepositories(RepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:500
Iterator findAlias(const std::string &alias_r, Iterator begin_r, Iterator end_r)
Find alias_r in repo/service container.
Definition: repomanager.h:93
The repository cache is not built yet so you can&#39;t create the repostories from the cache...
Definition: RepoException.h:65
void eraseFromPool()
Remove this Repository from its Pool.
Definition: Repository.cc:298
std::string targetDistribution() const
Distribution for which is this repository meant.
Definition: RepoInfo.cc:830
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
void removeRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove the best matching repository from known repos list.
Definition: RepoManager.cc:203
bool regex_match(const char *s, smatch &matches, const regex &regex) ZYPP_API
Regular expression matching.
Definition: Regex.cc:80
RefreshCheckStatus
Possibly return state of RepoManager::checkIfToRefreshMetadata function.
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:520
auto mtry(F &&f, Args &&...args)
Definition: mtry.h:50
static const RepoType RPMPLAINDIR
Definition: RepoType.h:32
static const std::string & systemRepoAlias()
Reserved system repository alias .
Definition: Repository.cc:38
expected< zypp::Pathname > rawcache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw cache path for a repository, this is usually /var/cache/zypp/alias.
Definition: repomanager.h:166
RepoStatus metadataStatus(const RepoInfo &info) const
Status of local metadata.
Definition: RepoManager.cc:127
Track changing files or directories.
Definition: RepoStatus.h:40
std::string join(const Tltext &ltext, const Trtext &rtext, const char *sep=" ")
Simple join of two string types.
Definition: Out.h:56
Repository already exists and some unique attribute can&#39;t be duplicated.
std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
Generate a related filename from a repo/service infos alias.
Definition: repomanager.cc:137
std::string & replaceAll(std::string &str_r, const std::string &from_r, const std::string &to_r)
Replace all occurrences of from_r with to_r in str_r (inplace).
Definition: String.cc:333
void modifyRepository(const RepoInfo &oldrepo, const RepoInfo &newrepo)
Log certain modifications to a repository.
Definition: HistoryLog.cc:312
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition: Exception.h:471
zypp::RepoManagerFlags::RefreshServiceOptions RefreshServiceOptions
Definition: repomanager.h:256
std::ostream & operator<<(std::ostream &str, zypp::RepoManagerFlags::RawMetadataRefreshPolicy obj)
Definition: repomanager.cc:101
RepoConstIterator repoEnd() const
Definition: RepoManager.cc:98
Represents a single, configurable network endpoint, combining a URL with specific access settings...
void refreshGeoIp(const RepoInfo::url_set &urls)
Definition: RepoManager.cc:272
repo::ServiceType type() const
Service type.
Definition: ServiceInfo.cc:111
endpoint_iterator begin()
std::string label() const
Label for use in messages for the user interface.
url_set baseUrls() const
The complete set of repository urls as configured.
Definition: RepoInfo.cc:818
repo::RepoType type() const
Type of repository,.
Definition: RepoInfo.cc:791
bool collect(const Url &url_r)
Remember credentials stored in URL authority leaving the password in url_r.
auto incProgress(ProgressObserverRef progressObserver, double progrIncrease=1.0, std::optional< std::string > newStr={})
void cleanMetadata(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local metadata.
Definition: RepoManager.cc:149
Functor collecting ServiceInfos into a ServiceSet.
Definition: repomanager.h:209
#define OUTS(V)
Url manipulation class.
Definition: Url.h:92
bool hasService(const std::string &alias) const
Return whether there is a known service for alias.
Definition: RepoManager.cc:242
expected< zypp::Pathname > solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
Definition: repomanager.h:199
auto transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition: transform.h:64
Repository type enumeration.
Definition: RepoType.h:28
bool warning(std::string msg_r, UserData userData_r=UserData())
send warning text
RepoManager::RefreshServiceOptions _options
Definition: serviceswf.cc:698
void addRepositories(const Url &url, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds repositores from a repo file to the list of known repositories.
Definition: RepoManager.cc:200