\endverbatim
<HR><!-- ====================================================================== -->
+\section plugins-impl Developers: Implementation
+
+Plugins are implemented in the following classes:
+
+- \ref zypp::PluginScript (Plugin as an external program)
+- \ref zypp::PluginScriptException
+- \ref zypp::PluginFrame (Message for the stateful protocol)
+- \ref zypp::PluginFrameException
+- \ref zypp::repo::PluginServices (Finds Service plugins)
+
+The plugins default location is obtained from \ref zypp::ZConfig::pluginsPath()
+
+<HR><!-- ====================================================================== -->
\section plugin-toc Supported plugins
\subpage plugin-commit Escort installation of packages
\ref plugin-url-resolver
+\ref plugin-appdata
<HR><!-- ====================================================================== -->
\section plugin-services Service plugins
plugin:foo?param1=val1¶m2=val2
\endverbatim
-ZYpp tries to executa a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and calla it with the following protocol:
+ZYpp tries to executa a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and call it with the following protocol:
\verbatim
RESOLVEURL
In this example, the service plugin could have inmediately resolved the urls and returned http://realurl, but the url resolver allows also to add HTTP headers to the request.
<HR><!-- ====================================================================== -->
-\section plugins-impl Developers: Implementation
+\section plugin-appdata Appdata refresh plugins (repo change)
-Plugins are implemented in the following classes:
+Stateless plugins found in /usr/lib/zypp/plugins/appdata are called whenever any of the system repos has changed (added/removed/renamed/modified) or has been refreshed. Detailed information \b what exactly has changed is not available. (scripts are executed IFF euid is '0' and --root is not used). For every enabled system repo we pass alias type and metadata path on the commandline like this:
-- \ref zypp::PluginScript (Plugin as an external program)
-- \ref zypp::PluginScriptException
-- \ref zypp::PluginFrame (Message for the stateful protocol)
-- \ref zypp::PluginFrameException
-- \ref zypp::repo::PluginServices (Finds Service plugins)
+\verbatim
+ -R REPO_ALIAS -t REPO_TYPE -p REPO_METADATA_PATH -R NEXT_REPO....
+\endverbatim
-The plugins default location is obtained from \ref zypp::ZConfig::pluginsPath()
+\note REPO_TYPE can be e.g. "rpm-md", "yast2", "plaindir" or "NONE" indicating the repo was not yet probed.
+
+\note REPO_METADATA_PATH can be empty or a not existing directory, indicating valid metadata for the repo are not yet available.
+Scripts are executed 'fire and forget' whenever a RepoManager instance that performed changes goes out of scope. So it's up to the script to protect against concurrency.
*/
\ No newline at end of file
# Features we provide (update doc/autoinclude/FeatureTest.doc):
Provides: libzypp(plugin) = 0
+Provides: libzypp(plugin:appdata) = 0
Provides: libzypp(plugin:commit) = 1
Provides: libzypp(plugin:services) = 0
Provides: libzypp(plugin:system) = 0
BuildRequires: libsolv-devel >= 0.6.5
%if 0%{?suse_version} >= 1100
+BuildRequires: libsolv-tools
%requires_eq libsolv-tools
%else
Requires: libsolv-tools
export CXXFLAGS="$RPM_OPT_FLAGS"
unset TRANSLATION_SET
unset EXTRA_CMAKE_OPTIONS
-+# Same codebase, but SLES may use it's own translation set.
-+# suse_version
-+# 1110 SLES11
-+# 1315 SLES12
+# Same codebase, but SLES may use it's own translation set.
+# suse_version
+# 1110 SLES11
+# 1315 SLES12
%if 0%{?suse_version} == 1110 || 0%{?suse_version} == 1315
if [ -f ../po/sle-zypp-po.tar.bz ]; then
export TRANSLATION_SET=sle-zypp
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/zypp/multiversion.d
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/appdata
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/commit
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/services
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/system
#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"
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 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 */
// set the downloaded packages path for the repo
repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
// remember it
- _repos.insert( repoInfo );
+ _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() );
// 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 << "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
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);
}
// now delete metadata (#301037)
cleanMetadata( todelete, mSubprogrcv );
cleanPackages( todelete, pSubprogrcv );
- _repos.erase(todelete);
+ reposManip().erase(todelete);
MIL << todelete.alias() << " sucessfully deleted." << endl;
HistoryLog(_options.rootDir).removeRepository(todelete);
return;
}
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 );