Imported Upstream version 15.0.0
[platform/upstream/libzypp.git] / zypp / target / TargetImpl.cc
index 1a8a803..edd1055 100644 (file)
@@ -56,7 +56,7 @@
 #include "zypp/sat/Pool.h"
 #include "zypp/sat/Transaction.h"
 
-#include "zypp/PluginExecutor.h"
+#include "zypp/PluginScript.h"
 
 using namespace std;
 
@@ -220,6 +220,149 @@ 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
     {
@@ -916,7 +1059,7 @@ namespace zypp
 
         cmd << "  > '" << tmpsolv.path() << "'";
 
-        MIL << "Executing: " << cmd << endl;
+        MIL << "Executing: " << cmd.str() << endl;
         ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
 
         cmd << endl;
@@ -944,13 +1087,27 @@ namespace zypp
         // 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;
@@ -1125,18 +1282,19 @@ namespace zypp
       ///////////////////////////////////////////////////////////////////
       // 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() )
         {
@@ -1321,7 +1479,7 @@ namespace zypp
       ///////////////////////////////////////////////////////////////////
       // Send result to commit plugins:
       ///////////////////////////////////////////////////////////////////
-      if ( commitPlugins )
+      if ( ! commitPlugins.empty() )
        commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
 
       ///////////////////////////////////////////////////////////////////
@@ -1341,20 +1499,6 @@ namespace zypp
     // 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 )
@@ -1363,11 +1507,7 @@ namespace zypp
       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;
@@ -1442,7 +1582,6 @@ namespace zypp
             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 );
@@ -1506,8 +1645,6 @@ namespace zypp
             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 );