libzypp  17.32.2
repomanagerbase_p.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "repomanagerbase_p.h"
11 
12 #include <solv/solvversion.h>
13 
14 #include <zypp-core/base/Regex.h>
15 #include <zypp-core/fs/PathInfo.h>
16 #include <zypp/HistoryLog.h>
17 #include <zypp/ZConfig.h>
18 #include <zypp/ZYppCallbacks.h>
19 #include <zypp/base/LogTools.h>
22 #include <zypp/sat/Pool.h>
24 #include <zypp/repo/ServiceType.h>
26 
27 #include <fstream>
28 #include <utility>
29 
30 namespace zypp
31 {
32 
33  namespace zypp_readonly_hack {
34  bool IGotIt(); // in readonly-mode
35  }
36 
37  namespace {
43  inline void cleanupNonRepoMetadataFolders( const Pathname & cachePath_r,
44  const Pathname & defaultCachePath_r,
45  const std::list<std::string> & repoEscAliases_r )
46  {
47  if ( cachePath_r != defaultCachePath_r )
48  return;
49 
50  std::list<std::string> entries;
51  if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
52  {
53  entries.sort();
54  std::set<std::string> oldfiles;
55  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
56  std::inserter( oldfiles, oldfiles.end() ) );
57 
58  // bsc#1178966: Files or symlinks here have been created by the user
59  // for whatever purpose. It's our cache, so we purge them now before
60  // they may later conflict with directories we need.
61  PathInfo pi;
62  for ( const std::string & old : oldfiles )
63  {
64  if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
65  continue;
66  pi( cachePath_r/old );
67  if ( pi.isDir() )
68  filesystem::recursive_rmdir( pi.path() );
69  else
70  filesystem::unlink( pi.path() );
71  }
72  }
73  }
74  } // namespace
75 
76  std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
77  {
78  std::string filename( alias_r );
79  // replace slashes with underscores
80  str::replaceAll( filename, "/", "_" );
81 
82  filename = Pathname(filename).extend("."+stem_r).asString();
83  MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << endl;
84  return filename;
85  }
86 
87  bool RepoCollector::collect(const RepoInfo &repo)
88  {
89  // skip repositories meant for other distros than specified
90  if (!targetDistro.empty()
91  && !repo.targetDistribution().empty()
92  && repo.targetDistribution() != targetDistro)
93  {
94  MIL
95  << "Skipping repository meant for '" << repo.targetDistribution()
96  << "' distribution (current distro is '"
97  << targetDistro << "')." << endl;
98 
99  return true;
100  }
101 
102  repos.push_back(repo);
103  return true;
104  }
105 
106  std::list<RepoInfo> repositories_in_file(const Pathname &file)
107  {
108  MIL << "repo file: " << file << endl;
109  RepoCollector collector;
110  parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
111  return std::move(collector.repos);
112  }
113 
114  std::list<RepoInfo> repositories_in_dir(const Pathname &dir)
115  {
116  MIL << "directory " << dir << endl;
117  std::list<RepoInfo> repos;
118  bool nonroot( geteuid() != 0 );
119  if ( nonroot && ! PathInfo(dir).userMayRX() )
120  {
121  JobReport::warning( str::Format(_("Cannot read repo directory '%1%': Permission denied")) % dir );
122  }
123  else
124  {
125  std::list<Pathname> entries;
126  if ( filesystem::readdir( entries, dir, false ) != 0 )
127  {
128  // TranslatorExplanation '%s' is a pathname
129  ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
130  }
131 
132  str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
133  for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
134  {
135  if ( str::regex_match(it->extension(), allowedRepoExt) )
136  {
137  if ( nonroot && ! PathInfo(*it).userMayR() )
138  {
139  JobReport::warning( str::Format(_("Cannot read repo file '%1%': Permission denied")) % *it );
140  }
141  else
142  {
143  const std::list<RepoInfo> & tmp( repositories_in_file( *it ) );
144  repos.insert( repos.end(), tmp.begin(), tmp.end() );
145  }
146  }
147  }
148  }
149  return repos;
150  }
151 
152  void assert_urls(const RepoInfo &info)
153  {
154  if ( info.baseUrlsEmpty() )
156  }
157 
158  bool autoPruneInDir(const Pathname &path_r)
159  { return not zypp::PathInfo(path_r/".no_auto_prune").isExist(); }
160 
161 
163  : _options(std::move(opt))
164  { }
165 
167  {
168  //@TODO Add Appdata refresh?
169  }
170 
172  {
173  Pathname mediarootpath = rawcache_path_for_repoinfo( options, info );
174  Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info );
175 
176  repo::RepoType repokind = info.type();
177  // If unknown, probe the local metadata
178  if ( repokind == repo::RepoType::NONE )
179  repokind = probeCache( productdatapath );
180 
181  // NOTE: The calling code expects an empty RepoStatus being returned
182  // if the metadata cache is empty. So additioanl components like the
183  // RepoInfos status are joined after the switch IFF the status is not
184  // empty.
185  RepoStatus status;
186  switch ( repokind.toEnum() )
187  {
189  status = RepoStatus( productdatapath/"repodata/repomd.xml");
190  if ( info.requireStatusWithMediaFile() )
191  status = status && RepoStatus( mediarootpath/"media.1/media" );
192  break;
193 
195  status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
196  break;
197 
199  status = RepoStatus::fromCookieFile( productdatapath/"cookie" ); // dir status at last refresh
200  break;
201 
203  // Return default RepoStatus in case of RepoType::NONE
204  // indicating it should be created?
205  // ZYPP_THROW(RepoUnknownTypeException());
206  break;
207  }
208 
209  if ( ! status.empty() )
210  status = status && RepoStatus( info );
211 
212  return status;
213  }
214 
216  {
217  return metadataStatus( info, _options );
218  }
219 
221  {
222  ProgressData progress(100);
223  progress.sendTo(progressfnc);
224  filesystem::recursive_rmdir( ZConfig::instance().geoipCachePath() );
226  progress.toMax();
227  }
228 
229  void RepoManagerBaseImpl::cleanPackages(const RepoInfo &info, const ProgressData::ReceiverFnc & progressfnc, bool isAutoClean )
230  {
231  ProgressData progress(100);
232  progress.sendTo(progressfnc);
233 
234  // bsc#1204956: Tweak to prevent auto pruning package caches
235  const Pathname & rpc { packagescache_path_for_repoinfo(_options, info) };
236  if ( not isAutoClean || autoPruneInDir( rpc.dirname() ) )
238 
239  progress.toMax();
240  }
241 
248  {
249  MIL << "going to probe the cached repo at " << path_r << endl;
250 
252 
253  if ( PathInfo(path_r/"/repodata/repomd.xml").isFile() )
254  { ret = repo::RepoType::RPMMD; }
255  else if ( PathInfo(path_r/"/content").isFile() )
256  { ret = repo::RepoType::YAST2; }
257  else if ( PathInfo(path_r).isDir() )
258  { ret = repo::RepoType::RPMPLAINDIR; }
259 
260  MIL << "Probed cached type " << ret << " at " << path_r << endl;
261  return ret;
262  }
263 
265  {
266  MIL << "Going to clean up garbage in cache dirs" << endl;
267 
268  ProgressData progress(300);
269  progress.sendTo(progressrcv);
270  progress.toMin();
271 
272  std::list<Pathname> cachedirs;
273  cachedirs.push_back(_options.repoRawCachePath);
274  cachedirs.push_back(_options.repoPackagesCachePath);
275  cachedirs.push_back(_options.repoSolvCachePath);
276 
277  for_( dir, cachedirs.begin(), cachedirs.end() )
278  {
279  if ( PathInfo(*dir).isExist() )
280  {
281  std::list<Pathname> entries;
282  if ( filesystem::readdir( entries, *dir, false ) != 0 )
283  // TranslatorExplanation '%s' is a pathname
284  ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
285 
286  unsigned sdircount = entries.size();
287  unsigned sdircurrent = 1;
288  for_( subdir, entries.begin(), entries.end() )
289  {
290  // if it does not belong known repo, make it disappear
291  bool found = false;
292  for_( r, repoBegin(), repoEnd() )
293  if ( subdir->basename() == r->escaped_alias() )
294  { found = true; break; }
295 
296  if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
297  filesystem::recursive_rmdir( *subdir );
298 
299  progress.set( progress.val() + sdircurrent * 100 / sdircount );
300  ++sdircurrent;
301  }
302  }
303  else
304  progress.set( progress.val() + 100 );
305  }
306  progress.toMax();
307  }
308 
310  {
311  ProgressData progress(100);
312  progress.sendTo(progressrcv);
313  progress.toMin();
314 
315  MIL << "Removing raw metadata cache for " << info.alias() << endl;
317 
318  progress.toMax();
319  }
320 
322  {
323  assert_alias(info);
324  Pathname solvfile = solv_path_for_repoinfo(_options, info) / "solv";
325 
326  if ( ! PathInfo(solvfile).isExist() )
328 
330 
331  Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
332  // test toolversion in order to rebuild solv file in case
333  // it was written by a different libsolv-tool parser.
334  const std::string & toolversion( sat::LookupRepoAttr( sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
335  if ( toolversion != LIBSOLV_TOOLVERSION ) {
336  repo.eraseFromPool();
337  ZYPP_THROW(Exception(str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
338  }
339  }
340 
342  {
343 
344  auto tosave = info;
345 
346  // assert the directory exists
348 
351  // now we have a filename that does not exists
352  MIL << "Saving repo in " << repofile << endl;
353 
354  std::ofstream file(repofile.c_str());
355  if (!file)
356  {
357  // TranslatorExplanation '%s' is a filename
358  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
359  }
360 
361  tosave.dumpAsIniOn(file);
362  tosave.setFilepath(repofile);
363  tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
364  tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
365  {
366  // We should fix the API as we must inject those paths
367  // into the repoinfo in order to keep it usable.
368  RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
369  oinfo.setFilepath(repofile);
372  }
373  reposManip().insert(tosave);
374 
375  // check for credentials in Urls
376  UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
377 
379  }
380 
382  {
383  ProgressData progress;
385  progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
386  progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
387 
388  MIL << "Going to delete repo " << info.alias() << endl;
389 
390  for_( it, repoBegin(), repoEnd() )
391  {
392  // they can be the same only if the provided is empty, that means
393  // the provided repo has no alias
394  // then skip
395  if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
396  continue;
397 
398  // TODO match by url
399 
400  // we have a matching repository, now we need to know
401  // where it does come from.
402  RepoInfo todelete = *it;
403  if (todelete.filepath().empty())
404  {
405  ZYPP_THROW(repo::RepoException( todelete, _("Can't figure out where the repo is stored.") ));
406  }
407  else
408  {
409  // figure how many repos are there in the file:
410  std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
411  if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
412  ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
413  {
414  // easy: file does not exist, contains no or only the repo to delete: delete the file
415  int ret = filesystem::unlink( todelete.filepath() );
416  if ( ! ( ret == 0 || ret == ENOENT ) )
417  {
418  // TranslatorExplanation '%s' is a filename
419  ZYPP_THROW(repo::RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
420  }
421  MIL << todelete.alias() << " successfully deleted." << endl;
422  }
423  else
424  {
425  // there are more repos in the same file
426  // write them back except the deleted one.
427  //TmpFile tmp;
428  //std::ofstream file(tmp.path().c_str());
429 
430  // assert the directory exists
432 
433  std::ofstream file(todelete.filepath().c_str());
434  if (!file)
435  {
436  // TranslatorExplanation '%s' is a filename
437  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
438  }
439  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
440  fit != filerepos.end();
441  ++fit )
442  {
443  if ( (*fit).alias() != todelete.alias() )
444  (*fit).dumpAsIniOn(file);
445  }
446  }
447 
448  CombinedProgressData cSubprogrcv(progress, 20);
449  CombinedProgressData mSubprogrcv(progress, 40);
450  CombinedProgressData pSubprogrcv(progress, 40);
451  // now delete it from cache
452  if ( isCached(todelete) )
453  cleanCache( todelete, cSubprogrcv);
454  // now delete metadata (#301037)
455  cleanMetadata( todelete, mSubprogrcv );
456  cleanPackages( todelete, pSubprogrcv, true/*isAutoClean*/ );
457  reposManip().erase(todelete);
458  MIL << todelete.alias() << " successfully deleted." << endl;
460  return;
461  } // else filepath is empty
462 
463  }
464  // should not be reached on a sucess workflow
466  }
467 
468  void RepoManagerBaseImpl::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, const ProgressData::ReceiverFnc & progressrcv )
469  {
470  RepoInfo toedit = getRepositoryInfo(alias);
471  RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
472 
473  // check if the new alias already exists when renaming the repo
474  if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
475  {
477  }
478 
479  if (toedit.filepath().empty())
480  {
481  ZYPP_THROW(repo::RepoException( toedit, _("Can't figure out where the repo is stored.") ));
482  }
483  else
484  {
485  // figure how many repos are there in the file:
486  std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
487 
488  // there are more repos in the same file
489  // write them back except the deleted one.
490  //TmpFile tmp;
491  //std::ofstream file(tmp.path().c_str());
492 
493  // assert the directory exists
495 
496  std::ofstream file(toedit.filepath().c_str());
497  if (!file)
498  {
499  // TranslatorExplanation '%s' is a filename
500  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
501  }
502  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
503  fit != filerepos.end();
504  ++fit )
505  {
506  // if the alias is different, dump the original
507  // if it is the same, dump the provided one
508  if ( (*fit).alias() != toedit.alias() )
509  (*fit).dumpAsIniOn(file);
510  else
511  newinfo.dumpAsIniOn(file);
512  }
513 
514  if ( toedit.enabled() && !newinfo.enabled() )
515  {
516  // On the fly remove solv.idx files for bash completion if a repo gets disabled.
517  const Pathname & solvidx = solv_path_for_repoinfo(_options, newinfo)/"solv.idx";
518  if ( PathInfo(solvidx).isExist() )
519  filesystem::unlink( solvidx );
520  }
521 
522  newinfo.setFilepath(toedit.filepath());
523  newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
525  {
526  // We should fix the API as we must inject those paths
527  // into the repoinfo in order to keep it usable.
528  RepoInfo & oinfo( const_cast<RepoInfo &>(newinfo_r) );
529  oinfo.setFilepath(toedit.filepath());
532  }
533  reposManip().erase(toedit);
534  reposManip().insert(newinfo);
535  // check for credentials in Urls
537  HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
538  MIL << "repo " << alias << " modified" << endl;
539  }
540  }
541 
543  {
544  RepoConstIterator it( findAlias( alias, repos() ) );
545  if ( it != repos().end() )
546  return *it;
547  RepoInfo info;
548  info.setAlias( alias );
550  }
551 
552 
554  {
555  for_( it, repoBegin(), repoEnd() )
556  {
557  for_( urlit, (*it).baseUrlsBegin(), (*it).baseUrlsEnd() )
558  {
559  if ( (*urlit).asString(urlview) == url.asString(urlview) )
560  return *it;
561  }
562  }
563  RepoInfo info;
564  info.setBaseUrl( url );
566  }
567 
569  {
570  assert_alias( service );
571 
572  // check if service already exists
573  if ( hasService( service.alias() ) )
575 
576  // Writable ServiceInfo is needed to save the location
577  // of the .service file. Finaly insert into the service list.
578  ServiceInfo toSave( service );
579  saveService( toSave );
580  _services.insert( toSave );
581 
582  // check for credentials in Url
584 
585  MIL << "added service " << toSave.alias() << endl;
586  }
587 
589 
590  void RepoManagerBaseImpl::removeService( const std::string & alias )
591  {
592  MIL << "Going to delete service " << alias << endl;
593 
594  const ServiceInfo & service = getService( alias );
595 
596  Pathname location = service.filepath();
597  if( location.empty() )
598  {
599  ZYPP_THROW(repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
600  }
601 
602  ServiceSet tmpSet;
603  parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
604 
605  // only one service definition in the file
606  if ( tmpSet.size() == 1 )
607  {
608  if ( filesystem::unlink(location) != 0 )
609  {
610  // TranslatorExplanation '%s' is a filename
611  ZYPP_THROW(repo::ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
612  }
613  MIL << alias << " successfully deleted." << endl;
614  }
615  else
616  {
617  filesystem::assert_dir(location.dirname());
618 
619  std::ofstream file(location.c_str());
620  if( !file )
621  {
622  // TranslatorExplanation '%s' is a filename
623  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
624  }
625 
626  for_(it, tmpSet.begin(), tmpSet.end())
627  {
628  if( it->alias() != alias )
629  it->dumpAsIniOn(file);
630  }
631 
632  MIL << alias << " successfully deleted from file " << location << endl;
633  }
634 
635  // now remove all repositories added by this service
636  RepoCollector rcollector;
638  boost::make_function_output_iterator( bind( &RepoCollector::collect, &rcollector, _1 ) ) );
639  // cannot do this directly in getRepositoriesInService - would invalidate iterators
640  for_(rit, rcollector.repos.begin(), rcollector.repos.end())
641  removeRepository(*rit);
642  }
643 
644  void RepoManagerBaseImpl::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
645  {
646  MIL << "Going to modify service " << oldAlias << endl;
647 
648  // we need a writable copy to link it to the file where
649  // it is saved if we modify it
650  ServiceInfo service(newService);
651 
652  if ( service.type() == repo::ServiceType::PLUGIN )
653  {
655  }
656 
657  const ServiceInfo & oldService = getService(oldAlias);
658 
659  Pathname location = oldService.filepath();
660  if( location.empty() )
661  {
662  ZYPP_THROW(repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
663  }
664 
665  // remember: there may multiple services being defined in one file:
666  ServiceSet tmpSet;
667  parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
668 
669  filesystem::assert_dir(location.dirname());
670  std::ofstream file(location.c_str());
671  for_(it, tmpSet.begin(), tmpSet.end())
672  {
673  if( *it != oldAlias )
674  it->dumpAsIniOn(file);
675  }
676  service.dumpAsIniOn(file);
677  file.close();
678  service.setFilepath(location);
679 
680  _services.erase(oldAlias);
681  _services.insert(service);
682  // check for credentials in Urls
684 
685 
686  // changed properties affecting also repositories
687  if ( oldAlias != service.alias() // changed alias
688  || oldService.enabled() != service.enabled() ) // changed enabled status
689  {
690  std::vector<RepoInfo> toModify;
691  getRepositoriesInService(oldAlias, std::back_inserter(toModify));
692  for_( it, toModify.begin(), toModify.end() )
693  {
694  if ( oldService.enabled() != service.enabled() )
695  {
696  if ( service.enabled() )
697  {
698  // reset to last refreshs state
699  const auto & last = service.repoStates().find( it->alias() );
700  if ( last != service.repoStates().end() )
701  it->setEnabled( last->second.enabled );
702  }
703  else
704  it->setEnabled( false );
705  }
706 
707  if ( oldAlias != service.alias() )
708  it->setService(service.alias());
709 
710  modifyRepository(it->alias(), *it);
711  }
712  }
713 
715  }
716 
717 
719  {
722  generateFilename( service ) );
723  service.setFilepath( servfile );
724 
725  MIL << "saving service in " << servfile << endl;
726 
727  std::ofstream file( servfile.c_str() );
728  if ( !file )
729  {
730  // TranslatorExplanation '%s' is a filename
731  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
732  }
733  service.dumpAsIniOn( file );
734  MIL << "done" << endl;
735  }
736 
753  const std::string & basefilename ) const
754  {
755  std::string final_filename = basefilename;
756  int counter = 1;
757  while ( PathInfo(dir + final_filename).isExist() )
758  {
759  final_filename = basefilename + "_" + str::numstring(counter);
760  ++counter;
761  }
762  return dir + Pathname(final_filename);
763  }
764 
766  {
767  Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info );
768 
769  repo::RepoType repokind = info.type();
770  if ( repokind.toEnum() == repo::RepoType::NONE_e )
771  // unknown, probe the local metadata
772  repokind = probeCache( productdatapath );
773  // if still unknown, just return
774  if (repokind == repo::RepoType::NONE_e)
775  return;
776 
777  Pathname p;
778  switch ( repokind.toEnum() )
779  {
781  p = Pathname(productdatapath + "/repodata/repomd.xml");
782  break;
783 
785  p = Pathname(productdatapath + "/content");
786  break;
787 
789  p = Pathname(productdatapath + "/cookie");
790  break;
791 
793  default:
794  break;
795  }
796 
797  // touch the file, ignore error (they are logged anyway)
799  }
800 
802  {
803  return touchIndexFile( info, _options );
804  }
805 
807  {
809  std::list<Pathname> entries;
810  if (PathInfo(dir).isExist())
811  {
812  if ( filesystem::readdir( entries, dir, false ) != 0 )
813  {
814  // TranslatorExplanation '%s' is a pathname
815  ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
816  }
817 
818  //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
819  for_(it, entries.begin(), entries.end() )
820  {
822  }
823  }
824 
826  }
827 
828  namespace {
835  inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
836  const Pathname & defaultCachePath_r,
837  const std::list<std::string> & repoEscAliases_r )
838  {
840  return;
841 
842  if ( cachePath_r != defaultCachePath_r )
843  return;
844 
845  std::list<std::string> entries;
846  if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
847  {
848  entries.sort();
849  std::set<std::string> oldfiles;
850  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
851  std::inserter( oldfiles, oldfiles.end() ) );
852 
853  // bsc#1178966: Files or symlinks here have been created by the user
854  // for whatever purpose. It's our cache, so we purge them now before
855  // they may later conflict with directories we need.
856  PathInfo pi;
857  for ( const std::string & old : oldfiles )
858  {
859  if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
860  continue;
861  pi( cachePath_r/old );
862  if ( pi.isDir() )
864  else
865  filesystem::unlink( pi.path() );
866  }
867  }
868  }
869  } // namespace
870 
872  {
873  MIL << "start construct known repos" << endl;
874 
876  {
877  std::list<std::string> repoEscAliases;
878  std::list<RepoInfo> orphanedRepos;
880  {
881  // set the metadata path for the repo
882  repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
883  // set the downloaded packages path for the repo
884  repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
885  // remember it
886  _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
887 
888  // detect orphaned repos belonging to a deleted service
889  const std::string & serviceAlias( repoInfo.service() );
890  if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
891  {
892  WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
893  orphanedRepos.push_back( repoInfo );
894  continue; // don't remember it in repoEscAliases
895  }
896 
897  repoEscAliases.push_back(repoInfo.escaped_alias());
898  }
899 
900  // Cleanup orphanded service repos:
901  if ( ! orphanedRepos.empty() )
902  {
903  for ( const auto & repoInfo : orphanedRepos )
904  {
905  MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
906  // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
907  // %1% = service name
908  // %2% = repository name
909  JobReport::warning( str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
910  % repoInfo.service()
911  % repoInfo.alias() );
912  try {
913  removeRepository( repoInfo );
914  }
915  catch ( const Exception & caugth )
916  {
917  JobReport::error( caugth.asUserHistory() );
918  }
919  }
920  }
921 
922  // bsc#1210740: Don't cleanup if read-only mode was promised.
923  if ( not zypp_readonly_hack::IGotIt() ) {
924  // delete metadata folders without corresponding repo (e.g. old tmp directories)
925  //
926  // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
927  // we'd need somemagic file to identify zypp cache directories. Without this
928  // we may easily remove user data (zypper --pkg-cache-dir . download ...)
929  repoEscAliases.sort();
930  cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
932  repoEscAliases );
933  cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
935  repoEscAliases );
936  // bsc#1204956: Tweak to prevent auto pruning package caches
938  cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
940  repoEscAliases );
941  }
942  }
943  MIL << "end construct known repos" << endl;
944  }
945 
946  void assert_alias(const RepoInfo &info)
947  {
948  if ( info.alias().empty() )
950  // bnc #473834. Maybe we can match the alias against a regex to define
951  // and check for valid aliases
952  if ( info.alias()[0] == '.')
954  info, _("Repository alias cannot start with dot.")));
955  }
956 
957 }
958 
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:122
Pathname filepath() const
File where this repo was read from.
static const ValueType day
Definition: Date.h:44
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:320
void setBaseUrl(Url url)
Clears current base URL list and adds url.
Definition: RepoInfo.cc:556
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:96
Pathname builtinRepoPackagesPath() const
The builtin config file value.
Definition: ZConfig.cc:1100
bool empty() const
Whether the status is empty (empty checksum)
Definition: RepoStatus.cc:222
static bool error(const std::string &msg_r, const UserData &userData_r=UserData())
send error text
Pathname solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
#define _(MSG)
Definition: Gettext.h:37
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:247
Read service data from a .service file.
void sendTo(const ReceiverFnc &fnc_r)
Set ReceiverFnc.
Definition: progressdata.h:229
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:429
Type toEnum() const
Definition: RepoType.h:48
Regular expression.
Definition: Regex.h:94
std::string generateFilename(const RepoInfo &info) const
void removeService(const std::string &alias)
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:925
std::list< RepoInfo > repositories_in_file(const Pathname &file)
Reads RepoInfo&#39;s from a repo file.
bool collect(const RepoInfo &repo)
bool hasRepo(const std::string &alias) const
function< bool(const ProgressData &)> ReceiverFnc
Most simple version of progress reporting The percentage in most cases.
Definition: progressdata.h:140
Pathname builtinRepoMetadataPath() const
The builtin config file value.
Definition: ZConfig.cc:1094
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:173
void loadFromCache(const RepoInfo &info, OPT_PROGRESS)
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
time_t mtime() const
Definition: PathInfo.h:377
const char * c_str() const
String representation.
Definition: Pathname.h:110
void setAlias(const std::string &alias)
set the repository alias
Definition: RepoInfoBase.cc:94
bool toMax()
Set counter value to current max value (unless no range).
Definition: progressdata.h:276
void setFilepath(const Pathname &filename)
set the path to the .repo file
Definition: Arch.h:363
What is known about a repository.
Definition: RepoInfo.h:71
static bool warning(const std::string &msg_r, const UserData &userData_r=UserData())
send warning text
Pathname packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
void reposErase(const std::string &alias_r)
Remove a Repository named alias_r.
Definition: Pool.h:112
Service already exists and some unique attribute can&#39;t be duplicated.
Convenient building of std::string with boost::format.
Definition: String.h:252
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:37
bool enabled() const
If enabled is false, then this repository must be ignored as if does not exists, except when checking...
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
void removeRepositoryImpl(const RepoInfo &info, OPT_PROGRESS)
Url::asString() view options.
Definition: UrlBase.h:39
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.
const RepoSet & repos() const
Extract credentials in Url authority and store them via CredentialManager.
Repo manager settings.
bool baseUrlsEmpty() const
whether repository urls are available
Definition: RepoInfo.cc:656
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:331
Progress callback from another progress.
Definition: progressdata.h:393
bool hasService(const std::string &alias) const
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
bool toMin()
Set counter value to current min value.
Definition: progressdata.h:272
void cleanPackages(const RepoInfo &info, OPT_PROGRESS, bool isAutoClean=false)
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:501
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
void addProbedRepository(const RepoInfo &info, repo::RepoType probedType)
Pathname rootDir
remembers root_r value for later use
void removeRepository(const RepoInfo &repo)
Log recently removed repository.
Definition: HistoryLog.cc:301
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:211
Lightweight repository attribute value lookup.
Definition: LookupAttr.h:263
Pathname generateNonExistingName(const Pathname &dir, const std::string &basefilename) const
Generate a non existing filename in a directory, using a base name.
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:701
thrown when it was impossible to determine one url for this repo.
Definition: RepoException.h:78
void assert_alias(const RepoInfo &info)
Iterator findAlias(const std::string &alias_r, Iterator begin_r, Iterator end_r)
Find alias_r in repo/service container.
const std::string & asString() const
String representation.
Definition: Pathname.h:91
std::string alias() const
unique identifier for this source.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:282
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:110
static const SolvAttr repositoryToolVersion
Definition: SolvAttr.h:181
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...
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
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
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:413
#define WAR
Definition: Logger.h:97
void setMetadataPath(const Pathname &path)
Set the path where the local metadata is stored.
Definition: RepoInfo.cc:575
RepoManagerOptions _options
Maintain [min,max] and counter (value) for progress counting.
Definition: progressdata.h:131
void cleanMetadata(const RepoInfo &info, OPT_PROGRESS)
void addRepository(const RepoInfo &repo)
Log a newly added repository.
Definition: HistoryLog.cc:289
ServiceInfo getService(const std::string &alias) const
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
void addService(const ServiceInfo &service)
Read repository data from a .repo file.
static const ServiceType PLUGIN
Plugin services are scripts installed on your system that provide the package manager with repositori...
Definition: ServiceType.h:43
Base Exception for service handling.
void saveService(ServiceInfo &service) const
bool requireStatusWithMediaFile() const
Returns true if this repository requires the media.1/media file to be included in the metadata status...
Definition: RepoInfo.cc:964
std::string numstring(char n, int w=0)
Definition: String.h:289
static const RepoType NONE
Definition: RepoType.h:32
void cleanCacheDirGarbage(OPT_PROGRESS)
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1235
value_type val() const
Definition: progressdata.h:298
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:578
static const RepoType RPMMD
Definition: RepoType.h:29
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:606
Pathname builtinRepoSolvfilesPath() const
The builtin config file value.
Definition: ZConfig.cc:1097
Simple callback to collect the results.
virtual void removeRepository(const RepoInfo &info, OPT_PROGRESS)=0
static const RepoType YAST2
Definition: RepoType.h:30
thrown when it was impossible to determine an alias for this repo.
Definition: RepoException.h:91
static repo::RepoType probeCache(const Pathname &path_r)
Probe Metadata in a local cache directory.
std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
Generate a related filename from a repo/service infos alias.
static void touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
const RepoStates & repoStates() const
Access the remembered repository states.
Definition: ServiceInfo.cc:164
Base class for Exception.
Definition: Exception.h:146
void assert_urls(const RepoInfo &info)
std::ostream & dumpAsIniOn(std::ostream &str) const override
Write this RepoInfo object into str in a .repo file format.
Definition: RepoInfo.cc:846
void cleanCache(const RepoInfo &info, OPT_PROGRESS)
Exception for repository handling.
Definition: RepoException.h:37
static Date now()
Return the current time.
Definition: Date.h:78
void modifyService(const std::string &oldAlias, const ServiceInfo &newService)
The repository cache is not built yet so you can&#39;t create the repostories from the cache...
Definition: RepoException.h:65
RepoConstIterator repoBegin() const
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:641
Functor collecting ServiceInfos into a ServiceSet.
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
RepoSet::const_iterator RepoConstIterator
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Definition: Regex.h:70
RepoManagerBaseImpl(RepoManagerOptions &&opt)
Thrown when the repo alias is found to be invalid.
static const RepoType RPMPLAINDIR
Definition: RepoType.h:31
static const std::string & systemRepoAlias()
Reserved system repository alias .
Definition: Repository.cc:38
Track changing files or directories.
Definition: RepoStatus.h:40
Repository already exists and some unique attribute can&#39;t be duplicated.
bool set(value_type val_r)
Set new counter value.
Definition: progressdata.h:249
std::list< RepoInfo > repositories_in_dir(const Pathname &dir)
List of RepoInfo&#39;s from a directory.
void modifyRepository(const RepoInfo &oldrepo, const RepoInfo &newrepo)
Log certain modifications to a repository.
Definition: HistoryLog.cc:312
Repository addRepoSolv(const Pathname &file_r, const std::string &name_r)
Load Solvables from a solv-file into a Repository named name_r.
Definition: Pool.cc:185
void name(const std::string &name_r)
Set counter name.
Definition: progressdata.h:225
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
static RepoStatus metadataStatus(const RepoInfo &info, const RepoManagerOptions &options)
RepoConstIterator repoEnd() const
repo::ServiceType type() const
Service type.
Definition: ServiceInfo.cc:111
std::string label() const
Label for use in messages for the user interface.
url_set baseUrls() const
The complete set of repository urls.
Definition: RepoInfo.cc:629
repo::RepoType type() const
Type of repository,.
Definition: RepoInfo.cc:602
bool isCached(const RepoInfo &info) const
bool collect(const Url &url_r)
Remember credentials stored in URL authority leaving the password in url_r.
RepoInfo getRepositoryInfo(const std::string &alias)
Url manipulation class.
Definition: Url.h:91
bool autoPruneInDir(const Pathname &path_r)
bsc#1204956: Tweak to prevent auto pruning package caches.
Repository type enumeration.
Definition: RepoType.h:27
void modifyRepository(const std::string &alias, const RepoInfo &newinfo_r, OPT_PROGRESS)