#include "zypp/base/InputStream.h"
#include "zypp/base/LogTools.h"
#include "zypp/base/Gettext.h"
+#include "zypp/base/DefaultIntegral.h"
#include "zypp/base/Function.h"
#include "zypp/base/Regex.h"
#include "zypp/PathInfo.h"
#include "zypp/repo/ServiceRepos.h"
#include "zypp/repo/yum/Downloader.h"
#include "zypp/repo/susetags/Downloader.h"
-#include "zypp/parser/plaindir/RepoParser.h"
#include "zypp/repo/PluginServices.h"
#include "zypp/Target.h" // for Target::targetDistribution() for repo index services
/**
* \short Simple callback to collect the results
*
- * Classes like RepoFileParser call the callback
+ * Classes like RepoFileReader call the callback
* once per each repo in a file.
*
* Passing this functor as callback, you can collect
&& repo.targetDistribution() != targetDistro)
{
MIL
- << "Skipping repository meant for '" << targetDistro
+ << "Skipping repository meant for '" << repo.targetDistribution()
<< "' distribution (current distro is '"
- << repo.targetDistribution() << "')." << endl;
+ << targetDistro << "')." << endl;
return true;
}
MIL << "repo file: " << file << endl;
RepoCollector collector;
parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
- return collector.repos;
+ return std::move(collector.repos);
}
////////////////////////////////////////////////////////////////////////////
{
MIL << "directory " << dir << endl;
std::list<RepoInfo> repos;
- std::list<Pathname> entries;
- if ( filesystem::readdir( entries, dir, false ) != 0 )
+ bool nonroot( geteuid() != 0 );
+ if ( nonroot && ! PathInfo(dir).userMayRX() )
{
- // TranslatorExplanation '%s' is a pathname
- ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+ JobReport::warning( formatNAC(_("Cannot read repo directory '%1%': Permission denied")) % dir );
}
-
- str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
- for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+ else
{
- if (str::regex_match(it->extension(), allowedRepoExt))
+ std::list<Pathname> entries;
+ if ( filesystem::readdir( entries, dir, false ) != 0 )
{
- std::list<RepoInfo> tmp = repositories_in_file( *it );
- repos.insert( repos.end(), tmp.begin(), tmp.end() );
+ // TranslatorExplanation '%s' is a pathname
+ ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+ }
- //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
- //MIL << "ok" << endl;
+ str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
+ for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+ {
+ if ( str::regex_match(it->extension(), allowedRepoExt) )
+ {
+ if ( nonroot && ! PathInfo(*it).userMayR() )
+ {
+ JobReport::warning( formatNAC(_("Cannot read repo file '%1%': Permission denied")) % *it );
+ }
+ else
+ {
+ const std::list<RepoInfo> & tmp( repositories_in_file( *it ) );
+ repos.insert( repos.end(), tmp.begin(), tmp.end() );
+ }
+ }
}
}
return repos;
inline void assert_alias( const RepoInfo & info )
{
if ( info.alias().empty() )
- ZYPP_THROW( RepoNoAliasException() );
+ ZYPP_THROW( RepoNoAliasException( info ) );
// bnc #473834. Maybe we can match the alias against a regex to define
// and check for valid aliases
if ( info.alias()[0] == '.')
inline void assert_alias( const ServiceInfo & info )
{
if ( info.alias().empty() )
- ZYPP_THROW( ServiceNoAliasException() );
+ ZYPP_THROW( ServiceNoAliasException( info ) );
// bnc #473834. Maybe we can match the alias against a regex to define
// and check for valid aliases
if ( info.alias()[0] == '.')
return ret;
}
+ std:: ostream & operator<<( std::ostream & str, const RepoManagerOptions & obj )
+ {
+#define OUTS(X) str << " " #X "\t" << obj.X << endl
+ str << "RepoManagerOptions (" << obj.rootDir << ") {" << endl;
+ OUTS( repoRawCachePath );
+ OUTS( repoSolvCachePath );
+ OUTS( repoPackagesCachePath );
+ OUTS( knownReposPath );
+ OUTS( knownServicesPath );
+ OUTS( pluginsPath );
+ str << "}" << endl;
+#undef OUTS
+ return str;
+ }
+
///////////////////////////////////////////////////////////////////
/// \class RepoManager::Impl
/// \brief RepoManager implementation.
init_knownRepositories();
}
+ ~Impl()
+ {
+ // trigger appdata refresh if some repos change
+ if ( _reposDirty && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
+ {
+ try {
+ std::list<Pathname> entries;
+ filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
+ if ( ! entries.empty() )
+ {
+ ExternalProgram::Arguments cmd;
+ cmd.push_back( "<" ); // discard stdin
+ cmd.push_back( ">" ); // discard stdout
+ cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
+ for ( const auto & rinfo : repos() )
+ {
+ if ( ! rinfo.enabled() )
+ continue;
+ cmd.push_back( "-R" );
+ cmd.push_back( rinfo.alias() );
+ cmd.push_back( "-t" );
+ cmd.push_back( rinfo.type().asString() );
+ cmd.push_back( "-p" );
+ cmd.push_back( rinfo.metadataPath().asString() );
+ }
+
+ for_( it, entries.begin(), entries.end() )
+ {
+ PathInfo pi( *it );
+ //DBG << "/tmp/xx ->" << pi << endl;
+ if ( pi.isFile() && pi.userMayRX() )
+ {
+ // trigger plugin
+ cmd[2] = pi.asString(); // [2] - PROGRAM
+ ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+ }
+ }
+ }
+ }
+ catch (...) {} // no throw in dtor
+ }
+ }
+
public:
- bool repoEmpty() const { return _repos.empty(); }
- RepoSizeType repoSize() const { return _repos.size(); }
- RepoConstIterator repoBegin() const { return _repos.begin(); }
- RepoConstIterator repoEnd() const { return _repos.end(); }
+ bool repoEmpty() const { return repos().empty(); }
+ RepoSizeType repoSize() const { return repos().size(); }
+ RepoConstIterator repoBegin() const { return repos().begin(); }
+ RepoConstIterator repoEnd() const { return repos().end(); }
bool hasRepo( const std::string & alias ) const
- { return foundAliasIn( alias, _repos ); }
+ { return foundAliasIn( alias, repos() ); }
RepoInfo getRepo( const std::string & alias ) const
{
- RepoConstIterator it( findAlias( alias, _repos ) );
- return it == _repos.end() ? RepoInfo::noRepo : *it;
+ RepoConstIterator it( findAlias( alias, repos() ) );
+ return it == repos().end() ? RepoInfo::noRepo : *it;
}
public:
void removeService( const ServiceInfo & service )
{ removeService( service.alias() ); }
- void refreshServices();
+ void refreshServices( const RefreshServiceOptions & options_r );
- void refreshService( const std::string & alias );
- void refreshService( const ServiceInfo & service )
- { refreshService( service.alias() ); }
+ void refreshService( const std::string & alias, const RefreshServiceOptions & options_r );
+ void refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
+ { refreshService( service.alias(), options_r ); }
void modifyService( const std::string & oldAlias, const ServiceInfo & newService );
void getRepositoriesInService( const std::string & alias, OutputIterator out ) const
{
MatchServiceAlias filter( alias );
- std::copy( boost::make_filter_iterator( filter, _repos.begin(), _repos.end() ),
- boost::make_filter_iterator( filter, _repos.end(), _repos.end() ),
+ std::copy( boost::make_filter_iterator( filter, repos().begin(), repos().end() ),
+ boost::make_filter_iterator( filter, repos().end(), repos().end() ),
out);
}
void init_knownServices();
void init_knownRepositories();
+ const RepoSet & repos() const { return _reposX; }
+ RepoSet & reposManip() { if ( ! _reposDirty ) _reposDirty = true; return _reposX; }
+
private:
RepoManagerOptions _options;
- RepoSet _repos;
+ RepoSet _reposX;
ServiceSet _services;
+ DefaultIntegral<bool,false> _reposDirty;
+
private:
friend Impl * rwcowClone<Impl>( const Impl * rhs );
/** clone for RWCOW_pointer */
repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
}
+ ///////////////////////////////////////////////////////////////////
+ namespace {
+ /** Delete \a cachePath_r subdirs not matching known aliases in \a repoEscAliases_r (must be sorted!)
+ * \note bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
+ * we'd need some magic file to identify zypp cache directories. Without this
+ * we may easily remove user data (zypper --pkg-cache-dir . download ...)
+ */
+ inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
+ const Pathname & defaultCachePath_r,
+ const std::list<std::string> & repoEscAliases_r )
+ {
+ if ( cachePath_r != defaultCachePath_r )
+ return;
+
+ std::list<std::string> entries;
+ if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
+ {
+ entries.sort();
+ std::set<std::string> oldfiles;
+ set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
+ std::inserter( oldfiles, oldfiles.end() ) );
+ for ( const std::string & old : oldfiles )
+ {
+ if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
+ continue;
+ filesystem::recursive_rmdir( cachePath_r / old );
+ }
+ }
+ }
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
void RepoManager::Impl::init_knownRepositories()
{
MIL << "start construct known repos" << endl;
if ( PathInfo(_options.knownReposPath).isExist() )
{
- RepoInfoList repol = repositories_in_dir(_options.knownReposPath);
- std::list<string> repo_esc_aliases;
- std::list<string> entries;
- for ( RepoInfoList::iterator it = repol.begin();
- it != repol.end();
- ++it )
+ std::list<std::string> repoEscAliases;
+ std::list<RepoInfo> orphanedRepos;
+ for ( RepoInfo & repoInfo : repositories_in_dir(_options.knownReposPath) )
{
// set the metadata path for the repo
- Pathname metadata_path = rawcache_path_for_repoinfo(_options, (*it));
- (*it).setMetadataPath(metadata_path);
-
+ repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
// set the downloaded packages path for the repo
- Pathname packages_path = packagescache_path_for_repoinfo(_options, (*it));
- (*it).setPackagesPath(packages_path);
+ repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
+ // remember it
+ _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
+
+ // detect orphaned repos belonging to a deleted service
+ const std::string & serviceAlias( repoInfo.service() );
+ if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
+ {
+ WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
+ orphanedRepos.push_back( repoInfo );
+ continue; // don't remember it in repoEscAliases
+ }
- _repos.insert(*it);
- repo_esc_aliases.push_back(it->escaped_alias());
+ repoEscAliases.push_back(repoInfo.escaped_alias());
}
- // delete metadata folders without corresponding repo (e.g. old tmp directories)
- if ( filesystem::readdir( entries, _options.repoRawCachePath, false ) == 0 )
+ // Cleanup orphanded service repos:
+ if ( ! orphanedRepos.empty() )
{
- std::set<string> oldfiles;
- repo_esc_aliases.sort();
- entries.sort();
- set_difference(entries.begin(), entries.end(), repo_esc_aliases.begin(), repo_esc_aliases.end(), std::inserter(oldfiles, oldfiles.end()));
- for_(it, oldfiles.begin(), oldfiles.end())
- {
- filesystem::recursive_rmdir(_options.repoRawCachePath / *it);
- }
+ for ( auto & repoInfo : orphanedRepos )
+ {
+ MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
+ // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
+ // %1% = service name
+ // %2% = repository name
+ JobReport::warning( formatNAC(_("Unknown service '%1%': Removing orphaned service repository '%2%'" ))
+ % repoInfo.service()
+ % repoInfo.alias() );
+ try {
+ removeRepository( repoInfo );
+ }
+ catch ( const Exception & caugth )
+ {
+ JobReport::error( caugth.asUserHistory() );
+ }
+ }
}
- }
+ // delete metadata folders without corresponding repo (e.g. old tmp directories)
+ //
+ // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
+ // we'd need somemagic file to identify zypp cache directories. Without this
+ // we may easily remove user data (zypper --pkg-cache-dir . download ...)
+ repoEscAliases.sort();
+ RepoManagerOptions defaultCache( _options.rootDir );
+ cleanupNonRepoMetadtaFolders( _options.repoRawCachePath, defaultCache.repoRawCachePath, repoEscAliases );
+ cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath, defaultCache.repoSolvCachePath, repoEscAliases );
+ cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath, defaultCache.repoPackagesCachePath, repoEscAliases );
+ }
MIL << "end construct known repos" << endl;
}
{
Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
- RepoType repokind = info.type();
- RepoStatus status;
- switch ( repokind.toEnum() )
- {
- case RepoType::NONE_e:
- // unknown, probe the local metadata
- repokind = probe( productdatapath.asUrl() );
- break;
- default:
- break;
- }
+ RepoType repokind = info.type();
+ // If unknown, probe the local metadata
+ if ( repokind == RepoType::NONE )
+ repokind = probe( productdatapath.asUrl() );
+ RepoStatus status;
switch ( repokind.toEnum() )
{
case RepoType::RPMMD_e :
- {
- status = RepoStatus( productdatapath + "/repodata/repomd.xml");
- }
- break;
+ status = RepoStatus( productdatapath/"repodata/repomd.xml");
+ break;
case RepoType::YAST2_e :
- {
- status = RepoStatus( productdatapath + "/content") && (RepoStatus( mediarootpath + "/media.1/media"));
- }
- break;
+ status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
+ break;
case RepoType::RPMPLAINDIR_e :
- {
- if ( PathInfo(Pathname(productdatapath + "/cookie")).isExist() )
- status = RepoStatus( productdatapath + "/cookie");
- }
- break;
+ status = RepoStatus::fromCookieFile( productdatapath/"cookie" );
+ break;
case RepoType::NONE_e :
// Return default RepoStatus in case of RepoType::NONE
RepoManager::RefreshCheckStatus RepoManager::Impl::checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy )
{
assert_alias(info);
-
- RepoStatus oldstatus;
- RepoStatus newstatus;
-
try
{
MIL << "Going to try to check whether refresh is needed for " << url << endl;
// first check old (cached) metadata
Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
- filesystem::assert_dir(mediarootpath);
- oldstatus = metadataStatus(info);
+ filesystem::assert_dir( mediarootpath );
+ RepoStatus oldstatus = metadataStatus( info );
if ( oldstatus.empty() )
{
}
{
- std::string scheme( url.getScheme() );
- if ( scheme == "cd" || scheme == "dvd" )
- {
- MIL << "never refresh CD/DVD" << endl;
+ if ( url.schemeIsVolatile() )
+ {
+ MIL << "never refresh CD/DVD" << endl;
return REPO_UP_TO_DATE;
- }
+ }
+ if ( url.schemeIsLocal() )
+ {
+ policy = RefreshIfNeededIgnoreDelay;
+ }
}
// now we've got the old (cached) status, we can decide repo.refresh.delay
}
}
- // To test the new matadta create temp dir as sibling of mediarootpath
- filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
-
repo::RepoType repokind = info.type();
- // if the type is unknown, try probing.
- switch ( repokind.toEnum() )
- {
- case RepoType::NONE_e:
- // unknown, probe it \todo respect productdir
- repokind = probe( url, info.path() );
- break;
- default:
- break;
- }
+ // if unknown: probe it
+ if ( repokind == RepoType::NONE )
+ repokind = probe( url, info.path() );
- if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
- ( repokind.toEnum() == RepoType::YAST2_e ) )
+ // retrieve newstatus
+ RepoStatus newstatus;
+ switch ( repokind.toEnum() )
{
- MediaSetAccess media(url);
- shared_ptr<repo::Downloader> downloader_ptr;
-
- if ( repokind.toEnum() == RepoType::RPMMD_e )
- downloader_ptr.reset(new yum::Downloader(info, mediarootpath));
- else
- downloader_ptr.reset( new susetags::Downloader(info, mediarootpath));
+ case RepoType::RPMMD_e:
+ {
+ MediaSetAccess media( url );
+ newstatus = yum::Downloader( info, mediarootpath ).status( media );
+ }
+ break;
- RepoStatus newstatus = downloader_ptr->status(media);
- bool refresh = false;
- if ( oldstatus.checksum() == newstatus.checksum() )
- {
- MIL << "repo has not changed" << endl;
- if ( policy == RefreshForced )
- {
- MIL << "refresh set to forced" << endl;
- refresh = true;
- }
- }
- else
- {
- MIL << "repo has changed, going to refresh" << endl;
- refresh = true;
- }
+ case RepoType::YAST2_e:
+ {
+ MediaSetAccess media( url );
+ newstatus = susetags::Downloader( info, mediarootpath ).status( media );
+ }
+ break;
- if (!refresh)
- touchIndexFile(info);
+ case RepoType::RPMPLAINDIR_e:
+ newstatus = RepoStatus( MediaMounter(url).getPathName(info.path()) ); // dir status
+ break;
- return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+ default:
+ case RepoType::NONE_e:
+ ZYPP_THROW( RepoUnknownTypeException( info ) );
+ break;
}
- else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
- {
- MediaMounter media( url );
- RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
- bool refresh = false;
- if ( oldstatus.checksum() == newstatus.checksum() )
- {
- MIL << "repo has not changed" << endl;
- if ( policy == RefreshForced )
- {
- MIL << "refresh set to forced" << endl;
- refresh = true;
- }
- }
- else
- {
- MIL << "repo has changed, going to refresh" << endl;
- refresh = true;
- }
-
- if (!refresh)
- touchIndexFile(info);
- return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+ // check status
+ bool refresh = false;
+ if ( oldstatus == newstatus )
+ {
+ MIL << "repo has not changed" << endl;
+ if ( policy == RefreshForced )
+ {
+ MIL << "refresh set to forced" << endl;
+ refresh = true;
+ }
}
else
{
- ZYPP_THROW(RepoUnknownTypeException(info));
+ MIL << "repo has changed, going to refresh" << endl;
+ refresh = true;
}
+
+ if (!refresh)
+ touchIndexFile(info);
+
+ return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+
}
catch ( const Exception &e )
{
assert_urls(info);
// we will throw this later if no URL checks out fine
- RepoException rexception(_PL("Valid metadata not found at specified URL",
- "Valid metadata not found at specified URLs",
- info.baseUrlsSize() ) );
+ RepoException rexception( info, _PL("Valid metadata not found at specified URL",
+ "Valid metadata not found at specified URLs",
+ info.baseUrlsSize() ) );
// try urls one by one
for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
repo::RepoType repokind = info.type();
// if the type is unknown, try probing.
- switch ( repokind.toEnum() )
- {
- case RepoType::NONE_e:
- // unknown, probe it
- repokind = probe( *it, info.path() );
-
- if (repokind.toEnum() != RepoType::NONE_e)
- {
- // Adjust the probed type in RepoInfo
- info.setProbedType( repokind ); // lazy init!
- //save probed type only for repos in system
- for_( it, repoBegin(), repoEnd() )
- {
- if ( info.alias() == (*it).alias() )
- {
- RepoInfo modifiedrepo = info;
- modifiedrepo.setType( repokind );
- modifyRepository( info.alias(), modifiedrepo );
- break;
- }
- }
- }
- break;
- default:
- break;
- }
+ if ( repokind == RepoType::NONE )
+ {
+ // unknown, probe it
+ repokind = probe( *it, info.path() );
+
+ if (repokind.toEnum() != RepoType::NONE_e)
+ {
+ // Adjust the probed type in RepoInfo
+ info.setProbedType( repokind ); // lazy init!
+ //save probed type only for repos in system
+ for_( it, repoBegin(), repoEnd() )
+ {
+ if ( info.alias() == (*it).alias() )
+ {
+ RepoInfo modifiedrepo = info;
+ modifiedrepo.setType( repokind );
+ modifyRepository( info.alias(), modifiedrepo );
+ break;
+ }
+ }
+ }
+ }
Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
if( filesystem::assert_dir(mediarootpath) )
else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
{
MediaMounter media( url );
- RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
+ RepoStatus newstatus = RepoStatus( media.getPathName( info.path() ) ); // dir status
Pathname productpath( tmpdir.path() / info.path() );
filesystem::assert_dir( productpath );
- std::ofstream file( (productpath/"cookie").c_str() );
- if ( !file )
- {
- // TranslatorExplanation '%s' is a filename
- ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), (productpath/"cookie").c_str() )));
- }
- file << url;
- if ( ! info.path().empty() && info.path() != "/" )
- file << " (" << info.path() << ")";
- file << endl;
- file << newstatus.checksum() << endl;
-
- file.close();
+ newstatus.saveToCookieFile( productpath/"cookie" );
}
else
{
- ZYPP_THROW(RepoUnknownTypeException());
+ ZYPP_THROW(RepoUnknownTypeException( info ));
}
// ok we have the metadata, now exchange
// the contents
filesystem::exchange( tmpdir.path(), mediarootpath );
+ reposManip(); // remember to trigger appdata refresh
// we are done.
return;
MIL << info.alias() << " is already cached." << endl;
RepoStatus cache_status = cacheStatus(info);
- if ( cache_status.checksum() == raw_metadata_status.checksum() )
+ if ( cache_status == raw_metadata_status )
{
MIL << info.alias() << " cache is up to date with metadata." << endl;
if ( policy == BuildIfNeeded ) {
ExternalProgram::Arguments cmd;
cmd.push_back( "repo2solv.sh" );
-
// repo2solv expects -o as 1st arg!
cmd.push_back( "-o" );
cmd.push_back( solvfile.asString() );
+ cmd.push_back( "-X" ); // autogenerate pattern from pattern-package
if ( repokind == RepoType::RPMPLAINDIR )
{
}
break;
default:
- ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") ));
+ ZYPP_THROW(RepoUnknownTypeException( info, _("Unhandled repository type") ));
break;
}
// update timestamp and checksum
if ( subdir->basename() == r->escaped_alias() )
{ found = true; break; }
- if ( ! found )
+ if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
filesystem::recursive_rmdir( *subdir );
progress.set( progress.val() + sdircurrent * 100 / sdircount );
MIL << "Try adding repo " << info << endl;
RepoInfo tosave = info;
- if ( _repos.find(tosave) != _repos.end() )
+ if ( repos().find(tosave) != repos().end() )
ZYPP_THROW(RepoAlreadyExistsException(info));
// check the first url for now
if ( tosave.baseUrlsSize() > 0 )
{
if ( probedtype == RepoType::NONE )
- ZYPP_THROW(RepoUnknownTypeException());
+ ZYPP_THROW(RepoUnknownTypeException(info));
else
tosave.setType(probedtype);
}
oinfo.setMetadataPath( metadataPath( tosave ) );
oinfo.setPackagesPath( packagesPath( tosave ) );
}
- _repos.insert(tosave);
+ reposManip().insert(tosave);
progress.set(90);
MIL << "Saving " << (*it).alias() << endl;
it->setFilepath(repofile.asString());
it->dumpAsIniOn(file);
- _repos.insert(*it);
+ reposManip().insert(*it);
HistoryLog(_options.rootDir).addRepository(*it);
}
RepoInfo todelete = *it;
if (todelete.filepath().empty())
{
- ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
+ ZYPP_THROW(RepoException( todelete, _("Can't figure out where the repo is stored.") ));
}
else
{
if ( filesystem::unlink(todelete.filepath()) != 0 )
{
// TranslatorExplanation '%s' is a filename
- ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
+ ZYPP_THROW(RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
}
- MIL << todelete.alias() << " sucessfully deleted." << endl;
+ MIL << todelete.alias() << " successfully deleted." << endl;
}
else
{
// now delete metadata (#301037)
cleanMetadata( todelete, mSubprogrcv );
cleanPackages( todelete, pSubprogrcv );
- _repos.erase(todelete);
- MIL << todelete.alias() << " sucessfully deleted." << endl;
+ reposManip().erase(todelete);
+ MIL << todelete.alias() << " successfully deleted." << endl;
HistoryLog(_options.rootDir).removeRepository(todelete);
return;
} // else filepath is empty
if (toedit.filepath().empty())
{
- ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
+ ZYPP_THROW(RepoException( toedit, _("Can't figure out where the repo is stored.") ));
}
else
{
}
newinfo.setFilepath(toedit.filepath());
- _repos.erase(toedit);
- _repos.insert(newinfo);
+ reposManip().erase(toedit);
+ reposManip().insert(newinfo);
HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
MIL << "repo " << alias << " modified" << endl;
}
RepoInfo RepoManager::Impl::getRepositoryInfo( const std::string & alias, const ProgressData::ReceiverFnc & progressrcv )
{
- RepoConstIterator it( findAlias( alias, _repos ) );
- if ( it != _repos.end() )
+ RepoConstIterator it( findAlias( alias, repos() ) );
+ if ( it != repos().end() )
return *it;
RepoInfo info;
info.setAlias( alias );
void RepoManager::Impl::removeService( const std::string & alias )
{
- MIL << "Going to delete repo " << alias << endl;
+ MIL << "Going to delete service " << alias << endl;
const ServiceInfo & service = getService( alias );
Pathname location = service.filepath();
if( location.empty() )
{
- ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
+ ZYPP_THROW(ServiceException( service, _("Can't figure out where the service is stored.") ));
}
ServiceSet tmpSet;
if ( filesystem::unlink(location) != 0 )
{
// TranslatorExplanation '%s' is a filename
- ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), location.c_str() )));
+ ZYPP_THROW(ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
}
- MIL << alias << " sucessfully deleted." << endl;
+ MIL << alias << " successfully deleted." << endl;
}
else
{
it->dumpAsIniOn(file);
}
- MIL << alias << " sucessfully deleted from file " << location << endl;
+ MIL << alias << " successfully deleted from file " << location << endl;
}
// now remove all repositories added by this service
////////////////////////////////////////////////////////////////////////////
- void RepoManager::Impl::refreshServices()
+ void RepoManager::Impl::refreshServices( const RefreshServiceOptions & options_r )
{
// copy the set of services since refreshService
// can eventually invalidate the iterator
continue;
try {
- refreshService(*it);
+ refreshService(*it, options_r);
}
catch ( const repo::ServicePluginInformalException & e )
{ ;/* ignore ServicePluginInformalException */ }
}
}
- void RepoManager::Impl::refreshService( const std::string & alias )
+ void RepoManager::Impl::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
{
ServiceInfo service( getService( alias ) );
assert_alias( service );
// Either when probing the type, or when adjusting the repositories
// enable/disable state.:
bool serviceModified = false;
- MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
+ MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << ", opts: " << options_r << endl;
//! \todo add callbacks for apps (start, end, repo removed, repo added, repo changed)
uglyHack.second = e;
}
+ ////////////////////////////////////////////////////////////////////////////
+ // On the fly remember the new repo states as defined the reopoindex.xml.
+ // Move into ServiceInfo later.
+ ServiceInfo::RepoStates newRepoStates;
+
// set service alias and base url for all collected repositories
for_( it, collector.repos.begin(), collector.repos.end() )
{
+ // First of all: Prepend service alias:
+ it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
+ // set refrence to the parent service
+ it->setService( service.alias() );
+
+ // remember the new parsed repo state
+ newRepoStates[it->alias()] = *it;
+
// if the repo url was not set by the repoindex parser, set service's url
Url url;
-
if ( it->baseUrlsEmpty() )
url = service.url();
else
it->setPath("");
}
- // Prepend service alias:
- it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
-
// save the url
it->setBaseUrl( url );
- // set refrence to the parent service
- it->setService( service.alias() );
}
////////////////////////////////////////////////////////////////////////////
RepoInfoList oldRepos;
getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
+ ////////////////////////////////////////////////////////////////////////////
// find old repositories to remove...
- for_( it, oldRepos.begin(), oldRepos.end() )
+ for_( oldRepo, oldRepos.begin(), oldRepos.end() )
{
- if ( ! foundAliasIn( it->alias(), collector.repos ) )
+ if ( ! foundAliasIn( oldRepo->alias(), collector.repos ) )
{
- if ( it->enabled() && ! service.repoToDisableFind( it->alias() ) )
- {
- DBG << "Service removes enabled repo " << it->alias() << endl;
- service.addRepoToEnable( it->alias() );
- serviceModified = true;
- }
- else
- {
- DBG << "Service removes disabled repo " << it->alias() << endl;
- }
- removeRepository( *it );
+ if ( oldRepo->enabled() )
+ {
+ // Currently enabled. If this was a user modification remember the state.
+ const auto & last = service.repoStates().find( oldRepo->alias() );
+ if ( last != service.repoStates().end() && ! last->second.enabled )
+ {
+ DBG << "Service removes user enabled repo " << oldRepo->alias() << endl;
+ service.addRepoToEnable( oldRepo->alias() );
+ serviceModified = true;
+ }
+ else
+ DBG << "Service removes enabled repo " << oldRepo->alias() << endl;
+ }
+ else
+ DBG << "Service removes disabled repo " << oldRepo->alias() << endl;
+
+ removeRepository( *oldRepo );
}
}
// create missing repositories and modify exising ones if needed...
for_( it, collector.repos.begin(), collector.repos.end() )
{
- // Service explicitly requests the repo being enabled?
- // Service explicitly requests the repo being disabled?
+ // User explicitly requested the repo being enabled?
+ // User explicitly requested the repo being disabled?
// And hopefully not both ;) If so, enable wins.
- bool beEnabled = service.repoToEnableFind( it->alias() );
- bool beDisabled = service.repoToDisableFind( it->alias() );
- // Make sure the service repo is created with the
- // appropriate enable
- if ( beEnabled ) it->setEnabled(true);
- if ( beDisabled ) it->setEnabled(false);
+ TriBool toBeEnabled( indeterminate ); // indeterminate - follow the service request
+ DBG << "Service request to " << (it->enabled()?"enable":"disable") << " service repo " << it->alias() << endl;
- if ( beEnabled )
+ if ( options_r.testFlag( RefreshService_restoreStatus ) )
{
- // Remove from enable request list.
- // NOTE: repoToDisable is handled differently.
- // It gets cleared on each refresh.
- service.delRepoToEnable( it->alias() );
- serviceModified = true;
+ DBG << "Opt RefreshService_restoreStatus " << it->alias() << endl;
+ // this overrides any pending request!
+ // Remove from enable request list.
+ // NOTE: repoToDisable is handled differently.
+ // It gets cleared on each refresh.
+ service.delRepoToEnable( it->alias() );
+ // toBeEnabled stays indeterminate!
+ }
+ else
+ {
+ if ( service.repoToEnableFind( it->alias() ) )
+ {
+ DBG << "User request to enable service repo " << it->alias() << endl;
+ toBeEnabled = true;
+ // Remove from enable request list.
+ // NOTE: repoToDisable is handled differently.
+ // It gets cleared on each refresh.
+ service.delRepoToEnable( it->alias() );
+ serviceModified = true;
+ }
+ else if ( service.repoToDisableFind( it->alias() ) )
+ {
+ DBG << "User request to disable service repo " << it->alias() << endl;
+ toBeEnabled = false;
+ }
}
RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
{
// Not found in oldRepos ==> a new repo to add
- // At that point check whether a repo with the same alias
- // exists outside this service. Maybe forcefully re-alias
- // the existing repo?
+ // Make sure the service repo is created with the appropriate enablement
+ if ( ! indeterminate(toBeEnabled) )
+ it->setEnabled( toBeEnabled );
+
DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
addRepository( *it );
-
- // save repo credentials
- // ma@: task for modifyRepository?
}
else
{
// ==> an exising repo to check
bool oldRepoModified = false;
+ if ( indeterminate(toBeEnabled) )
+ {
+ // No user request: check for an old user modificaton otherwise follow service request.
+ // NOTE: Assert toBeEnabled is boolean afterwards!
+ if ( oldRepo->enabled() == it->enabled() )
+ toBeEnabled = it->enabled(); // service requests no change to the system
+ else if (options_r.testFlag( RefreshService_restoreStatus ) )
+ {
+ toBeEnabled = it->enabled(); // RefreshService_restoreStatus forced
+ DBG << "Opt RefreshService_restoreStatus " << it->alias() << " forces " << (toBeEnabled?"enabled":"disabled") << endl;
+ }
+ else
+ {
+ const auto & last = service.repoStates().find( oldRepo->alias() );
+ if ( last == service.repoStates().end() || last->second.enabled != it->enabled() )
+ toBeEnabled = it->enabled(); // service request has changed since last refresh -> follow
+ else
+ {
+ toBeEnabled = oldRepo->enabled(); // service request unchaned since last refresh -> keep user modification
+ DBG << "User modified service repo " << it->alias() << " may stay " << (toBeEnabled?"enabled":"disabled") << endl;
+ }
+ }
+ }
+
// changed enable?
- if ( beEnabled )
- {
- if ( ! oldRepo->enabled() )
- {
- DBG << "Service repo " << it->alias() << " gets enabled" << endl;
- oldRepo->setEnabled( true );
- oldRepoModified = true;
- }
- else
- {
- DBG << "Service repo " << it->alias() << " stays enabled" << endl;
- }
- }
- else if ( beDisabled )
- {
- if ( oldRepo->enabled() )
- {
- DBG << "Service repo " << it->alias() << " gets disabled" << endl;
- oldRepo->setEnabled( false );
- oldRepoModified = true;
- }
- else
- {
- DBG << "Service repo " << it->alias() << " stays disabled" << endl;
- }
- }
- else
+ if ( toBeEnabled == oldRepo->enabled() )
+ {
+ DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
+ }
+ else if ( toBeEnabled )
+ {
+ DBG << "Service repo " << it->alias() << " gets enabled" << endl;
+ oldRepo->setEnabled( true );
+ oldRepoModified = true;
+ }
+ else
{
- DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
- }
+ DBG << "Service repo " << it->alias() << " gets disabled" << endl;
+ oldRepo->setEnabled( false );
+ oldRepoModified = true;
+ }
+
+ // all other attributes follow the service request:
+
+ // changed autorefresh
+ if ( oldRepo->autorefresh() != it->autorefresh() )
+ {
+ DBG << "Service repo " << it->alias() << " gets new AUTOREFRESH " << it->autorefresh() << endl;
+ oldRepo->setAutorefresh( it->autorefresh() );
+ oldRepoModified = true;
+ }
+
+ // changed priority?
+ if ( oldRepo->priority() != it->priority() )
+ {
+ DBG << "Service repo " << it->alias() << " gets new PRIORITY " << it->priority() << endl;
+ oldRepo->setPriority( it->priority() );
+ oldRepoModified = true;
+ }
// changed url?
// service repo can contain only one URL now, so no need to iterate.
serviceModified = true;
}
+ // Remember original service request for next refresh
+ if ( service.repoStates() != newRepoStates )
+ {
+ service.setRepoStates( std::move(newRepoStates) );
+ serviceModified = true;
+ }
+
////////////////////////////////////////////////////////////////////////////
- // save service if modified:
- if ( serviceModified )
+ // save service if modified: (unless a plugin service)
+ if ( serviceModified && service.type() != ServiceType::PLUGIN )
{
// write out modified service file.
modifyService( service.alias(), service );
if ( service.type() == ServiceType::PLUGIN )
{
- MIL << "Not modifying plugin service '" << oldAlias << "'" << endl;
- return;
+ ZYPP_THROW(ServicePluginImmutableException( service ));
}
const ServiceInfo & oldService = getService(oldAlias);
Pathname location = oldService.filepath();
if( location.empty() )
{
- ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
+ ZYPP_THROW(ServiceException( oldService, _("Can't figure out where the service is stored.") ));
}
// remember: there may multiple services being defined in one file:
_services.insert(service);
// changed properties affecting also repositories
- if( oldAlias != service.alias() // changed alias
- || oldService.enabled() != service.enabled() // changed enabled status
- )
+ if ( oldAlias != service.alias() // changed alias
+ || oldService.enabled() != service.enabled() ) // changed enabled status
{
std::vector<RepoInfo> toModify;
getRepositoriesInService(oldAlias, std::back_inserter(toModify));
for_( it, toModify.begin(), toModify.end() )
{
- if (oldService.enabled() && !service.enabled())
- it->setEnabled(false);
- else if (!oldService.enabled() && service.enabled())
- {
- //! \todo do nothing? the repos will be enabled on service refresh
- //! \todo how to know the service needs a (auto) refresh????
- }
- else
+ if ( oldService.enabled() != service.enabled() )
+ {
+ if ( service.enabled() )
+ {
+ // reset to last refreshs state
+ const auto & last = service.repoStates().find( it->alias() );
+ if ( last != service.repoStates().end() )
+ it->setEnabled( last->second.enabled );
+ }
+ else
+ it->setEnabled( false );
+ }
+
+ if ( oldAlias != service.alias() )
it->setService(service.alias());
+
modifyRepository(it->alias(), *it);
}
}
void RepoManager::removeService( const ServiceInfo & service )
{ return _pimpl->removeService( service ); }
- void RepoManager::refreshServices()
- { return _pimpl->refreshServices(); }
+ void RepoManager::refreshServices( const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshServices( options_r ); }
- void RepoManager::refreshService( const std::string & alias )
- { return _pimpl->refreshService( alias ); }
+ void RepoManager::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshService( alias, options_r ); }
- void RepoManager::refreshService( const ServiceInfo & service )
- { return _pimpl->refreshService( service ); }
+ void RepoManager::refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshService( service, options_r ); }
void RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & service )
{ return _pimpl->modifyService( oldAlias, service ); }