libzypp  17.32.4
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  // Dir status at last refresh. Plaindir uses the cookiefile as pseudo metadata index file.
200  // It gets touched if the refresh check finds the data being up-to-date. That's why we use
201  // the files mtime as timestamp (like the RepoStatus ctor in the other cases above).
202  status = RepoStatus::fromCookieFileUseMtime( productdatapath/"cookie" );
203  break;
204 
206  // Return default RepoStatus in case of RepoType::NONE
207  // indicating it should be created?
208  // ZYPP_THROW(RepoUnknownTypeException());
209  break;
210  }
211 
212  if ( ! status.empty() )
213  status = status && RepoStatus( info );
214 
215  return status;
216  }
217 
219  {
220  return metadataStatus( info, _options );
221  }
222 
224  {
225  ProgressData progress(100);
226  progress.sendTo(progressfnc);
227  filesystem::recursive_rmdir( ZConfig::instance().geoipCachePath() );
229  progress.toMax();
230  }
231 
232  void RepoManagerBaseImpl::cleanPackages(const RepoInfo &info, const ProgressData::ReceiverFnc & progressfnc, bool isAutoClean )
233  {
234  ProgressData progress(100);
235  progress.sendTo(progressfnc);
236 
237  // bsc#1204956: Tweak to prevent auto pruning package caches
238  const Pathname & rpc { packagescache_path_for_repoinfo(_options, info) };
239  if ( not isAutoClean || autoPruneInDir( rpc.dirname() ) )
241 
242  progress.toMax();
243  }
244 
251  {
252  MIL << "going to probe the cached repo at " << path_r << endl;
253 
255 
256  if ( PathInfo(path_r/"/repodata/repomd.xml").isFile() )
257  { ret = repo::RepoType::RPMMD; }
258  else if ( PathInfo(path_r/"/content").isFile() )
259  { ret = repo::RepoType::YAST2; }
260  else if ( PathInfo(path_r).isDir() )
261  { ret = repo::RepoType::RPMPLAINDIR; }
262 
263  MIL << "Probed cached type " << ret << " at " << path_r << endl;
264  return ret;
265  }
266 
268  {
269  MIL << "Going to clean up garbage in cache dirs" << endl;
270 
271  ProgressData progress(300);
272  progress.sendTo(progressrcv);
273  progress.toMin();
274 
275  std::list<Pathname> cachedirs;
276  cachedirs.push_back(_options.repoRawCachePath);
277  cachedirs.push_back(_options.repoPackagesCachePath);
278  cachedirs.push_back(_options.repoSolvCachePath);
279 
280  for_( dir, cachedirs.begin(), cachedirs.end() )
281  {
282  if ( PathInfo(*dir).isExist() )
283  {
284  std::list<Pathname> entries;
285  if ( filesystem::readdir( entries, *dir, false ) != 0 )
286  // TranslatorExplanation '%s' is a pathname
287  ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
288 
289  unsigned sdircount = entries.size();
290  unsigned sdircurrent = 1;
291  for_( subdir, entries.begin(), entries.end() )
292  {
293  // if it does not belong known repo, make it disappear
294  bool found = false;
295  for_( r, repoBegin(), repoEnd() )
296  if ( subdir->basename() == r->escaped_alias() )
297  { found = true; break; }
298 
299  if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
300  filesystem::recursive_rmdir( *subdir );
301 
302  progress.set( progress.val() + sdircurrent * 100 / sdircount );
303  ++sdircurrent;
304  }
305  }
306  else
307  progress.set( progress.val() + 100 );
308  }
309  progress.toMax();
310  }
311 
313  {
314  ProgressData progress(100);
315  progress.sendTo(progressrcv);
316  progress.toMin();
317 
318  MIL << "Removing raw metadata cache for " << info.alias() << endl;
320 
321  progress.toMax();
322  }
323 
325  {
326  assert_alias(info);
327  Pathname solvfile = solv_path_for_repoinfo(_options, info) / "solv";
328 
329  if ( ! PathInfo(solvfile).isExist() )
331 
333 
334  Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
335  // test toolversion in order to rebuild solv file in case
336  // it was written by a different libsolv-tool parser.
337  const std::string & toolversion( sat::LookupRepoAttr( sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
338  if ( toolversion != LIBSOLV_TOOLVERSION ) {
339  repo.eraseFromPool();
340  ZYPP_THROW(Exception(str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
341  }
342  }
343 
345  {
346 
347  auto tosave = info;
348 
349  // assert the directory exists
351 
354  // now we have a filename that does not exists
355  MIL << "Saving repo in " << repofile << endl;
356 
357  std::ofstream file(repofile.c_str());
358  if (!file)
359  {
360  // TranslatorExplanation '%s' is a filename
361  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
362  }
363 
364  tosave.dumpAsIniOn(file);
365  tosave.setFilepath(repofile);
366  tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
367  tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
368  {
369  // We should fix the API as we must inject those paths
370  // into the repoinfo in order to keep it usable.
371  RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
372  oinfo.setFilepath(repofile);
375  }
376  reposManip().insert(tosave);
377 
378  // check for credentials in Urls
379  UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
380 
382  }
383 
385  {
386  ProgressData progress;
388  progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
389  progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
390 
391  MIL << "Going to delete repo " << info.alias() << endl;
392 
393  for_( it, repoBegin(), repoEnd() )
394  {
395  // they can be the same only if the provided is empty, that means
396  // the provided repo has no alias
397  // then skip
398  if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
399  continue;
400 
401  // TODO match by url
402 
403  // we have a matching repository, now we need to know
404  // where it does come from.
405  RepoInfo todelete = *it;
406  if (todelete.filepath().empty())
407  {
408  ZYPP_THROW(repo::RepoException( todelete, _("Can't figure out where the repo is stored.") ));
409  }
410  else
411  {
412  // figure how many repos are there in the file:
413  std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
414  if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
415  ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
416  {
417  // easy: file does not exist, contains no or only the repo to delete: delete the file
418  int ret = filesystem::unlink( todelete.filepath() );
419  if ( ! ( ret == 0 || ret == ENOENT ) )
420  {
421  // TranslatorExplanation '%s' is a filename
422  ZYPP_THROW(repo::RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
423  }
424  MIL << todelete.alias() << " successfully deleted." << endl;
425  }
426  else
427  {
428  // there are more repos in the same file
429  // write them back except the deleted one.
430  //TmpFile tmp;
431  //std::ofstream file(tmp.path().c_str());
432 
433  // assert the directory exists
435 
436  std::ofstream file(todelete.filepath().c_str());
437  if (!file)
438  {
439  // TranslatorExplanation '%s' is a filename
440  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
441  }
442  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
443  fit != filerepos.end();
444  ++fit )
445  {
446  if ( (*fit).alias() != todelete.alias() )
447  (*fit).dumpAsIniOn(file);
448  }
449  }
450 
451  CombinedProgressData cSubprogrcv(progress, 20);
452  CombinedProgressData mSubprogrcv(progress, 40);
453  CombinedProgressData pSubprogrcv(progress, 40);
454  // now delete it from cache
455  if ( isCached(todelete) )
456  cleanCache( todelete, cSubprogrcv);
457  // now delete metadata (#301037)
458  cleanMetadata( todelete, mSubprogrcv );
459  cleanPackages( todelete, pSubprogrcv, true/*isAutoClean*/ );
460  reposManip().erase(todelete);
461  MIL << todelete.alias() << " successfully deleted." << endl;
463  return;
464  } // else filepath is empty
465 
466  }
467  // should not be reached on a sucess workflow
469  }
470 
471  void RepoManagerBaseImpl::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, const ProgressData::ReceiverFnc & progressrcv )
472  {
473  RepoInfo toedit = getRepositoryInfo(alias);
474  RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
475 
476  // check if the new alias already exists when renaming the repo
477  if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
478  {
480  }
481 
482  if (toedit.filepath().empty())
483  {
484  ZYPP_THROW(repo::RepoException( toedit, _("Can't figure out where the repo is stored.") ));
485  }
486  else
487  {
488  // figure how many repos are there in the file:
489  std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
490 
491  // there are more repos in the same file
492  // write them back except the deleted one.
493  //TmpFile tmp;
494  //std::ofstream file(tmp.path().c_str());
495 
496  // assert the directory exists
498 
499  std::ofstream file(toedit.filepath().c_str());
500  if (!file)
501  {
502  // TranslatorExplanation '%s' is a filename
503  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
504  }
505  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
506  fit != filerepos.end();
507  ++fit )
508  {
509  // if the alias is different, dump the original
510  // if it is the same, dump the provided one
511  if ( (*fit).alias() != toedit.alias() )
512  (*fit).dumpAsIniOn(file);
513  else
514  newinfo.dumpAsIniOn(file);
515  }
516 
517  if ( toedit.enabled() && !newinfo.enabled() )
518  {
519  // On the fly remove solv.idx files for bash completion if a repo gets disabled.
520  const Pathname & solvidx = solv_path_for_repoinfo(_options, newinfo)/"solv.idx";
521  if ( PathInfo(solvidx).isExist() )
522  filesystem::unlink( solvidx );
523  }
524 
525  newinfo.setFilepath(toedit.filepath());
526  newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
528  {
529  // We should fix the API as we must inject those paths
530  // into the repoinfo in order to keep it usable.
531  RepoInfo & oinfo( const_cast<RepoInfo &>(newinfo_r) );
532  oinfo.setFilepath(toedit.filepath());
535  }
536  reposManip().erase(toedit);
537  reposManip().insert(newinfo);
538  // check for credentials in Urls
540  HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
541  MIL << "repo " << alias << " modified" << endl;
542  }
543  }
544 
546  {
547  RepoConstIterator it( findAlias( alias, repos() ) );
548  if ( it != repos().end() )
549  return *it;
550  RepoInfo info;
551  info.setAlias( alias );
553  }
554 
555 
557  {
558  for_( it, repoBegin(), repoEnd() )
559  {
560  for_( urlit, (*it).baseUrlsBegin(), (*it).baseUrlsEnd() )
561  {
562  if ( (*urlit).asString(urlview) == url.asString(urlview) )
563  return *it;
564  }
565  }
566  RepoInfo info;
567  info.setBaseUrl( url );
569  }
570 
572  {
573  assert_alias( service );
574 
575  // check if service already exists
576  if ( hasService( service.alias() ) )
578 
579  // Writable ServiceInfo is needed to save the location
580  // of the .service file. Finaly insert into the service list.
581  ServiceInfo toSave( service );
582  saveService( toSave );
583  _services.insert( toSave );
584 
585  // check for credentials in Url
587 
588  MIL << "added service " << toSave.alias() << endl;
589  }
590 
592 
593  void RepoManagerBaseImpl::removeService( const std::string & alias )
594  {
595  MIL << "Going to delete service " << alias << endl;
596 
597  const ServiceInfo & service = getService( alias );
598 
599  Pathname location = service.filepath();
600  if( location.empty() )
601  {
602  ZYPP_THROW(repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
603  }
604 
605  ServiceSet tmpSet;
606  parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
607 
608  // only one service definition in the file
609  if ( tmpSet.size() == 1 )
610  {
611  if ( filesystem::unlink(location) != 0 )
612  {
613  // TranslatorExplanation '%s' is a filename
614  ZYPP_THROW(repo::ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
615  }
616  MIL << alias << " successfully deleted." << endl;
617  }
618  else
619  {
620  filesystem::assert_dir(location.dirname());
621 
622  std::ofstream file(location.c_str());
623  if( !file )
624  {
625  // TranslatorExplanation '%s' is a filename
626  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
627  }
628 
629  for_(it, tmpSet.begin(), tmpSet.end())
630  {
631  if( it->alias() != alias )
632  it->dumpAsIniOn(file);
633  }
634 
635  MIL << alias << " successfully deleted from file " << location << endl;
636  }
637 
638  // now remove all repositories added by this service
639  RepoCollector rcollector;
641  boost::make_function_output_iterator( bind( &RepoCollector::collect, &rcollector, _1 ) ) );
642  // cannot do this directly in getRepositoriesInService - would invalidate iterators
643  for_(rit, rcollector.repos.begin(), rcollector.repos.end())
644  removeRepository(*rit);
645  }
646 
647  void RepoManagerBaseImpl::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
648  {
649  MIL << "Going to modify service " << oldAlias << endl;
650 
651  // we need a writable copy to link it to the file where
652  // it is saved if we modify it
653  ServiceInfo service(newService);
654 
655  if ( service.type() == repo::ServiceType::PLUGIN )
656  {
658  }
659 
660  const ServiceInfo & oldService = getService(oldAlias);
661 
662  Pathname location = oldService.filepath();
663  if( location.empty() )
664  {
665  ZYPP_THROW(repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
666  }
667 
668  // remember: there may multiple services being defined in one file:
669  ServiceSet tmpSet;
670  parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
671 
672  filesystem::assert_dir(location.dirname());
673  std::ofstream file(location.c_str());
674  for_(it, tmpSet.begin(), tmpSet.end())
675  {
676  if( *it != oldAlias )
677  it->dumpAsIniOn(file);
678  }
679  service.dumpAsIniOn(file);
680  file.close();
681  service.setFilepath(location);
682 
683  _services.erase(oldAlias);
684  _services.insert(service);
685  // check for credentials in Urls
687 
688 
689  // changed properties affecting also repositories
690  if ( oldAlias != service.alias() // changed alias
691  || oldService.enabled() != service.enabled() ) // changed enabled status
692  {
693  std::vector<RepoInfo> toModify;
694  getRepositoriesInService(oldAlias, std::back_inserter(toModify));
695  for_( it, toModify.begin(), toModify.end() )
696  {
697  if ( oldService.enabled() != service.enabled() )
698  {
699  if ( service.enabled() )
700  {
701  // reset to last refreshs state
702  const auto & last = service.repoStates().find( it->alias() );
703  if ( last != service.repoStates().end() )
704  it->setEnabled( last->second.enabled );
705  }
706  else
707  it->setEnabled( false );
708  }
709 
710  if ( oldAlias != service.alias() )
711  it->setService(service.alias());
712 
713  modifyRepository(it->alias(), *it);
714  }
715  }
716 
718  }
719 
720 
722  {
725  generateFilename( service ) );
726  service.setFilepath( servfile );
727 
728  MIL << "saving service in " << servfile << endl;
729 
730  std::ofstream file( servfile.c_str() );
731  if ( !file )
732  {
733  // TranslatorExplanation '%s' is a filename
734  ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
735  }
736  service.dumpAsIniOn( file );
737  MIL << "done" << endl;
738  }
739 
756  const std::string & basefilename ) const
757  {
758  std::string final_filename = basefilename;
759  int counter = 1;
760  while ( PathInfo(dir + final_filename).isExist() )
761  {
762  final_filename = basefilename + "_" + str::numstring(counter);
763  ++counter;
764  }
765  return dir + Pathname(final_filename);
766  }
767 
769  {
770  Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info );
771 
772  repo::RepoType repokind = info.type();
773  if ( repokind.toEnum() == repo::RepoType::NONE_e )
774  // unknown, probe the local metadata
775  repokind = probeCache( productdatapath );
776  // if still unknown, just return
777  if (repokind == repo::RepoType::NONE_e)
778  return;
779 
780  Pathname p;
781  switch ( repokind.toEnum() )
782  {
784  p = Pathname(productdatapath + "/repodata/repomd.xml");
785  break;
786 
788  p = Pathname(productdatapath + "/content");
789  break;
790 
792  p = Pathname(productdatapath + "/cookie");
793  break;
794 
796  default:
797  break;
798  }
799 
800  // touch the file, ignore error (they are logged anyway)
802  }
803 
805  {
806  return touchIndexFile( info, _options );
807  }
808 
810  {
812  std::list<Pathname> entries;
813  if (PathInfo(dir).isExist())
814  {
815  if ( filesystem::readdir( entries, dir, false ) != 0 )
816  {
817  // TranslatorExplanation '%s' is a pathname
818  ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
819  }
820 
821  //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
822  for_(it, entries.begin(), entries.end() )
823  {
825  }
826  }
827 
829  }
830 
831  namespace {
838  inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
839  const Pathname & defaultCachePath_r,
840  const std::list<std::string> & repoEscAliases_r )
841  {
843  return;
844 
845  if ( cachePath_r != defaultCachePath_r )
846  return;
847 
848  std::list<std::string> entries;
849  if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
850  {
851  entries.sort();
852  std::set<std::string> oldfiles;
853  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
854  std::inserter( oldfiles, oldfiles.end() ) );
855 
856  // bsc#1178966: Files or symlinks here have been created by the user
857  // for whatever purpose. It's our cache, so we purge them now before
858  // they may later conflict with directories we need.
859  PathInfo pi;
860  for ( const std::string & old : oldfiles )
861  {
862  if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
863  continue;
864  pi( cachePath_r/old );
865  if ( pi.isDir() )
867  else
868  filesystem::unlink( pi.path() );
869  }
870  }
871  }
872  } // namespace
873 
875  {
876  MIL << "start construct known repos" << endl;
877 
879  {
880  std::list<std::string> repoEscAliases;
881  std::list<RepoInfo> orphanedRepos;
883  {
884  // set the metadata path for the repo
885  repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
886  // set the downloaded packages path for the repo
887  repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
888  // remember it
889  _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
890 
891  // detect orphaned repos belonging to a deleted service
892  const std::string & serviceAlias( repoInfo.service() );
893  if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
894  {
895  WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
896  orphanedRepos.push_back( repoInfo );
897  continue; // don't remember it in repoEscAliases
898  }
899 
900  repoEscAliases.push_back(repoInfo.escaped_alias());
901  }
902 
903  // Cleanup orphanded service repos:
904  if ( ! orphanedRepos.empty() )
905  {
906  for ( const auto & repoInfo : orphanedRepos )
907  {
908  MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
909  // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
910  // %1% = service name
911  // %2% = repository name
912  JobReport::warning( str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
913  % repoInfo.service()
914  % repoInfo.alias() );
915  try {
916  removeRepository( repoInfo );
917  }
918  catch ( const Exception & caugth )
919  {
920  JobReport::error( caugth.asUserHistory() );
921  }
922  }
923  }
924 
925  // bsc#1210740: Don't cleanup if read-only mode was promised.
926  if ( not zypp_readonly_hack::IGotIt() ) {
927  // delete metadata folders without corresponding repo (e.g. old tmp directories)
928  //
929  // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
930  // we'd need somemagic file to identify zypp cache directories. Without this
931  // we may easily remove user data (zypper --pkg-cache-dir . download ...)
932  repoEscAliases.sort();
933  cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
935  repoEscAliases );
936  cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
938  repoEscAliases );
939  // bsc#1204956: Tweak to prevent auto pruning package caches
941  cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
943  repoEscAliases );
944  }
945  }
946  MIL << "end construct known repos" << endl;
947  }
948 
949  void assert_alias(const RepoInfo &info)
950  {
951  if ( info.alias().empty() )
953  // bnc #473834. Maybe we can match the alias against a regex to define
954  // and check for valid aliases
955  if ( info.alias()[0] == '.')
957  info, _("Repository alias cannot start with dot.")));
958  }
959 
960 }
961 
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:234
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...
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 RepoStatus fromCookieFileUseMtime(const Pathname &path)
Reads the status from a cookie file but uses the files mtime.
Definition: RepoStatus.cc:217
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:1238
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: Application.cc:19
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)