libzypp 17.38.15
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#include <solv/repo_solv.h>
15
16#include <algorithm>
17#include <zypp/Digest.h>
18
22#include <zypp-core/ng/pipelines/MTry>
23#include <zypp-core/ng/pipelines/Transform>
25#include <zypp-core/ng/ui/ProgressObserver>
27#include <zypp/HistoryLog.h>
28#include <zypp/ZConfig.h>
29#include <zypp/ZYppCallbacks.h>
33#include <zypp/sat/Pool.h>
37
42
43#include <fstream>
44#include <utility>
45
46#undef ZYPP_BASE_LOGGER_LOGGROUP
47#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::repomanager"
48
50 bool IGotIt(); // in readonly-mode
51}
52
53
54namespace zyppng
55{
56 namespace env
57 {
60 {
61 const char * env = getenv("ZYPP_PLUGIN_APPDATA_FORCE_COLLECT");
62 return( env && zypp::str::strToBool( env, true ) );
63 }
64 } // namespace env
65
66 namespace {
72 inline void cleanupNonRepoMetadataFolders( const zypp::Pathname & cachePath_r,
73 const zypp::Pathname & defaultCachePath_r,
74 const std::list<std::string> & repoEscAliases_r )
75 {
76 if ( cachePath_r != defaultCachePath_r )
77 return;
78
79 std::list<std::string> entries;
80 if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
81 {
82 entries.sort();
83 std::set<std::string> oldfiles;
84 set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
85 std::inserter( oldfiles, oldfiles.end() ) );
86
87 // bsc#1178966: Files or symlinks here have been created by the user
88 // for whatever purpose. It's our cache, so we purge them now before
89 // they may later conflict with directories we need.
91 for ( const std::string & old : oldfiles )
92 {
93 if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
94 continue;
95 pi( cachePath_r/old );
96 if ( pi.isDir() )
98 else
100 }
101 }
102 }
103 } // namespace
104
106 {
107 switch ( obj ) {
108#define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
109 OUTS( RefreshIfNeeded );
110 OUTS( RefreshForced );
111 OUTS( RefreshIfNeededIgnoreDelay );
112#undef OUTS
113 }
114 return str;
115 }
116
117 std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::RefreshCheckStatus obj )
118 {
119 switch ( obj ) {
120#define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
121 OUTS( REFRESH_NEEDED );
122 OUTS( REPO_UP_TO_DATE );
123 OUTS( REPO_CHECK_DELAYED );
124#undef OUTS
125 }
126 return str;
127 }
128
129 std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::CacheBuildPolicy obj )
130 {
131 switch ( obj ) {
132#define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
133 OUTS( BuildIfNeeded );
134 OUTS( BuildForced );
135#undef OUTS
136 }
137 return str;
138 }
139
140
141 std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
142 {
143 std::string filename( alias_r );
144 // replace slashes with underscores
145 zypp::str::replaceAll( filename, "/", "_" );
146
147 filename = zypp::Pathname(filename).extend("."+stem_r).asString();
148 MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << std::endl;
149 return filename;
150 }
151
153 {
154 // skip repositories meant for other distros than specified
155 if (!targetDistro.empty()
156 && !repo.targetDistribution().empty()
157 && repo.targetDistribution() != targetDistro)
158 {
159 MIL
160 << "Skipping repository meant for '" << repo.targetDistribution()
161 << "' distribution (current distro is '"
162 << targetDistro << "')." << std::endl;
163
164 return true;
165 }
166
167 repos.push_back(repo);
168 return true;
169 }
170
172 {
173 try {
174 MIL << "repo file: " << file << std::endl;
175 RepoCollector collector;
176 zypp::parser::RepoFileReader parser( file, std::bind( &RepoCollector::collect, &collector, std::placeholders::_1 ) );
177 return expected<std::list<RepoInfo>>::success( std::move(collector.repos) );
178 } catch ( ... ) {
180 }
181 }
182
192 template <typename ZContextRef>
193 std::list<RepoInfo> repositories_in_dir( ZContextRef zyppContext, const zypp::Pathname &dir )
194 {
195 MIL << "directory " << dir << std::endl;
196 std::list<RepoInfo> repos;
197 bool nonroot( geteuid() != 0 );
198 if ( nonroot && ! zypp::PathInfo(dir).userMayRX() )
199 {
200 JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo directory '%1%': Permission denied")) % dir );
201 }
202 else
203 {
204 std::list<zypp::Pathname> entries;
205 if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
206 {
207 // TranslatorExplanation '%s' is a pathname
208 ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
209 }
210
211 zypp::str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
212 for ( std::list<zypp::Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
213 {
214 if ( zypp::str::regex_match(it->extension(), allowedRepoExt) )
215 {
216 if ( nonroot && ! zypp::PathInfo(*it).userMayR() )
217 {
218 JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo file '%1%': Permission denied")) % *it );
219 }
220 else
221 {
222 const std::list<RepoInfo> tmp( repositories_in_file( *it ).unwrap() );
223 repos.insert( repos.end(), tmp.begin(), tmp.end() );
224 }
225 }
226 }
227 }
228 return repos;
229 }
230
232 {
233 if ( info.repoOriginsEmpty() )
236 }
237
238 bool autoPruneInDir(const zypp::Pathname &path_r)
239 { return not zypp::PathInfo(path_r/".no_auto_prune").isExist(); }
240
241
243 : _zyppContext( std::move(zyppCtx) )
244 , _options( std::move(opt) )
245 , _pluginRepoverification( _options.pluginsPath / "repoverification",
246 _options.rootDir)
247 {
248
249 }
250
252 {
253 // trigger appdata refresh if some repos change
255 && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
256 {
257 try {
258 std::list<zypp::Pathname> entries;
259 zypp::filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
260 if ( ! entries.empty() )
261 {
263 cmd.push_back( "<" ); // discard stdin
264 cmd.push_back( ">" ); // discard stdout
265 cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
266 for ( const auto & rinfo : repos() )
267 {
268 if ( ! rinfo.enabled() )
269 continue;
270 cmd.push_back( "-R" );
271 cmd.push_back( rinfo.alias() );
272 cmd.push_back( "-t" );
273 cmd.push_back( rinfo.type().asString() );
274 cmd.push_back( "-p" );
275 cmd.push_back( (rinfo.metadataPath()/rinfo.path()).asString() ); // bsc#1197684: path to the repodata/ directory inside the cache
276 }
277
278 for_( it, entries.begin(), entries.end() )
279 {
280 zypp::PathInfo pi( *it );
281 //DBG << "/tmp/xx ->" << pi << endl;
282 if ( pi.isFile() && pi.userMayRX() )
283 {
284 // trigger plugin
285 cmd[2] = pi.asString(); // [2] - PROGRAM
287 }
288 }
289 }
290 }
291 catch (...) {} // no throw in dtor
292 }
293 }
294
295
297 {
298 using namespace zyppng::operators;
299 return
301 | and_then( [this](){ return init_knownRepositories(); } );
302 }
303
304
306 {
307 return _options;
308 }
309
310
312 {
313 try {
314 using namespace zyppng::operators;
315
316 // ATTENTION when making this pipeline async
317 // consider moving it into a workflow object
318 // this var is caputured by ref to modify it from
319 // inside the pipeline, which would break.
320 zypp::Pathname mediarootpath;
321
323 | and_then( [&]( zypp::Pathname mrPath ) {
324 mediarootpath = std::move(mrPath);
326 })
327 | and_then( [&]( zypp::Pathname productdatapath ) {
328 zypp::repo::RepoType repokind = info.type();
329 // If unknown, probe the local metadata
330 if ( repokind == zypp::repo::RepoType::NONE )
331 repokind = probeCache( productdatapath );
332
333 // NOTE: The calling code expects an empty RepoStatus being returned
334 // if the metadata cache is empty. So additional components like the
335 // RepoInfos status are joined after the switch IFF the status is not
336 // empty.huhu
337 RepoStatus status;
338 switch ( repokind.toEnum() )
339 {
341 status = RepoStatus( productdatapath/"repodata/repomd.xml");
342 if ( info.requireStatusWithMediaFile() )
343 status = status && RepoStatus( mediarootpath/"media.1/media" );
344 break;
345
347 status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
348 break;
349
351 // Dir status at last refresh. Plaindir uses the cookiefile as pseudo metadata index file.
352 // It gets touched if the refresh check finds the data being up-to-date. That's why we use
353 // the files mtime as timestamp (like the RepoStatus ctor in the other cases above).
354 status = RepoStatus::fromCookieFileUseMtime( productdatapath/"cookie" );
355 break;
356
358 // Return default RepoStatus in case of RepoType::NONE
359 // indicating it should be created?
360 // ZYPP_THROW(RepoUnknownTypeException());
361 break;
362 }
363
364 if ( ! status.empty() )
365 status = status && RepoStatus( info );
366
367 return expected<RepoStatus>::success(status);
368 });
369 } catch (...) {
371 }
372 }
373
374
379
380
381 expected<void> RepoManager::cleanMetadata(const RepoInfo &info, ProgressObserverRef myProgress )
382 {
383 try {
384
385 ProgressObserver::setup( myProgress, _("Cleaning metadata"), 100 );
386 ProgressObserver::start( myProgress );
387 zypp::filesystem::recursive_rmdir( _zyppContext->config().geoipCachePath() );
388 ProgressObserver::setCurrent ( myProgress, 50 );
390 ProgressObserver::finish ( myProgress );
391
392 } catch ( ... ) {
395 }
397 }
398
399
400 expected<void> RepoManager::cleanPackages(const RepoInfo &info, ProgressObserverRef myProgress, bool isAutoClean )
401 {
402 try {
403 ProgressObserver::setup( myProgress, _("Cleaning packages"), 100 );
404 ProgressObserver::start( myProgress );
405
406 // bsc#1204956: Tweak to prevent auto pruning package caches
408 if ( not isAutoClean || autoPruneInDir( rpc.dirname() ) )
410
411 ProgressObserver::finish ( myProgress );
412
413 } catch (...) {
416 }
417
419 }
420
426
428 {
429 MIL << "going to probe the cached repo at " << path_r << std::endl;
430
432
433 if ( zypp::PathInfo(path_r/"/repodata/repomd.xml").isFile() )
435 else if ( zypp::PathInfo(path_r/"/content").isFile() )
437 else if ( zypp::PathInfo(path_r/"/cookie").isFile() )
439
440 MIL << "Probed cached type " << ret << " at " << path_r << std::endl;
441 return ret;
442 }
443
444
445 expected<void> RepoManager::cleanCacheDirGarbage( ProgressObserverRef myProgress )
446 {
447 try {
448 MIL << "Going to clean up garbage in cache dirs" << std::endl;
449
450 std::list<zypp::Pathname> cachedirs;
451 cachedirs.push_back(_options.repoRawCachePath);
452 cachedirs.push_back(_options.repoPackagesCachePath);
453 cachedirs.push_back(_options.repoSolvCachePath);
454
455 ProgressObserver::setup( myProgress, _("Cleaning up cache dirs"), cachedirs.size() );
456 ProgressObserver::start( myProgress );
457
458 for( const auto &dir : cachedirs )
459 {
460 // increase progress on end of every iteration
461 zypp_defer {
462 ProgressObserver::increase( myProgress );
463 };
464
465 if ( zypp::PathInfo(dir).isExist() )
466 {
467 std::list<zypp::Pathname> entries;
468 if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
469 // TranslatorExplanation '%s' is a pathname
470 ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
471
472 if ( !entries.size() )
473 continue;
474
475 auto dirProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Format( _("Cleaning up directory: %1%") ) % dir, entries.size() );
476 for( const auto &subdir : entries )
477 {
478 // if it does not belong known repo, make it disappear
479 bool found = false;
480 for_( r, repoBegin(), repoEnd() )
481 if ( subdir.basename() == r->escaped_alias() )
482 { found = true; break; }
483
484 if ( ! found && ( zypp::Date::now()-zypp::PathInfo(subdir).mtime() > zypp::Date::day ) )
486
487 ProgressObserver::increase( dirProgress );
488 }
489 ProgressObserver::finish( dirProgress );
490 }
491 }
492 } catch (...) {
493 // will finish all subprogress children
496 }
497 ProgressObserver::finish ( myProgress );
499 }
500
501
502 expected<void> RepoManager::cleanCache(const RepoInfo &info, ProgressObserverRef myProgress )
503 {
504 try {
505 ProgressObserver::setup( myProgress, _("Cleaning cache"), 100 );
506 ProgressObserver::start( myProgress );
507
508 MIL << "Removing raw metadata cache for " << info.alias() << std::endl;
510
511 ProgressObserver::finish( myProgress );
513
514 } catch (...) {
515 // will finish all subprogress children
518 }
519 }
520
521
522 namespace {
523 // On by default; ZYPP_POOL_SNAPSHOT=0 disables.
524 bool poolSnapshotEnabled()
525 {
526 const char * e = ::getenv( "ZYPP_POOL_SNAPSHOT" );
527 return !( e && strcmp( e, "0" ) == 0 );
528 }
529 }
530
532 {
533 // hash over the sorted aliases and their solv cookie files: any
534 // refresh or change of the repo set invalidates the snapshot,
535 // as does a libsolv or architecture change
536 std::vector<std::string> aliases;
537 std::vector<zypp::Pathname> cookiefiles;
538 for ( const RepoInfo & info : repos() )
539 {
540 if ( ! info.enabled() )
541 continue;
542 try {
543 cookiefiles.push_back( solv_path_for_repoinfo( _options, info ).unwrap() / "cookie" );
544 aliases.push_back( info.alias() );
545 } catch (...) {
546 return std::string();
547 }
548 }
549 aliases.push_back( "@System" );
550 cookiefiles.push_back( _options.repoSolvCachePath / "@System" / "cookie" );
551 // sort both by alias
552 std::vector<size_t> idx( aliases.size() );
553 for ( size_t n = 0; n < idx.size(); n++ ) idx[n] = n;
554 std::sort( idx.begin(), idx.end(), [&]( size_t a, size_t b ){ return aliases[a] < aliases[b]; } );
555
556 zypp::Digest dig;
557 if ( ! dig.create( "sha256" ) )
558 return std::string();
559 static const char version[] = LIBSOLV_TOOLVERSION " " LIBSOLV_VERSION_STRING;
560 dig.update( version, sizeof(version) );
561 const std::string & arch { zypp::ZConfig::instance().systemArchitecture().asString() };
562 dig.update( arch.c_str(), arch.size() );
563 for ( size_t n : idx )
564 {
565 std::ifstream is( cookiefiles[n].c_str() );
566 std::string content { std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>() };
567 if ( content.empty() )
568 {
569 if ( aliases[n] == "@System" )
570 continue; // no target cache yet
571 return std::string(); // repo without cookie, e.g. temporary
572 }
573 dig.update( aliases[n].c_str(), aliases[n].size() + 1 );
574 dig.update( content.c_str(), content.size() );
575 }
576 return dig.digest();
577 }
578
580 {
581 if ( _poolSnapshotState )
582 return _poolSnapshotState > 0;
584 if ( ! poolSnapshotEnabled() )
585 return false;
586 std::string cookie { poolSnapshotCookie() };
587 if ( cookie.empty() )
588 return false;
589 zypp::Pathname path { _options.repoCachePath / "pool.snapshot" };
590 std::vector<std::string> aliases;
591 for ( const RepoInfo & info : repos() )
592 if ( info.enabled() )
593 aliases.push_back( info.alias() );
594 aliases.push_back( "@System" );
595 std::sort( aliases.begin(), aliases.end() );
596 auto pool { _zyppContext->satPool() };
597 pool.setSnapshotCandidate( path, cookie, aliases ); // arm writing after a normal load
598 if ( pool.mapSnapshot( path, cookie ) )
600 return _poolSnapshotState > 0;
601 }
602
604 {
605 if ( _poolIdsReserved )
606 return;
607 _poolIdsReserved = true;
608 unsigned numid = 0, numrel = 0;
609 for ( const RepoInfo & info : repos() )
610 {
611 if ( ! info.enabled() )
612 continue;
613 zypp::Pathname solvfile;
614 try {
615 solvfile = solv_path_for_repoinfo( _options, info ).unwrap() / "solv";
616 } catch (...) {
617 continue;
618 }
619 FILE * fp = ::fopen( solvfile.c_str(), "re" );
620 if ( ! fp )
621 continue;
622 unsigned nid = 0, nrel = 0;
623 if ( ::solv_read_idcounts( fp, &nid, &nrel ) == 0 )
624 {
625 numid += nid;
626 numrel += nrel;
627 }
628 ::fclose( fp );
629 }
630 if ( numid || numrel )
631 {
632 MIL << "Reserving pool ids for " << numid << " strings, " << numrel << " rels" << std::endl;
633 _zyppContext->satPool().reserveIds( numid, numrel );
634 }
635 }
636
637 expected<void> RepoManager::loadFromCache( const RepoInfo & info, ProgressObserverRef myProgress )
638 {
639 using namespace zyppng::operators;
640 return zyppng::mtry( [this, info, myProgress](){
641 ProgressObserver::setup( myProgress, _("Loading from cache"), 3 );
642 ProgressObserver::start( myProgress );
643
644 assert_alias(info).unwrap();
645
646 if ( tryLoadPoolSnapshot() )
647 {
648 zypp::Repository repo = _zyppContext->satPool().reposFind( info.alias() );
649 if ( repo )
650 {
651 MIL << "Repo " << info.alias() << " provided by the pool snapshot" << std::endl;
652 repo.setInfo( info );
653 ProgressObserver::increase( myProgress );
654 ProgressObserver::increase( myProgress );
655 return;
656 }
657 }
658
660 zypp::Pathname solvfile = solv_path_for_repoinfo(_options, info).unwrap() / "solv";
661
662 if ( ! zypp::PathInfo(solvfile).isExist() )
664
665 _zyppContext->satPool().reposErase( info.alias() );
666
667 ProgressObserver::increase ( myProgress );
668
669 zypp::Repository repo = _zyppContext->satPool().addRepoSolv( solvfile, info );
670
671 ProgressObserver::increase ( myProgress );
672
673 // test toolversion in order to rebuild solv file in case
674 // it was written by a different libsolv-tool parser.
675 const std::string & toolversion( zypp::sat::LookupRepoAttr( zypp::sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
676 if ( toolversion != LIBSOLV_TOOLVERSION ) {
677 repo.eraseFromPool();
678 ZYPP_THROW(zypp::Exception(zypp::str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
679 }
680 })
681 | or_else( [this, info, myProgress]( std::exception_ptr exp ) {
682 ZYPP_CAUGHT( exp );
683 MIL << "Try to handle exception by rebuilding the solv-file" << std::endl;
684 return cleanCache( info, ProgressObserver::makeSubTask( myProgress ) )
685 | and_then([this, info, myProgress]{
687 })
688 | and_then( mtry([this, info = info]{
689 _zyppContext->satPool().addRepoSolv( solv_path_for_repoinfo(_options, info).unwrap() / "solv", info );
690 }));
691 })
692 | and_then([myProgress]{
693 ProgressObserver::finish ( myProgress );
695 })
696 | or_else([myProgress]( auto ex ){
698 return expected<void>::error(ex);
699 })
700 ;
701 }
702
703
705 {
706 try {
707 auto tosave = info;
708
709 // assert the directory exists
711
713 _options.knownReposPath, generateFilename(tosave));
714 // now we have a filename that does not exists
715 MIL << "Saving repo in " << repofile << std::endl;
716
717 std::ofstream file(repofile.c_str());
718 if (!file)
719 {
720 // TranslatorExplanation '%s' is a filename
721 ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
722 }
723
724 tosave.dumpAsIniOn(file);
725 tosave.setFilepath(repofile);
726 tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ).unwrap() );
727 tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ).unwrap() );
728 reposManip().insert(tosave);
729
730 // check for credentials in base Urls
731 zypp::UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
732
733 zypp::HistoryLog(_options.rootDir).addRepository(tosave);
734
735 // return the new repoinfo
736 return expected<RepoInfo>::success( tosave );
737
738 } catch (...) {
740 }
741 }
742
743
744 expected<void> RepoManager::removeRepository( const RepoInfo & info, ProgressObserverRef myProgress )
745 {
746 try {
747 ProgressObserver::setup( myProgress, zypp::str::form(_("Removing repository '%s'"), info.label().c_str()), 1 );
748 ProgressObserver::start( myProgress );
749
750 MIL << "Going to delete repo " << info.alias() << std::endl;
751
752 for( const auto &repo : repos() )
753 {
754 // they can be the same only if the provided is empty, that means
755 // the provided repo has no alias
756 // then skip
757 if ( (!info.alias().empty()) && ( info.alias() != repo.alias() ) )
758 continue;
759
760 // TODO match by url
761
762 // we have a matching repository, now we need to know
763 // where it does come from.
764 RepoInfo todelete = repo;
765 if (todelete.filepath().empty())
766 {
767 ZYPP_THROW(zypp::repo::RepoException( todelete, _("Can't figure out where the repo is stored.") ));
768 }
769 else
770 {
771 // figure how many repos are there in the file:
772 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath()).unwrap();
773 if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
774 ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
775 {
776 // easy: file does not exist, contains no or only the repo to delete: delete the file
777 int ret = zypp::filesystem::unlink( todelete.filepath() );
778 if ( ! ( ret == 0 || ret == ENOENT ) )
779 {
780 // TranslatorExplanation '%s' is a filename
781 ZYPP_THROW(zypp::repo::RepoException( todelete, zypp::str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
782 }
783 MIL << todelete.alias() << " successfully deleted." << std::endl;
784 }
785 else
786 {
787 // there are more repos in the same file
788 // write them back except the deleted one.
789 //TmpFile tmp;
790 //std::ofstream file(tmp.path().c_str());
791
792 // assert the directory exists
794
795 std::ofstream file(todelete.filepath().c_str());
796 if (!file)
797 {
798 // TranslatorExplanation '%s' is a filename
799 ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
800 }
801 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
802 fit != filerepos.end();
803 ++fit )
804 {
805 if ( (*fit).alias() != todelete.alias() )
806 (*fit).dumpAsIniOn(file);
807 }
808 }
809
810 // now delete it from cache
811 if ( isCached(todelete) )
812 cleanCache( todelete, ProgressObserver::makeSubTask( myProgress, 0.2 )).unwrap();
813 // now delete metadata (#301037)
814 cleanMetadata( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 )).unwrap();
815 cleanPackages( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 ), true/*isAutoClean*/ ).unwrap();
816 reposManip().erase(todelete);
817 MIL << todelete.alias() << " successfully deleted." << std::endl;
818 zypp::HistoryLog(_options.rootDir).removeRepository(todelete);
819
820 ProgressObserver::finish(myProgress);
822 } // else filepath is empty
823 }
824 // should not be reached on a sucess workflow
826 } catch (...) {
828 return expected<void>::error( std::current_exception () );
829 }
830 }
831
832
833 expected<RepoInfo> RepoManager::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, ProgressObserverRef myProgress )
834 {
835 try {
836
837 ProgressObserver::setup( myProgress, _("Modifying repository"), 5 );
838 ProgressObserver::start( myProgress );
839
840 RepoInfo toedit = getRepositoryInfo(alias).unwrap();
841 RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
842
843 // check if the new alias already exists when renaming the repo
844 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
845 {
847 }
848
849 if (toedit.filepath().empty())
850 {
851 ZYPP_THROW(zypp::repo::RepoException( toedit, _("Can't figure out where the repo is stored.") ));
852 }
853 else
854 {
855 ProgressObserver::increase( myProgress );
856 // figure how many repos are there in the file:
857 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath()).unwrap();
858
859 // there are more repos in the same file
860 // write them back except the deleted one.
861 //TmpFile tmp;
862 //std::ofstream file(tmp.path().c_str());
863
864 // assert the directory exists
866
867 std::ofstream file(toedit.filepath().c_str());
868 if (!file)
869 {
870 // TranslatorExplanation '%s' is a filename
871 ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
872 }
873 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
874 fit != filerepos.end();
875 ++fit )
876 {
877 // if the alias is different, dump the original
878 // if it is the same, dump the provided one
879 if ( (*fit).alias() != toedit.alias() )
880 (*fit).dumpAsIniOn(file);
881 else
882 newinfo.dumpAsIniOn(file);
883 }
884
885 ProgressObserver::increase( myProgress );
886
887 if ( toedit.enabled() && !newinfo.enabled() )
888 {
889 // On the fly remove solv.idx files for bash completion if a repo gets disabled.
890 const zypp::Pathname solvidx = solv_path_for_repoinfo(_options, newinfo).unwrap()/"solv.idx";
891 if ( zypp::PathInfo(solvidx).isExist() )
892 zypp::filesystem::unlink( solvidx );
893 }
894
895 newinfo.setFilepath(toedit.filepath());
896 newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ).unwrap() );
897 newinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ).unwrap() );
898
899 ProgressObserver::increase( myProgress );
900
901 reposManip().erase(toedit);
902 reposManip().insert(newinfo);
903
904 ProgressObserver::increase( myProgress );
905
906 // check for credentials in Urls
908 zypp::HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
909 MIL << "repo " << alias << " modified" << std::endl;
910
911 ProgressObserver::finish ( myProgress );
912 return expected<RepoInfo>::success( newinfo );
913 }
914
915 } catch ( ... ) {
918 }
919 }
920
921
923 {
924 try {
925 RepoConstIterator it( findAlias( alias, repos() ) );
926 if ( it != repos().end() )
927 return make_expected_success(*it);
929 info.setAlias( alias );
931 } catch ( ... ) {
932 return expected<RepoInfo>::error( std::current_exception () );
933 }
934 }
935
936
937
939 {
940 try {
941
942 for_( it, repoBegin(), repoEnd() )
943 {
944 for( const auto &origin : it->repoOrigins() )
945 {
946 if ( std::any_of( origin.begin(), origin.end(), [&url, &urlview]( const zypp::OriginEndpoint &ep ){ return (ep.url().asString(urlview) == url.asString(urlview)); }) )
947 return make_expected_success(*it);
948 }
949 }
951 info.setBaseUrl( url );
953
954 } catch ( ... ) {
955 return expected<RepoInfo>::error( std::current_exception () );
956 }
957 }
958
959
961 {
962 using namespace zyppng::operators;
965 | [this, info](auto) { return zyppng::repo::RefreshContext::create( _zyppContext, info, shared_this<RepoManager>() ); }
966 | and_then( [this, origin, policy]( zyppng::repo::RefreshContextRef &&refCtx ) {
967 refCtx->setPolicy ( static_cast<zyppng::repo::RawMetadataRefreshPolicy>( policy ) );
968
969 return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() )
970 | and_then( [ r = std::move(refCtx) ]( auto mediaHandle ) mutable { return zyppng::RepoManagerWorkflow::checkIfToRefreshMetadata ( std::move(r), std::move(mediaHandle), nullptr ); } );
971 })
972 );
973 }
974
975
976 expected<void> RepoManager::refreshMetadata( const RepoInfo &info, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
977 {
978 using namespace zyppng::operators;
979 // helper callback in case the repo type changes on the remote
980 // do NOT capture by reference here, since this is possibly executed async
981 const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
982 // update probed type only for repos in system
983 for( const auto &repo : repos() ) {
984 if ( info.alias() == repo.alias() )
985 {
986 RepoInfo modifiedrepo = repo;
987 modifiedrepo.setType( repokind );
988 // don't modify .repo in refresh.
989 // modifyRepository( info.alias(), modifiedrepo );
990 break;
991 }
992 }
993 };
994
995 // the list of URLs we want to have geo ip redirects for
996 auto urls = info.baseUrls ();
997 if ( info.mirrorListUrl ().isValid () )
998 urls.push_back ( info.mirrorListUrl () );
999
1000 return joinPipeline( _zyppContext,
1001 // make sure geoIP data is up 2 date, but ignore errors
1004 | and_then( [policy, myProgress, cb = updateProbedType]( repo::RefreshContextRef refCtx ) {
1005 refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
1006 // in case probe detects a different repokind, update our internal repos
1007 refCtx->connectFunc( &repo::RefreshContext::sigProbedTypeChanged, cb );
1008
1009 return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), myProgress );
1010 })
1011 | and_then([rMgr = shared_this<RepoManager>()]( repo::RefreshContextRef ctx ) {
1012
1013 if ( ! isTmpRepo( ctx->repoInfo() ) )
1014 rMgr->reposManip(); // remember to trigger appdata refresh
1015
1016 return expected<void>::success ();
1017 }));
1018 }
1019
1020
1021 std::vector<std::pair<RepoInfo, expected<void>>> RepoManager::refreshMetadata( std::vector<RepoInfo> infos, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
1022 {
1023 using namespace zyppng::operators;
1024
1025 ProgressObserver::setup( myProgress, "Refreshing repositories" , 1 );
1026
1027 auto r = std::move(infos)
1028 | transform( [this, policy, myProgress]( const RepoInfo &info ) {
1029
1030 auto subProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Str() << _("Refreshing Repository: ") << info.alias(), 3 );
1031
1032 // helper callback in case the repo type changes on the remote
1033 // do NOT capture by reference here, since this is possibly executed async
1034 const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
1035 // update probed type only for repos in system
1036 for( const auto &repo : repos() ) {
1037 if ( info.alias() == repo.alias() )
1038 {
1039 RepoInfo modifiedrepo = repo;
1040 modifiedrepo.setType( repokind );
1041 // don't modify .repo in refresh.
1042 // modifyRepository( info.alias(), modifiedrepo );
1043 break;
1044 }
1045 }
1046 };
1047
1048 auto sharedThis = shared_this<RepoManager>();
1049
1050 return
1051 // make sure geoIP data is up 2 date, but ignore errors
1053 | [sharedThis, info = info](auto) { return zyppng::repo::RefreshContext::create( sharedThis->_zyppContext, info, sharedThis); }
1054 | inspect( incProgress( subProgress ) )
1055 | and_then( [policy, subProgress, cb = updateProbedType]( repo::RefreshContextRef refCtx ) {
1056 refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
1057 // in case probe detects a different repokind, update our internal repos
1058 refCtx->connectFunc( &repo::RefreshContext::sigProbedTypeChanged, cb );
1059
1060 return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), ProgressObserver::makeSubTask( subProgress ) );
1061 })
1062 | inspect( incProgress( subProgress ) )
1063 | and_then([subProgress]( repo::RefreshContextRef ctx ) {
1064
1065 if ( ! isTmpRepo( ctx->repoInfo() ) )
1066 ctx->repoManager()->reposManip(); // remember to trigger appdata refresh
1067
1068 return zyppng::RepoManagerWorkflow::buildCache ( std::move(ctx), CacheBuildPolicy::BuildIfNeeded, ProgressObserver::makeSubTask( subProgress ) );
1069 })
1070 | inspect( incProgress( subProgress ) )
1071 | [ info = info, subProgress ]( expected<repo::RefreshContextRef> result ) {
1072 if ( result ) {
1074 return std::make_pair(info, expected<void>::success() );
1075 } else {
1077 return std::make_pair(info, expected<void>::error( result.error() ) );
1078 }
1079 };
1080 }
1081 | [myProgress]( auto res ) {
1083 return res;
1084 }
1085 );
1086
1087 return joinPipeline( _zyppContext, r );
1088 }
1089
1096
1098 {
1099 using namespace zyppng::operators;
1100
1101 RepoInfo::url_set allUrls;
1102 std::transform( origin.begin (), origin.end(), std::back_inserter(allUrls), []( const zypp::OriginEndpoint &ep ){ return ep.url(); } );
1103
1104 return joinPipeline( _zyppContext,
1106 | [this, origin=origin](auto) { return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() ); }
1107 | and_then( [this, path = path]( auto mediaHandle ) {
1108 return RepoManagerWorkflow::probeRepoType( _zyppContext, std::move(mediaHandle), path );
1109 }));
1110 }
1111
1112
1113 expected<void> RepoManager::buildCache( const RepoInfo &info, CacheBuildPolicy policy, ProgressObserverRef myProgress )
1114 {
1115 using namespace zyppng::operators;
1116 return joinPipeline( _zyppContext,
1118 | and_then( [policy, myProgress]( repo::RefreshContextRef refCtx ) {
1119 return zyppng::RepoManagerWorkflow::buildCache ( std::move(refCtx), policy, myProgress );
1120 })
1121 | and_then([]( auto ){ return expected<void>::success(); })
1122 );
1123 }
1124
1125
1126 expected<RepoInfo> RepoManager::addRepository(const RepoInfo &info, ProgressObserverRef myProgress, const zypp::TriBool & forcedProbe )
1127 {
1128 return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepository( shared_this<RepoManager>(), info, std::move(myProgress), forcedProbe ) );
1129 }
1130
1131
1132 expected<void> RepoManager::addRepositories(const zypp::Url &url, ProgressObserverRef myProgress)
1133 {
1134 using namespace zyppng::operators;
1136 }
1137
1138
1143
1144
1146 {
1147 try {
1148
1149 assert_alias( service ).unwrap();
1150
1151 // check if service already exists
1152 if ( hasService( service.alias() ) )
1154
1155 // Writable ServiceInfo is needed to save the location
1156 // of the .service file. Finaly insert into the service list.
1157 ServiceInfo toSave( service );
1158 saveService( toSave ).unwrap();
1159 _services.insert( toSave );
1160
1161 // check for credentials in Url
1162 zypp::UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
1163
1164 MIL << "added service " << toSave.alias() << std::endl;
1165
1166 } catch ( ... ) {
1167 return expected<void>::error( std::current_exception () );
1168 }
1169
1170 return expected<void>::success();
1171 }
1172
1173
1174 expected<void> RepoManager::refreshService( const std::string &alias, const RefreshServiceOptions &options_r )
1175 {
1177 }
1178
1182
1184 {
1185 using namespace zyppng::operators;
1186 // copy the set of services since refreshService
1187 // can eventually invalidate the iterator
1188 ServiceSet servicesCopy( serviceBegin(), serviceEnd() );
1189
1190 // convert the set into a vector, transform needs a container with push_back support
1191 std::vector<ServiceInfo> servicesVec;
1192 std::copy( std::make_move_iterator(servicesCopy.begin()), std::make_move_iterator(servicesCopy.end()), std::back_inserter(servicesVec));
1193
1194 return joinPipeline( _zyppContext,
1195 std::move(servicesVec)
1196 | transform( [options_r, this]( ServiceInfo i ){ return RepoServicesWorkflow::refreshService( shared_this<RepoManager>(), i, options_r ); } )
1197 | join()
1198 | collect()
1199 );
1200 }
1201
1203
1204
1205 expected<void> RepoManager::removeService( const std::string & alias )
1206 {
1207 try {
1208 MIL << "Going to delete service " << alias << std::endl;
1209
1210 const ServiceInfo & service = getService( alias );
1211
1212 zypp::Pathname location = service.filepath();
1213 if( location.empty() )
1214 {
1215 ZYPP_THROW(zypp::repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
1216 }
1217
1218 ServiceSet tmpSet;
1220
1221 // only one service definition in the file
1222 if ( tmpSet.size() == 1 )
1223 {
1224 if ( zypp::filesystem::unlink(location) != 0 )
1225 {
1226 // TranslatorExplanation '%s' is a filename
1227 ZYPP_THROW(zypp::repo::ServiceException( service, zypp::str::form( _("Can't delete '%s'"), location.c_str() ) ));
1228 }
1229 MIL << alias << " successfully deleted." << std::endl;
1230 }
1231 else
1232 {
1234
1235 std::ofstream file(location.c_str());
1236 if( !file )
1237 {
1238 // TranslatorExplanation '%s' is a filename
1239 ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), location.c_str() )));
1240 }
1241
1242 for_(it, tmpSet.begin(), tmpSet.end())
1243 {
1244 if( it->alias() != alias )
1245 it->dumpAsIniOn(file);
1246 }
1247
1248 MIL << alias << " successfully deleted from file " << location << std::endl;
1249 }
1250
1251 // now remove all repositories added by this service
1252 RepoCollector rcollector;
1254 boost::make_function_output_iterator( std::bind( &RepoCollector::collect, &rcollector, std::placeholders::_1 ) ) );
1255 // cannot do this directly in getRepositoriesInService - would invalidate iterators
1256 for_(rit, rcollector.repos.begin(), rcollector.repos.end())
1257 removeRepository(*rit).unwrap();
1258
1259 return expected<void>::success();
1260
1261 } catch ( ... ) {
1262 return expected<void>::error( std::current_exception () );
1263 }
1264 }
1265
1266
1267 expected<void> RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
1268 {
1269 try {
1270
1271 MIL << "Going to modify service " << oldAlias << std::endl;
1272
1273 // we need a writable copy to link it to the file where
1274 // it is saved if we modify it
1275 ServiceInfo service(newService);
1276
1277 if ( service.type() == zypp::repo::ServiceType::PLUGIN )
1278 {
1280 }
1281
1282 const ServiceInfo & oldService = getService(oldAlias);
1283
1284 zypp::Pathname location = oldService.filepath();
1285 if( location.empty() )
1286 {
1287 ZYPP_THROW(zypp::repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
1288 }
1289
1290 // remember: there may multiple services being defined in one file:
1291 ServiceSet tmpSet;
1293
1295 std::ofstream file(location.c_str());
1296 for_(it, tmpSet.begin(), tmpSet.end())
1297 {
1298 if( *it != oldAlias )
1299 it->dumpAsIniOn(file);
1300 }
1301 service.dumpAsIniOn(file);
1302 file.close();
1303 service.setFilepath(location);
1304
1305 _services.erase(oldAlias);
1306 _services.insert(service);
1307 // check for credentials in Urls
1308 zypp::UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
1309
1310
1311 // changed properties affecting also repositories
1312 if ( oldAlias != service.alias() // changed alias
1313 || oldService.enabled() != service.enabled() ) // changed enabled status
1314 {
1315 std::vector<RepoInfo> toModify;
1316 getRepositoriesInService(oldAlias, std::back_inserter(toModify));
1317 for_( it, toModify.begin(), toModify.end() )
1318 {
1319 if ( oldService.enabled() != service.enabled() )
1320 {
1321 if ( service.enabled() )
1322 {
1323 // reset to last refreshs state
1324 const auto & last = service.repoStates().find( it->alias() );
1325 if ( last != service.repoStates().end() )
1326 it->setEnabled( last->second.enabled );
1327 }
1328 else
1329 it->setEnabled( false );
1330 }
1331
1332 if ( oldAlias != service.alias() )
1333 it->setService(service.alias());
1334
1335 modifyRepository(it->alias(), *it).unwrap();
1336 }
1337 }
1338
1339 return expected<void>::success();
1340
1341 } catch ( ... ) {
1342 return expected<void>::error( std::current_exception () );
1343 }
1344
1346 }
1347
1348
1349
1351 {
1352 try {
1353
1354 zypp::filesystem::assert_dir( _options.knownServicesPath );
1355 zypp::Pathname servfile = generateNonExistingName( _options.knownServicesPath,
1356 generateFilename( service ) );
1357 service.setFilepath( servfile );
1358
1359 MIL << "saving service in " << servfile << std::endl;
1360
1361 std::ofstream file( servfile.c_str() );
1362 if ( !file )
1363 {
1364 // TranslatorExplanation '%s' is a filename
1365 ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
1366 }
1367 service.dumpAsIniOn( file );
1368 MIL << "done" << std::endl;
1369
1370 return expected<void>::success();
1371
1372 } catch ( ... ) {
1373 return expected<void>::error( std::current_exception () );
1374 }
1375 }
1376
1392
1394 const std::string & basefilename ) const
1395 {
1396 std::string final_filename = basefilename;
1397 int counter = 1;
1398 while ( zypp::PathInfo(dir + final_filename).isExist() )
1399 {
1400 final_filename = basefilename + "_" + zypp::str::numstring(counter);
1401 ++counter;
1402 }
1403 return dir + zypp::Pathname(final_filename);
1404 }
1405
1406
1408 {
1409 try {
1410 zypp::Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info ).unwrap();
1411
1412 zypp::repo::RepoType repokind = info.type();
1413 if ( repokind.toEnum() == zypp::repo::RepoType::NONE_e )
1414 // unknown, probe the local metadata
1415 repokind = probeCache( productdatapath );
1416 // if still unknown, just return
1417 if (repokind == zypp::repo::RepoType::NONE_e)
1418 return expected<void>::success();
1419
1421 switch ( repokind.toEnum() )
1422 {
1424 p = zypp::Pathname(productdatapath + "/repodata/repomd.xml");
1425 break;
1426
1428 p = zypp::Pathname(productdatapath + "/content");
1429 break;
1430
1432 p = zypp::Pathname(productdatapath + "/cookie");
1433 break;
1434
1436 default:
1437 break;
1438 }
1439
1440 // touch the file, ignore error (they are logged anyway)
1442 } catch ( ... ) {
1444 }
1445 return expected<void>::success();
1446 }
1447
1452
1457
1458
1460 {
1461 try {
1462 zypp::Pathname dir = _options.knownServicesPath;
1463 std::list<zypp::Pathname> entries;
1464 if (zypp::PathInfo(dir).isExist())
1465 {
1466 if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
1467 {
1468 // TranslatorExplanation '%s' is a pathname
1469 ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
1470 }
1471
1472 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1473 for_(it, entries.begin(), entries.end() )
1474 {
1476 }
1477 }
1478
1480
1481 return expected<void>::success();
1482
1483 } catch ( ... ) {
1484 return expected<void>::error( std::current_exception () );
1485 }
1486
1487 }
1488
1489 namespace {
1496 inline void cleanupNonRepoMetadtaFolders( const zypp::Pathname & cachePath_r,
1497 const zypp::Pathname & defaultCachePath_r,
1498 const std::list<std::string> & repoEscAliases_r )
1499 {
1501 return;
1502
1503 if ( cachePath_r != defaultCachePath_r )
1504 return;
1505
1506 std::list<std::string> entries;
1507 if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
1508 {
1509 entries.sort();
1510 std::set<std::string> oldfiles;
1511 set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
1512 std::inserter( oldfiles, oldfiles.end() ) );
1513
1514 // bsc#1178966: Files or symlinks here have been created by the user
1515 // for whatever purpose. It's our cache, so we purge them now before
1516 // they may later conflict with directories we need.
1517 zypp::PathInfo pi;
1518 for ( const std::string & old : oldfiles )
1519 {
1520 if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
1521 continue;
1522 pi( cachePath_r/old );
1523 if ( pi.isDir() )
1525 else
1527 }
1528 }
1529 }
1530 } // namespace
1531
1532
1534 {
1535 try {
1536
1537 MIL << "start construct known repos" << std::endl;
1538
1539 if ( zypp::PathInfo(_options.knownReposPath).isExist() )
1540 {
1541 std::list<std::string> repoEscAliases;
1542 std::list<RepoInfo> orphanedRepos;
1543 for ( RepoInfo & repoInfo : repositories_in_dir( _zyppContext, _options.knownReposPath ) )
1544 {
1545 // set the metadata path for the repo
1546 repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo).unwrap() );
1547 // set the downloaded packages path for the repo
1548 repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo).unwrap() );
1549 // remember it
1550 _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
1551
1552 // detect orphaned repos belonging to a deleted service
1553 const std::string & serviceAlias( repoInfo.service() );
1554 if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
1555 {
1556 WAR << "Schedule orphaned service repo for deletion: " << repoInfo << std::endl;
1557 orphanedRepos.push_back( repoInfo );
1558 continue; // don't remember it in repoEscAliases
1559 }
1560
1561 repoEscAliases.push_back(repoInfo.escaped_alias());
1562 }
1563
1564 // Cleanup orphanded service repos:
1565 if ( ! orphanedRepos.empty() )
1566 {
1567 for ( const auto & repoInfo : orphanedRepos )
1568 {
1569 MIL << "Delete orphaned service repo " << repoInfo.alias() << std::endl;
1570 // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
1571 // %1% = service name
1572 // %2% = repository name
1573 JobReportHelper(_zyppContext).warning( zypp::str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
1574 % repoInfo.service()
1575 % repoInfo.alias() );
1576 try {
1577 removeRepository( repoInfo ).unwrap();
1578 }
1579 catch ( const zypp::Exception & caugth )
1580 {
1582 }
1583 }
1584 }
1585
1586 // bsc#1210740: Don't cleanup if read-only mode was promised.
1588 // delete metadata folders without corresponding repo (e.g. old tmp directories)
1589 //
1590 // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
1591 // we'd need somemagic file to identify zypp cache directories. Without this
1592 // we may easily remove user data (zypper --pkg-cache-dir . download ...)
1593 repoEscAliases.sort();
1594 cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
1595 zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoMetadataPath() ),
1596 repoEscAliases );
1597 cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
1598 zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoSolvfilesPath() ),
1599 repoEscAliases );
1600 // bsc#1204956: Tweak to prevent auto pruning package caches
1601 if ( autoPruneInDir( _options.repoPackagesCachePath ) )
1602 cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
1603 zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoPackagesPath() ),
1604 repoEscAliases );
1605 }
1606 }
1607 MIL << "end construct known repos" << std::endl;
1608
1609 return expected<void>::success();
1610
1611 } catch ( ... ) {
1612 return expected<void>::error( std::current_exception () );
1613 }
1614 }
1615} // namespace zyppng
#define zypp_defer
#define OUTS(VAL)
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:27
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:463
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:471
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:459
#define _(MSG)
Definition Gettext.h:39
#define MIL
Definition Logger.h:130
#define WAR
Definition Logger.h:131
const std::string & asString() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Arch.cc:724
static const ValueType day
Definition Date.h:44
static Date now()
Return the current time.
Definition Date.h:78
Compute Message Digests (MD5, SHA1 etc)
Definition Digest.h:38
std::string digest()
get hex string representation of the digest
Definition Digest.cc:239
bool update(const char *bytes, size_t len)
feed data into digest computation algorithm
Definition Digest.cc:288
bool create(const std::string &name)
initialize creation of a new message digest
Definition Digest.cc:198
Base class for Exception.
Definition Exception.h:153
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition Exception.cc:140
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
std::vector< std::string > Arguments
Writing the zypp history file.
Definition HistoryLog.h:57
void modifyRepository(const RepoInfo &oldrepo, const RepoInfo &newrepo)
Log certain modifications to a repository.
void addRepository(const RepoInfo &repo)
Log a newly added repository.
void removeRepository(const RepoInfo &repo)
Log recently removed repository.
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
endpoint_iterator end()
endpoint_iterator begin()
Represents a single, configurable network endpoint, combining a URL with specific access settings.
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Unless path_r does not already denote a path below root_r, combine them.
Definition Pathname.cc:272
What is known about a repository.
Definition RepoInfo.h:72
void setPackagesPath(const Pathname &path)
set the path where the local packages are stored
Definition RepoInfo.cc:798
url_set baseUrls() const
The complete set of repository urls as configured.
Definition RepoInfo.cc:858
std::ostream & dumpAsIniOn(std::ostream &str) const override
Write this RepoInfo object into str in a .repo file format.
Definition RepoInfo.cc:1087
void setMetadataPath(const Pathname &path)
Set the path where the local metadata is stored.
Definition RepoInfo.cc:795
void setType(const repo::RepoType &t)
set the repository type
Definition RepoInfo.cc:788
std::list< Url > url_set
Definition RepoInfo.h:108
void cleanCacheDirGarbage(const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove any subdirectories of cache directories which no longer belong to any of known repositories.
void cleanMetadata(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local metadata.
bool hasService(const std::string &alias) const
Return whether there is a known service for alias.
void addService(const std::string &alias, const Url &url)
Adds a new service by its alias and URL.
bool isCached(const RepoInfo &info) const
Whether a repository exists in cache.
void removeService(const std::string &alias)
Removes service specified by its name.
repo::ServiceType probeService(const Url &url) const
Probe the type or the service.
void cleanCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
clean local cache
void refreshServices(const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refreshes all enabled services.
void addRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds a repository to the list of known repositories.
void addRepositories(const Url &url, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds repositores from a repo file to the list of known repositories.
void refreshMetadata(const RepoInfo &info, RawMetadataRefreshPolicy policy=RefreshIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local raw cache.
void refreshGeoIp(const RepoInfo::url_set &urls)
void refreshService(const std::string &alias, const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refresh specific service.
ServiceConstIterator serviceEnd() const
Iterator to place behind last service in internal storage.
ServiceConstIterator serviceBegin() const
Iterator to first service in internal storage.
void modifyRepository(const std::string &alias, const RepoInfo &newinfo, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Modify repository attributes.
void removeRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove the best matching repository from known repos list.
RepoInfo getRepositoryInfo(const std::string &alias, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Find a matching repository info.
ServiceInfo getService(const std::string &alias) const
Finds ServiceInfo by alias or return ServiceInfo::noService.
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy=RefreshIfNeeded)
Checks whether to refresh metadata for specified repository and url.
RepoConstIterator repoBegin() const
Iterable< RepoConstIterator > repos() const
Iterate the known repositories.
void buildCache(const RepoInfo &info, CacheBuildPolicy policy=BuildIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local cache.
RepoManager(RepoManagerOptions options=RepoManagerOptions())
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
fill to output iterator repositories in service name.
void loadFromCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Load resolvables into the pool.
void cleanPackages(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local package cache.
RepoConstIterator repoEnd() const
RepoStatus metadataStatus(const RepoInfo &info) const
Status of local metadata.
void modifyService(const std::string &oldAlias, const ServiceInfo &service)
Modifies service file (rewrites it with new values) and underlying repositories if needed.
repo::RepoType probe(const Url &url, const Pathname &path) const
Probe repo metadata type.
Track changing files or directories.
Definition RepoStatus.h:41
static RepoStatus fromCookieFileUseMtime(const Pathname &path)
Reads the status from a cookie file but uses the files mtime.
bool empty() const
Whether the status is empty (empty checksum)
static const std::string & systemRepoAlias()
Reserved system repository alias @System .
Definition Repository.cc:43
Service data.
Definition ServiceInfo.h:37
repo::ServiceType type() const
Service type.
const RepoStates & repoStates() const
Access the remembered repository states.
Url url() const
The service url.
std::ostream & dumpAsIniOn(std::ostream &str) const override
Writes ServiceInfo to stream in ".service" format.
Extract credentials in Url authority and store them via CredentialManager.
bool collect(const Url &url_r)
Remember credentials stored in URL authority leaving the password in url_r.
Url manipulation class.
Definition Url.h:93
Arch systemArchitecture() const
The system architecture zypp uses.
Definition ZConfig.cc:857
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:794
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const Pathname & path() const
Return current Pathname.
Definition PathInfo.h:251
bool isExist() const
Return whether valid stat info exists.
Definition PathInfo.h:286
const std::string & asString() const
Return current Pathname as String.
Definition PathInfo.h:253
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition Pathname.h:192
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:133
const char * c_str() const
String representation.
Definition Pathname.h:113
const std::string & asString() const
String representation.
Definition Pathname.h:94
bool empty() const
Test for an empty path.
Definition Pathname.h:117
Read repository data from a .repo file.
Read service data from a .service file.
Repository already exists and some unique attribute can't be duplicated.
Exception for repository handling.
void setFilepath(const Pathname &filename)
set the path to the .repo file
Pathname filepath() const
File where this repo was read from.
bool enabled() const
If enabled is false, then this repository must be ignored as if does not exists, except when checking...
std::string alias() const
unique identifier for this source.
thrown when it was impossible to determine one url for this repo.
The repository cache is not built yet so you can't create the repostories from the cache.
thrown when it was impossible to match a repository
Service already exists and some unique attribute can't be duplicated.
Base Exception for service handling.
Lightweight repository attribute value lookup.
Definition LookupAttr.h:265
static const SolvAttr repositoryToolVersion
Definition SolvAttr.h:193
Regular expression.
Definition Regex.h:95
std::shared_ptr< T > shared_this() const
Definition base.h:114
bool error(std::string msg_r, UserData userData_r=UserData())
send error text
bool warning(std::string msg_r, UserData userData_r=UserData())
send warning text
static void increase(ProgressObserverRef progress, double inc=1.0, const std::optional< std::string > &newLabel={})
static ProgressObserverRef makeSubTask(ProgressObserverRef parentProgress, float weight=1.0, const std::string &label=std::string(), int steps=100)
static void setup(ProgressObserverRef progress, const std::string &label=std::string(), int steps=100)
static void finish(ProgressObserverRef progress, ProgressObserver::FinishResult result=ProgressObserver::Success)
std::string poolSnapshotCookie() const
Cookie describing the enabled repos and their solv caches, empty if the pool snapshot cannot be used.
RepoSet::const_iterator RepoConstIterator
expected< RepoInfo > addProbedRepository(RepoInfo info, zypp::repo::RepoType probedType)
std::string generateFilename(const RepoInfo &info) const
expected< void > init_knownServices()
zypp::RepoManagerFlags::RefreshServiceOptions RefreshServiceOptions
zypp::DefaultIntegral< bool, false > _reposDirty
bool hasRepo(const std::string &alias) const
expected< void > saveService(ServiceInfo &service) const
zypp::RepoManagerFlags::RawMetadataRefreshPolicy RawMetadataRefreshPolicy
static zypp::repo::RepoType probeCache(const zypp::Pathname &path_r)
Probe Metadata in a local cache directory.
zypp::Pathname generateNonExistingName(const zypp::Pathname &dir, const std::string &basefilename) const
Generate a non existing filename in a directory, using a base name.
ContextRefType _zyppContext
static expected< void > touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
expected< void > initialize()
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
RepoSet & reposManip()
RepoManagerOptions _options
expected< void > init_knownRepositories()
int _poolSnapshotState
0 unknown, 1 mapped, -1 unusable
bool tryLoadPoolSnapshot()
Try to map the pool snapshot on the first loadFromCache, and arm writing a fresh one after a normal l...
void reservePoolIds()
Peek at the solv files of all enabled repos and size the pool's id hashes once for their sum,...
bool hasService(const std::string &alias) const
const RepoManagerOptions & options() const
zypp::RepoManagerFlags::CacheBuildPolicy CacheBuildPolicy
zypp::DefaultIntegral< bool, false > _poolIdsReserved
Functor collecting ServiceInfos into a ServiceSet.
static expected success(ConsParams &&...params)
Definition expected.h:178
static expected error(ConsParams &&...params)
Definition expected.h:189
SignalProxy< void(zypp::repo::RepoType)> sigProbedTypeChanged()
Definition refresh.cc:157
static expected< repo::RefreshContextRef > create(ContextRef zyppContext, zypp::RepoInfo info, RepoManagerRef repoManager)
Definition refresh.cc:31
nullptr CURL handle void p CURLversion nullptr struct curl_slist list CURLM CURLM_INTERNAL_ERROR CURLM CURL CURLM_INTERNAL_ERROR CURLM curl_socket_t int int CURLM_INTERNAL_ERROR CURLINFO info
Definition curl_dl.cc:117
nullptr CURL handle void * p
Definition curl_dl.cc:99
boost::logic::tribool TriBool
3-state boolean logic (true, false and indeterminate).
Definition String.h:31
bool regex_match(const char *s, smatch &matches, const regex &regex) ZYPP_API
Regular expression matching.
Definition Regex.cc:80
unsigned short a
unsigned short b
Definition ansi.h:855
String related utilities and Regular expression matching.
RefreshCheckStatus
Possibly return state of RepoManager::checkIfToRefreshMetadata function.
Namespace intended to collect all environment variables we use.
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition PathInfo.cc:431
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:719
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
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:338
int touch(const Pathname &path)
Change file's modification and access times.
Definition PathInfo.cc:1256
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
std::string numstring(char n, int w=0)
Definition String.h:290
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:39
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition String.h:500
Url details namespace.
Definition UrlBase.cc:58
std::string join(const ParamVec &pvec, const std::string &psep)
Join parameter vector to a string.
Definition UrlUtils.cc:252
std::string asString(const Patch::Category &obj)
relates: Patch::Category string representation.
Definition Patch.cc:122
std::ostream & operator<<(std::ostream &str, const Capabilities &obj)
relates: Capabilities Stream output
MaybeAwaitable< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::RefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
MaybeAwaitable< expected< void > > addRepositories(RepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
MaybeAwaitable< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, Provide::LazyMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath)
MaybeAwaitable< expected< RepoInfo > > addRepository(RepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress, const zypp::TriBool &forcedProbe)
MaybeAwaitable< expected< repo::RefreshContextRef > > refreshMetadata(repo::RefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
MaybeAwaitable< expected< repo::RefreshContextRef > > buildCache(repo::RefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
MaybeAwaitable< expected< void > > refreshGeoIPData(ContextRef ctx, RepoInfo::url_set urls)
MaybeAwaitable< expected< void > > refreshService(RepoManagerRef repoMgr, ServiceInfo info, zypp::RepoManagerFlags::RefreshServiceOptions options)
MaybeAwaitable< expected< zypp::repo::ServiceType > > probeServiceType(ContextRef ctx, const zypp::Url &url)
auto incProgress(ProgressObserverRef progressObserver, double progrIncrease=1.0, std::optional< std::string > newStr={})
zypp::RepoManagerFlags::RawMetadataRefreshPolicy RawMetadataRefreshPolicy
Definition refresh.h:33
bool isTmpRepo(const RepoInfo &info_r)
Whether repo is not under RM control and provides its own methadata paths.
Definition repomanager.h:49
expected< void > assert_urls(const RepoInfo &info)
std::list< RepoInfo > repositories_in_dir(ZContextRef zyppContext, const zypp::Pathname &dir)
List of RepoInfo's from a 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 expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition expected.h:470
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.
expected< void > assert_alias(const RepoInfo &info)
Definition repomanager.h:52
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition expected.h:554
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:520
auto transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition transform.h:64
expected< zypp::Pathname > solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
expected< std::list< RepoInfo > > repositories_in_file(const zypp::Pathname &file)
Reads RepoInfo's from a repo file.
Iterator findAlias(const std::string &alias_r, Iterator begin_r, Iterator end_r)
Find alias_r in repo/service container.
Definition repomanager.h:93
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
expected< zypp::Pathname > packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
auto joinPipeline(ContextRef ctx, T &&val)
Definition context.h:52
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,...
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition expected.h:616
bool autoPruneInDir(const zypp::Pathname &path_r)
bsc#1204956: Tweak to prevent auto pruning package caches.
auto mtry(F &&f, Args &&...args)
Definition mtry.h:50
Repo manager settings.
Repository type enumeration.
Definition RepoType.h:29
static const RepoType YAST2
Definition RepoType.h:31
Type toEnum() const
Definition RepoType.h:49
static const RepoType RPMMD
Definition RepoType.h:30
static const RepoType NONE
Definition RepoType.h:33
static const RepoType RPMPLAINDIR
Definition RepoType.h:32
Convenient building of std::string with boost::format.
Definition String.h:254
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213
Url::asString() view options.
Definition UrlBase.h:41
Simple callback to collect the results.
std::string targetDistro
bool collect(const RepoInfo &repo)
#define ZYPP_PRIVATE_CONSTR_ARG
Definition zyppglobal.h:153