#include "zypp/sat/Pool.h"
#include "zypp/sat/Transaction.h"
-#include "zypp/PluginExecutor.h"
+#include "zypp/PluginScript.h"
using namespace std;
} // namespace
///////////////////////////////////////////////////////////////////
+ /** Helper for commit plugin execution.
+ * \ingroup g_RAII
+ */
+ class CommitPlugins : private base::NonCopyable
+ {
+ public:
+ /** Default ctor: Empty plugin list */
+ CommitPlugins()
+ {}
+
+ /** Dtor: Send PLUGINEND message and close plugins. */
+ ~CommitPlugins()
+ {
+ if ( ! _scripts.empty() )
+ send( PluginFrame( "PLUGINEND" ) );
+ // ~PluginScript will disconnect all remaining plugins!
+ }
+
+ /** Whether no plugins are waiting */
+ bool empty() const
+ { return _scripts.empty(); }
+
+
+ /** Send \ref PluginFrame to all open plugins.
+ * Failed plugins are removed from the execution list.
+ */
+ void send( const PluginFrame & frame_r )
+ {
+ DBG << "+++++++++++++++ send " << frame_r << endl;
+ for ( auto it = _scripts.begin(); it != _scripts.end(); )
+ {
+ doSend( *it, frame_r );
+ if ( it->isOpen() )
+ ++it;
+ else
+ it = _scripts.erase( it );
+ }
+ DBG << "--------------- send " << frame_r << endl;
+ }
+
+ /** Find and launch plugins sending PLUGINSTART message.
+ *
+ * If \a path_r is a directory all executable files whithin are
+ * expected to be plugins. Otherwise \a path_r must point to an
+ * executable plugin.
+ */
+ void load( const Pathname & path_r )
+ {
+ PathInfo pi( path_r );
+ DBG << "+++++++++++++++ load " << pi << endl;
+ if ( pi.isDir() )
+ {
+ std::list<Pathname> entries;
+ if ( filesystem::readdir( entries, pi.path(), false ) != 0 )
+ {
+ WAR << "Plugin dir is not readable: " << pi << endl;
+ return;
+ }
+ for_( it, entries.begin(), entries.end() )
+ {
+ PathInfo pii( *it );
+ if ( pii.isFile() && pii.userMayRX() )
+ doLoad( pii );
+ }
+ }
+ else if ( pi.isFile() )
+ {
+ if ( pi.userMayRX() )
+ doLoad( pi );
+ else
+ WAR << "Plugin file is not executable: " << pi << endl;
+ }
+ else
+ {
+ WAR << "Plugin path is neither dir nor file: " << pi << endl;
+ }
+ DBG << "--------------- load " << pi << endl;
+ }
+
+ private:
+ /** Send \ref PluginFrame and expect valid answer (ACK|_ENOMETHOD).
+ * Upon invalid answer or error, close the plugin. and remove it from the
+ * execution list.
+ * \returns the received \ref PluginFrame (empty Frame upon Exception)
+ */
+ PluginFrame doSend( PluginScript & script_r, const PluginFrame & frame_r )
+ {
+ PluginFrame ret;
+
+ try {
+ script_r.send( frame_r );
+ ret = script_r.receive();
+ }
+ catch( const zypp::Exception & e )
+ { ZYPP_CAUGHT(e); }
+
+ if ( ! ( ret.isAckCommand() || ret.isEnomethodCommand() ) )
+ {
+ WAR << "Bad plugin response from " << script_r << endl;
+ WAR << dump(ret) << endl;
+ script_r.close();
+ }
+
+ return ret;
+ }
+
+ /** Launch a plugin sending PLUGINSTART message. */
+ void doLoad( const PathInfo & pi_r )
+ {
+ MIL << "Load plugin: " << pi_r << endl;
+ try {
+ PluginScript plugin( pi_r.path() );
+ plugin.open();
+
+ PluginFrame frame( "PLUGINBEGIN" );
+ if ( ZConfig::instance().hasUserData() )
+ frame.setHeader( "userdata", ZConfig::instance().userData() );
+
+ doSend( plugin, frame ); // closes on error
+ if ( plugin.isOpen() )
+ _scripts.push_back( plugin );
+ }
+ catch( const zypp::Exception & e )
+ {
+ WAR << "Failed to load plugin " << pi_r << endl;
+ }
+ }
+
+ private:
+ std::list<PluginScript> _scripts;
+ };
+
+ void testCommitPlugins( const Pathname & path_r ) // for testing only
+ {
+ USR << "+++++" << endl;
+ {
+ CommitPlugins pl;
+ pl.load( path_r );
+ USR << "=====" << endl;
+ }
+ USR << "-----" << endl;
+ }
+
///////////////////////////////////////////////////////////////////
namespace
{
cmd << " > '" << tmpsolv.path() << "'";
- MIL << "Executing: " << cmd << endl;
+ MIL << "Executing: " << cmd.str() << endl;
ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
cmd << endl;
// We keep it.
guard.resetDispose();
- // system-hook: Finally send notification to plugins
- if ( root() == "/" )
+ // Finally send notification to plugins
+ // NOTE: quick hack looking for spacewalk plugin only
{
- PluginExecutor plugins;
- plugins.load( ZConfig::instance().pluginsPath()/"system" );
- if ( plugins )
- plugins.send( PluginFrame( "PACKAGESETCHANGED" ) );
+ Pathname script( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"system/spacewalk" ) );
+ if ( PathInfo( script ).isX() )
+ try {
+ PluginScript spacewalk( script );
+ spacewalk.open();
+
+ PluginFrame notify( "PACKAGESETCHANGED" );
+ spacewalk.send( notify );
+
+ PluginFrame ret( spacewalk.receive() );
+ MIL << ret << endl;
+ if ( ret.command() == "ERROR" )
+ ret.writeTo( WAR ) << endl;
+ }
+ catch ( const Exception & excpt )
+ {
+ WAR << excpt.asUserHistory() << endl;
+ }
}
}
return build_rpm_solv;
///////////////////////////////////////////////////////////////////
// Prepare execution of commit plugins:
///////////////////////////////////////////////////////////////////
- PluginExecutor commitPlugins;
+ CommitPlugins commitPlugins;
if ( root() == "/" && ! policy_r.dryRun() )
{
- commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
+ Pathname plugindir( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"commit" ) );
+ commitPlugins.load( plugindir );
}
- if ( commitPlugins )
+ if ( ! commitPlugins.empty() )
commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
///////////////////////////////////////////////////////////////////
// Write out a testcase if we're in dist upgrade mode.
///////////////////////////////////////////////////////////////////
- if ( pool_r.resolver().upgradeMode() || pool_r.resolver().upgradingRepos() )
+ if ( getZYpp()->resolver()->upgradeMode() )
{
if ( ! policy_r.dryRun() )
{
///////////////////////////////////////////////////////////////////
// Send result to commit plugins:
///////////////////////////////////////////////////////////////////
- if ( commitPlugins )
+ if ( ! commitPlugins.empty() )
commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
///////////////////////////////////////////////////////////////////
// COMMIT internal
//
///////////////////////////////////////////////////////////////////
- namespace
- {
- struct NotifyAttemptToModify
- {
- NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
-
- void operator()()
- { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
-
- TrueBool _guard;
- ZYppCommitResult & _result;
- };
- } // namespace
-
void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
CommitPackageCache & packageCache_r,
ZYppCommitResult & result_r )
ZYppCommitResult::TransactionStepList & steps( result_r.rTransactionStepList() );
MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
- // Send notification once upon 1st call to rpm
- NotifyAttemptToModify attemptToModify( result_r );
-
bool abort = false;
-
RpmPostTransCollector postTransCollector( _root );
std::vector<sat::Solvable> successfullyInstalledPackages;
TargetImpl::PoolItemList remaining;
if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
- attemptToModify();
try
{
progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
flags |= rpm::RPMINST_NODEPS;
if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
-
- attemptToModify();
try
{
rpm().removePackage( p, flags );