Imported Upstream version 15.21.5 97/94697/1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 1 Nov 2016 02:08:43 +0000 (11:08 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 1 Nov 2016 02:08:44 +0000 (11:08 +0900)
Change-Id: I949d1104e2006a1b9137e20315c800b6a50d78bb
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
16 files changed:
VERSION.cmake
doc/autoinclude/FeatureTest.doc
doc/autoinclude/Plugin-System.doc [new file with mode: 0644]
doc/autoinclude/Plugins.doc
libzypp.spec.cmake
package/libzypp.changes
po/zypp-po.tar.bz2
tests/zypp/PluginFrame_test.cc
zypp/CMakeLists.txt
zypp/Patch.cc
zypp/PluginExecutor.cc [new file with mode: 0644]
zypp/PluginExecutor.h [new file with mode: 0644]
zypp/PluginFrame.h
zypp/PluginScript.h
zypp/base/Iterable.h
zypp/target/TargetImpl.cc

index e37f57c..9199a22 100644 (file)
@@ -61,8 +61,8 @@
 SET(LIBZYPP_MAJOR "15")
 SET(LIBZYPP_COMPATMINOR "19")
 SET(LIBZYPP_MINOR "21")
-SET(LIBZYPP_PATCH "4")
+SET(LIBZYPP_PATCH "5")
 #
-# LAST RELEASED: 15.21.4 (19)
+# LAST RELEASED: 15.21.5 (19)
 # (The number in parenthesis is LIBZYPP_COMPATMINOR)
 #=======
index 3ebf429..8a2090b 100644 (file)
@@ -29,32 +29,41 @@ Packages requiring a feature may use the corresponding \c Requires: in their .sp
 
   <DT>plugin:commit</DT>
   <DD><DL>
+    <DT>\ref plugin-commit </DT>
     <DT>version 0</DT>
-    <DD>\see \ref plugin-commit </DD>
+    <DD>Basic plugin indicating start and end of commit.</DD>
+    <DT>version 1</DT>
+    <DD>Added COMMITBEGIN/COMMITEND.</DD>
   </DL></DD>
 
   <DT>plugin:services</DT>
   <DD><DL>
+    <DT>\ref plugin-services </DT>
     <DT>version 0</DT>
-    <DD>\see \ref plugin-services </DD>
+    <DD>Provide a client a list of repositories.</DD>
   </DL></DD>
 
   <DT>plugin:system</DT>
   <DD><DL>
+    <DT>\ref plugin-system </DT>
     <DT>version 0</DT>
     <DD>Plugin executed when system content change is detected (by now SUSE Manager/spacewalk only).</DD>
+    <DT>version 1</DT>
+    <DD>Plugin executed when system content change is detected (all installed plugins).</DD>
   </DL></DD>
 
   <DT>plugin:urlresolver</DT>
   <DD><DL>
+    <DT>\ref plugin-url-resolver </DT>
     <DT>version 0</DT>
-    <DD>\see \ref plugin-url-resolver </DD>
+    <DD>Convert urls of scheme "plugin" into a supported scheme. </DD>
   </DL></DD>
 
   <DT>repovarexpand</DT>
   <DD><DL>
+    <DT>\ref zypp-repovars </DT>
     <DT>version 0</DT>
-    <DD>Also support braced variables, shell like default and alternate values. \see \ref zypp-repovars</DD>
+    <DD>Also support braced variables, shell like default and alternate values.</DD>
   </DL></DD>
 </DL>
 
diff --git a/doc/autoinclude/Plugin-System.doc b/doc/autoinclude/Plugin-System.doc
new file mode 100644 (file)
index 0000000..5642c32
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+
+\page plugin-system System plugin
+
+\author Michael Andres <ma@suse.de>
+
+<HR><!-- ====================================================================== -->
+\section intro Introduction
+
+This is a statefull plugin executed at the end of \ref zypp::ZYpp::commit, if the system content has change, i.e. if packages have actually been installed or deleted.
+
+All plugins found in \c /usr/lib/zypp/plugins/system are launched. Unless otherwise specified, messages received need to be confirmed by sending an \c ACC message. Sending back an unexpected or \c ERROR message, the execution of the plugin will be canceled.
+
+If you have e.g. \c zypp-plugin-python installed a basic system plugin could look like this:
+
+\verbatim
+#!/usr/bin/env python
+#
+# zypp system plugin
+#
+import os
+import sys
+from zypp_plugin import Plugin
+
+class MyPlugin(Plugin):
+
+  def PACKAGESETCHANGED(self, headers, body):
+
+    // Installation has ended. The set of installed packages has changed.
+    // ....
+
+    self.ack()
+  def
+
+plugin = MyPlugin()
+plugin.main()
+\endverbatim
+
+\see \ref plugin-writing
+
+<HR><!-- ====================================================================== -->
+\section pluginbegin PLUGINBEGIN
+\verbatim
+PLUGINBEGIN
+userdata:TIDfoo42
+
+^@
+\endverbatim
+Sent as 1st message after the plugin was launched. Prepare your plugin and send an \c ACC message when you are done.
+
+\li \c userdata:stringval Optional header sent if the application has provided a user data string. \see \ref zypp-userdata
+
+
+<HR><!-- ====================================================================== -->
+\section packagesetchanged PACKAGESETCHANGED
+\verbatim
+PACKAGESETCHANGED
+
+^@
+\endverbatim
+Installation has ended. The set of installed packages has changed.
+
+\see \ref zypp::sat::Transaction::Step
+
+<HR><!-- ====================================================================== -->
+\section pluginend PLUGINEND
+\verbatim
+PLUGINEND
+
+^@
+\endverbatim
+This message is sent at the end before the plugin is closed. You should receive this message even if the action was aborted by some unexpected exception.
+
+
+*/
index 040b8b1..461bfbd 100644 (file)
@@ -100,6 +100,8 @@ The plugins default location is obtained from \ref zypp::ZConfig::pluginsPath()
 
 \subpage plugin-commit Escort installation of packages
 
+\subpage plugin-system Receive notification if system content has changed
+
 \ref plugin-services
 
 \ref plugin-url-resolver
@@ -155,7 +157,7 @@ ZYpp sees a repository whose url has the format:
 plugin:foo?param1=val1&param2=val2
 \endverbatim
 
-ZYpp tries to executa a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and call it with the following protocol:
+ZYpp tries to execute a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and call it with the following protocol:
 
 \verbatim
    RESOLVEURL
@@ -214,4 +216,4 @@ Stateless plugins found in /usr/lib/zypp/plugins/appdata are called whenever any
 \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
+*/
index 4103d57..56ac03a 100644 (file)
@@ -36,7 +36,7 @@ Provides:       libzypp(plugin) = 0
 Provides:       libzypp(plugin:appdata) = 0
 Provides:       libzypp(plugin:commit) = 1
 Provides:       libzypp(plugin:services) = 0
-Provides:       libzypp(plugin:system) = 0
+Provides:       libzypp(plugin:system) = 1
 Provides:       libzypp(plugin:urlresolver) = 0
 Provides:       libzypp(repovarexpand) = 0
 
@@ -75,7 +75,7 @@ BuildRequires:  pkgconfig
 BuildRequires:  pkg-config
 %endif
 
-BuildRequires:  libsolv-devel >= 0.6.7
+BuildRequires:  libsolv-devel >= 0.6.19
 %if 0%{?suse_version} >= 1100
 BuildRequires:  libsolv-tools
 %requires_eq    libsolv-tools
index 3e61319..a4dcd7a 100644 (file)
@@ -1,4 +1,21 @@
 -------------------------------------------------------------------
+Fri Mar 18 13:37:22 CET 2016 - ma@suse.de
+
+- Use PluginExecutor for commit- and system-hooks (bnc#971637)
+- BuildRequires:  libsolv-devel >= 0.6.19 (bnc#971018)
+- version 15.21.5 (19)
+
+-------------------------------------------------------------------
+Sun Mar 13 01:13:08 CET 2016 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Mar 10 01:13:24 CET 2016 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
 Tue Mar  1 12:34:53 CET 2016 - ma@suse.de
 
 - media: Send stats header to download.opensuse.org only (bsc#955801)
index 7c6af33..c4028d7 100644 (file)
Binary files a/po/zypp-po.tar.bz2 and b/po/zypp-po.tar.bz2 differ
index cac4f3b..56ecd9c 100644 (file)
@@ -5,7 +5,7 @@
 #include <sstream>
 
 #include "TestSetup.h"
-#include "zypp/PluginScript.h"
+#include "zypp/PluginExecutor.h"
 
 BOOST_AUTO_TEST_CASE(InitialSettings)
 {
@@ -15,8 +15,8 @@ BOOST_AUTO_TEST_CASE(InitialSettings)
 BOOST_AUTO_TEST_CASE(PluginFrameDefaultCtor)
 {
   PluginFrame f;
+  BOOST_CHECK_EQUAL( bool(f), !f.empty() );
   BOOST_CHECK_EQUAL( f.empty(), true );
-  BOOST_CHECK_EQUAL( bool(f), f.empty() );
   BOOST_CHECK_EQUAL( f.command().empty(), true );
   BOOST_CHECK_EQUAL( f.body().empty(), true );
   BOOST_CHECK_EQUAL( f.headerEmpty(), true );
@@ -27,8 +27,8 @@ BOOST_AUTO_TEST_CASE(PluginFrameDefaultCtor)
 BOOST_AUTO_TEST_CASE(PluginFrameCtorAssign)
 {
   PluginFrame f( "command" );
+  BOOST_CHECK_EQUAL( bool(f), !f.empty() );
   BOOST_CHECK_EQUAL( f.empty(), false );
-  BOOST_CHECK_EQUAL( bool(f), f.empty() );
   BOOST_CHECK_EQUAL( f.command(), "command" );
   BOOST_CHECK_EQUAL( f.body().empty(), true );
   BOOST_CHECK_EQUAL( f.headerEmpty(), true );
@@ -36,8 +36,8 @@ BOOST_AUTO_TEST_CASE(PluginFrameCtorAssign)
   BOOST_CHECK_EQUAL( (f != f), false );
 
   PluginFrame g( "command", "body" );
+  BOOST_CHECK_EQUAL( bool(g), !g.empty() );
   BOOST_CHECK_EQUAL( g.empty(), false );
-  BOOST_CHECK_EQUAL( bool(g), g.empty() );
   BOOST_CHECK_EQUAL( g.command(), "command" );
   BOOST_CHECK_EQUAL( g.body(), "body");
   BOOST_CHECK_EQUAL( g.headerEmpty(), true );
@@ -168,3 +168,25 @@ BOOST_AUTO_TEST_CASE(PluginScriptReceive)
   ::kill( scr.getPid(), SIGKILL);
   BOOST_CHECK_THROW(  scr.receive(), PluginScriptDiedUnexpectedly );
 }
+
+BOOST_AUTO_TEST_CASE(PluginExecutorTest)
+{
+  PluginExecutor exec;
+  BOOST_CHECK_EQUAL( (bool)exec, !exec.empty() );
+  BOOST_CHECK_EQUAL( exec.empty(), true );
+  BOOST_CHECK_EQUAL( exec.size(), 0 );
+
+  exec.load( "/bin/cat" );
+  BOOST_CHECK_EQUAL( (bool)exec, !exec.empty() );
+  BOOST_CHECK_EQUAL( exec.empty(), false );
+  BOOST_CHECK_EQUAL( exec.size(), 1 );
+
+  exec.load( "/bin/cat" );
+  BOOST_CHECK_EQUAL( exec.size(), 2 );
+
+  exec.send( PluginFrame( "ACK" ) );
+  BOOST_CHECK_EQUAL( exec.size(), 2 );
+
+  exec.send( PluginFrame( "ERROR" ) );
+  BOOST_CHECK_EQUAL( exec.size(), 0 ); // deleted failing scripts
+}
index d5c0a5c..7d481c0 100644 (file)
@@ -27,6 +27,7 @@ SET( zypp_SRCS
   PluginFrameException.cc
   PluginScript.cc
   PluginScriptException.cc
+  PluginExecutor.cc
   Fetcher.cc
   FileChecker.cc
   Glob.cc
@@ -116,6 +117,7 @@ SET( zypp_HEADERS
   PluginFrameException.h
   PluginScript.h
   PluginScriptException.h
+  PluginExecutor.h
   Fetcher.h
   FileChecker.h
   Glob.h
index a2662f9..b15f14d 100644 (file)
@@ -44,7 +44,7 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
 
   std::string Patch::category() const
-  { return lookupStrAttribute( sat::SolvAttr::patchcategory ); }
+  { return str::toLower(lookupStrAttribute( sat::SolvAttr::patchcategory )); }
 
   Patch::Category Patch::categoryEnum() const
   { return categoryEnum( category() ); }
@@ -132,7 +132,7 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
 
   std::string Patch::severity() const
-  { return lookupStrAttribute( sat::SolvAttr::severity ); }
+  { return str::toLower(lookupStrAttribute( sat::SolvAttr::severity )); }
 
   Patch::SeverityFlag Patch::severityFlag() const
   { return severityFlag( severity() ); }
diff --git a/zypp/PluginExecutor.cc b/zypp/PluginExecutor.cc
new file mode 100644 (file)
index 0000000..2951ccd
--- /dev/null
@@ -0,0 +1,180 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginExecutor.cc
+ */
+#include <iostream>
+#include "zypp/base/LogTools.h"
+#include "zypp/base/NonCopyable.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/PathInfo.h"
+#include "zypp/PluginExecutor.h"
+
+using std::endl;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::plugin"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /// \class PluginExecutor::Impl
+  /// \brief PluginExecutor implementation.
+  ///////////////////////////////////////////////////////////////////
+  class PluginExecutor::Impl : private base::NonCopyable
+  {
+  public:
+    Impl()
+    {}
+
+    ~Impl()
+    {
+      if ( ! empty() )
+       send( PluginFrame( "PLUGINEND" ) );
+      // ~PluginScript will disconnect all remaining plugins!
+    }
+
+    bool empty() const
+    { return _scripts.empty(); }
+
+    size_t size() const
+    { return _scripts.size(); }
+
+    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;
+    }
+
+    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;
+    }
+
+    const std::list<PluginScript> scripts() const
+    { return _scripts; }
+
+  private:
+    /** 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;
+      }
+    }
+
+    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);
+       WAR << e.asUserHistory() << endl;
+      }
+
+      // Allow using "/bin/cat" as reflector-script for testing
+      if ( ! ( ret.isAckCommand() || ret.isEnomethodCommand() || ( script_r.script() == "/bin/cat" && frame_r.command() != "ERROR" ) ) )
+      {
+       WAR << "Bad plugin response from " << script_r << ": " << ret << endl;
+       WAR << "(Expected " << PluginFrame::ackCommand() << " or " << PluginFrame::enomethodCommand() << ")" << endl;
+       script_r.close();
+      }
+
+      return ret;
+    }
+  private:
+    std::list<PluginScript> _scripts;
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginExecutor
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  PluginExecutor::PluginExecutor()
+    : _pimpl( new Impl() )
+  {}
+
+  PluginExecutor::~PluginExecutor()
+  {}
+
+  bool PluginExecutor::empty() const
+  { return _pimpl->empty(); }
+
+  size_t PluginExecutor::size() const
+  { return _pimpl->size(); }
+
+  void PluginExecutor::load( const Pathname & path_r )
+  { _pimpl->load( path_r ); }
+
+  void PluginExecutor::send( const PluginFrame & frame_r )
+  { _pimpl->send( frame_r ); }
+
+  std::ostream & operator<<( std::ostream & str, const PluginExecutor & obj )
+  { return str << obj._pimpl->scripts(); }
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PluginExecutor.h b/zypp/PluginExecutor.h
new file mode 100644 (file)
index 0000000..1f193ca
--- /dev/null
@@ -0,0 +1,97 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginExecutor.h
+ */
+#ifndef ZYPP_PLUGINEXECUTOR_H
+#define ZYPP_PLUGINEXECUTOR_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/PluginScript.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /// \class PluginExecutor
+  /// \brief Parallel execution of stateful PluginScripts
+  ///
+  /// Sent PluginFrames are distributed to all open PluginScripts and
+  /// need to be receipted by sending back either \c ACK or \c _ENOMETHOD
+  /// command.
+  ///
+  /// All PluginScripts receive an initial \c PLUGINBEGIN frame, containing
+  /// a \c userdata header if \ref ZConfig::userData are defined.
+  /// \see also zypper '--userdata' option
+  ///
+  /// A final \c PLUGINEND frame is sent and open scripts are closed, when the
+  /// executors last reference goes out of scope. Failing PluginScripts are
+  /// closed immediately.
+  ///
+  /// \see PluginScript
+  /// \ingroup g_RAII
+  ///////////////////////////////////////////////////////////////////
+  class PluginExecutor
+  {
+    friend std::ostream & operator<<( std::ostream & str, const PluginExecutor & obj );
+    friend bool operator==( const PluginExecutor & lhs, const PluginExecutor & rhs );
+
+    public:
+      /** Default ctor: Empty plugin list */
+      PluginExecutor();
+
+      /** Dtor: Send \c PLUGINEND and close all plugins */
+      ~PluginExecutor();
+
+    public:
+      /**  Validate object in a boolean context: There are plugins waiting for input */
+      explicit operator bool() const
+      { return !empty(); }
+
+      /** Whether no plugins are waiting */
+      bool empty() const;
+
+      /** Number of open plugins */
+      size_t size() const;
+
+    public:
+      /** Find and launch plugins sending \c PLUGINBEGIN.
+       *
+       * If \a path_r is a directory all executable files within are
+       * expected to be plugins. Otherwise \a path_r must point to an
+       * executable plugin.
+       */
+      void load( const Pathname & path_r );
+
+      /** Send \ref PluginFrame to all open plugins.
+       * Failed plugins are removed from the execution list.
+       */
+      void send( const PluginFrame & frame_r );
+
+    public:
+      class Impl;              ///< Implementation class.
+    private:
+      RW_pointer<Impl> _pimpl; ///< Pointer to implementation.
+  };
+
+  /** \relates PluginExecutor Stream output */
+  std::ostream & operator<<( std::ostream & str, const PluginExecutor & obj );
+
+  /** \relates PluginExecutor Comparison based on reference. */
+  inline bool operator==( const PluginExecutor & lhs, const PluginExecutor & rhs )
+  { return ( lhs._pimpl == rhs._pimpl ); }
+
+  /** \relates PluginExecutor Comparison based on reference. */
+  inline bool operator!=( const PluginExecutor & lhs, const PluginExecutor & rhs )
+  { return( ! operator==( lhs, rhs ) ); }
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PLUGINEXECUTOR_H
index 8683fe2..96c7bad 100644 (file)
@@ -89,9 +89,9 @@ namespace zypp
       /** Whether this is an empty frame. */
       bool empty() const;
 
-      /** Evaluate in a boolean context (empty frame) */
+      /** Evaluate in a boolean context (not an empty frame) */
       explicit operator bool() const
-      { return empty(); }
+      { return !empty(); }
 
     public:
       /** Return the frame command. */
@@ -174,7 +174,7 @@ namespace zypp
       bool keyEmpty( const std::string & key_r ) const
       { return headerList().find( key_r ) == headerEnd(); }
 
-      /** Return number of header entires for \c key_r. */
+      /** Return number of header entries for \c key_r. */
       bool keySize( const std::string & key_r ) const
       { return headerList().count( key_r ); }
 
@@ -201,7 +201,7 @@ namespace zypp
       /** Not throwing version returing one of the matching header values or \c default_r string. */
       const std::string & getHeaderNT( const std::string & key_r, const std::string & default_r = std::string() ) const;
 
-      /** Set header for \c key_r removing all other occurences of \c key_r.
+      /** Set header for \c key_r removing all other occurrences of \c key_r.
        * \throw PluginFrameException If key contains illegal chars (\c NL or \c :)
        * \throw PluginFrameException If value contains illegal chars (\c NL)
        */
@@ -256,9 +256,9 @@ namespace zypp
   /** \relates PluginFrame Stream output for logging */
   std::ostream & operator<<( std::ostream & str, const PluginFrame & obj );
 
-  /** \relates PluginFrame Stream output sending all data */
+  /** \relates PluginFrame Stream output writing all data for logging (no throw) */
   inline std::ostream & dumpOn( std::ostream & str, const PluginFrame & obj )
-  { return PluginFrame::writeTo( str, obj ); }
+  { if ( str ) try { PluginFrame::writeTo( str, obj ); } catch(...){}; return str; }
 
   /** \relates PluginFrame Construct from stream. */
   inline std::istream & operator>>( std::istream & str, PluginFrame & obj )
index 2f1e4d9..fa64aa8 100644 (file)
@@ -27,7 +27,7 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   /**
-   * \brief Interface to pluigin scripts using a \c Stomp inspired communication protocol.
+   * \brief Interface to plugin scripts using a \c Stomp inspired communication protocol.
    *
    * \note \ref PluginScript is copyable and assignable, but the connection is shared
    * among multiple copies. It gets automatically closed if the last copy goes out of
index 420950b..8bf5533 100644 (file)
@@ -54,6 +54,12 @@ namespace zypp
     , _end( std::move(end_r) )
     {}
 
+    /** Ctor taking the iterator pair */
+    Iterable( std::pair<iterator_type,iterator_type> range_r )
+    : _begin( std::move(range_r.first) )
+    , _end( std::move(range_r.second) )
+    {}
+
     iterator_type begin() const
     { return _begin; }
 
@@ -82,6 +88,10 @@ namespace zypp
   Iterable<TIterator> makeIterable( TIterator && begin_r, TIterator && end_r )
   { return Iterable<TIterator>( std::forward<TIterator>(begin_r), std::forward<TIterator>(end_r) ); }
 
+  /** \relates Iterable convenient construction. */
+  template <class TIterator>
+  Iterable<TIterator> makeIterable( std::pair<TIterator,TIterator> && range_r )
+  { return Iterable<TIterator>( std::forward<std::pair<TIterator,TIterator>>(range_r) ); }
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
 #endif // ZYPP_BASE_ITERABLE_H
index b2d2457..bcc7661 100644 (file)
@@ -54,7 +54,7 @@
 #include "zypp/sat/detail/PoolImpl.h"
 #include "zypp/sat/Transaction.h"
 
-#include "zypp/PluginScript.h"
+#include "zypp/PluginExecutor.h"
 
 using namespace std;
 
@@ -231,149 +231,6 @@ namespace zypp
     } // 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
     {
@@ -1097,27 +954,13 @@ namespace zypp
         guard.resetDispose();
        sat::updateSolvFileIndex( rpmsolv );    // content digest for zypper bash completion
 
-       // Finally send notification to plugins
-       // NOTE: quick hack looking for spacewalk plugin only
+       // system-hook: Finally send notification to plugins
+       if ( root() == "/" )
        {
-         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;
-           }
+         PluginExecutor plugins;
+         plugins.load( ZConfig::instance().pluginsPath()/"system" );
+         if ( plugins )
+           plugins.send( PluginFrame( "PACKAGESETCHANGED" ) );
        }
       }
       else
@@ -1298,13 +1141,12 @@ namespace zypp
       ///////////////////////////////////////////////////////////////////
       // Prepare execution of commit plugins:
       ///////////////////////////////////////////////////////////////////
-      CommitPlugins commitPlugins;
+      PluginExecutor commitPlugins;
       if ( root() == "/" && ! policy_r.dryRun() )
       {
-       Pathname plugindir( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"commit" ) );
-       commitPlugins.load( plugindir );
+       commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
       }
-      if ( ! commitPlugins.empty() )
+      if ( commitPlugins )
        commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
 
       ///////////////////////////////////////////////////////////////////
@@ -1495,7 +1337,7 @@ namespace zypp
       ///////////////////////////////////////////////////////////////////
       // Send result to commit plugins:
       ///////////////////////////////////////////////////////////////////
-      if ( ! commitPlugins.empty() )
+      if ( commitPlugins )
        commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
 
       ///////////////////////////////////////////////////////////////////