fixup Fix to build with libxml 2.12.x (fixes #505)
[platform/upstream/libzypp.git] / zypp / ExternalProgram.h
index 212f213..5f46aa4 100644 (file)
 #ifndef ZYPP_EXTERNALPROGRAM_H
 #define ZYPP_EXTERNALPROGRAM_H
 
+#include <unistd.h>
+
 #include <map>
 #include <string>
+#include <vector>
 
-#include "zypp/base/ExternalDataSource.h"
-#include "zypp/Pathname.h"
+#include <zypp/APIConfig.h>
+#include <zypp/base/ExternalDataSource.h>
+#include <zypp/Pathname.h>
 
 namespace zypp {
 
@@ -27,11 +31,38 @@ namespace zypp {
      * an external program. It starts the program using fork
      * and some exec.. call, gives you access to the program's
      * stdio and closes the program after use.
+     *
+     * \code
+     *
+     * const char* argv[] =
+     * {
+     *     "/usr/bin/foo,
+     *     "--option1",
+     *     "--option2",
+     *     NULL
+     * };
+     *
+     * ExternalProgram prog( argv,
+     *                        ExternalProgram::Discard_Stderr,
+     *                        false, -1, true);
+     * string line;
+     * for(line = prog.receiveLine();
+     *     ! line.empty();
+     *     line = prog.receiveLine() )
+     * {
+     *     stream << line;
+     * }
+     * prog.close();
+     *
+     * \endcode
      */
     class ExternalProgram : public zypp::externalprogram::ExternalDataSource
     {
 
     public:
+
+      typedef std::vector<std::string> Arguments;
+
       /**
        * Define symbols for different policies on the handling
        * of stderr
@@ -43,6 +74,7 @@ namespace zypp {
        Stderr_To_FileDesc
       };
 
+
       /**
        * For passing additional environment variables to set
        */
@@ -54,7 +86,7 @@ namespace zypp {
        * @param commandline a shell commandline that is appended to
        * <tt>/bin/sh -c</tt>.
        * @param default_locale whether to set LC_ALL=C before starting
-       * @param root directory to chroot into, / or empty to not chroot
+       * @param root directory to chroot into; or just 'cd' if '/'l;  nothing if empty
        */
       ExternalProgram (std::string commandline,
                     Stderr_Disposition stderr_disp = Normal_Stderr,
@@ -65,10 +97,36 @@ namespace zypp {
        * Start an external program by giving the arguments as an arry of char *pointers.
        * If environment is provided, varaiables will be added to the childs environment,
        * overwriting existing ones.
+       *
+       * Initial args starting with \c # are discarded but some are treated specially:
+       *       #/[path] - chdir to /[path] before executing
+       *
+       * Stdin redirection: If the \b 1st argument starts with a \b '<', the remaining
+       * part is treated as file opened for reading on standard input (or \c /dev/null
+       * if empty).
+       * \code
+       *   // cat file /tmp/x
+       *   const char* argv[] = { "</tmp/x", "cat", NULL };
+       *   ExternalProgram prog( argv );
+       * \endcode
+       *
+       * Stdout redirection: If the \b 1st argument starts with a \b '>', the remaining
+       * part is treated as file opened for writing on standard output (or \c /dev/null
+       * if empty).
        */
 
       ExternalProgram();
 
+      ExternalProgram (const Arguments &argv,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      ExternalProgram (const Arguments &argv, const Environment & environment,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
       ExternalProgram (const char *const *argv,
                     Stderr_Disposition stderr_disp = Normal_Stderr,
                     bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
@@ -89,6 +147,7 @@ namespace zypp {
 
       ~ExternalProgram();
 
+      /** Wait for the progamm to complete. */
       int close();
 
       /**
@@ -97,6 +156,11 @@ namespace zypp {
       bool kill();
 
       /**
+       * Send a signal to the program
+       */
+      bool kill( int sig );
+
+      /**
        * Return whether program is running
        */
       bool running();
@@ -106,11 +170,49 @@ namespace zypp {
        * */
       pid_t getpid() { return pid; }
 
+      /** The command we're executing. */
+      const std::string & command() const
+      { return _command; }
+
+      /** Some detail telling why the execution failed, if it failed.
+       * Empty if the command is still running or successfully completed.
+       *
+       * \li <tt>Can't open pty (%s).</tt>
+       * \li <tt>Can't open pipe (%s).</tt>
+       * \li <tt>Can't fork (%s).</tt>
+       * \li <tt>Command exited with status %d.</tt>
+       * \li <tt>Command was killed by signal %d (%s).</tt>
+      */
+      const std::string & execError() const
+      { return _execError; }
+
       /**
        * origfd will be accessible as newfd and closed (unless they were equal)
        */
       static void renumber_fd (int origfd, int newfd);
 
+    public:
+
+      /**
+       * Redirect all command output to an \c ostream.
+       * Returns when the command has completed.
+       * \code
+       *   std::ostringstream s;
+       *   ExternalProgram("pwd") >> s;
+       *   SEC << s.str() << endl;
+       * \endcode
+       * \code
+       *   std::ostringstream s;
+       *   ExternalProgram prog("ls -l wrzl");
+       *   prog >> s;
+       *   if ( prog.close() == 0 )
+       *     MIL << s.str() << endl;
+       *   else
+       *     ERR << prog.execError() << endl;
+       * \endcode
+       */
+      std::ostream & operator>>( std::ostream & out_r );
+
     protected:
       int checkStatus( int );
 
@@ -124,14 +226,103 @@ namespace zypp {
 
       pid_t pid;
       int _exitStatus;
+      /** Store the command we're executing. */
+      std::string _command;
+      /** Remember execution errors like failed fork/exec. */
+      std::string _execError;
+
+    protected:
 
       void start_program (const char *const *argv, const Environment & environment,
                        Stderr_Disposition stderr_disp = Normal_Stderr,
                        int stderr_fd = -1, bool default_locale = false,
-                       const char* root = NULL);
+                       const char* root = NULL, bool switch_pgid = false, bool die_with_parent = false );
 
     };
 
+
+  namespace externalprogram
+  {
+    /** Helper providing pipe FDs for \ref ExternalProgramWithStderr.
+     * Moved to a basse class because the pipe needs to be initialized
+     * before the \ref ExternalProgram base class is initialized.
+     * \see \ref ExternalProgramWithStderr
+     */
+    struct EarlyPipe
+    {
+      enum { R=0, W=1 };
+      EarlyPipe();
+      ~EarlyPipe();
+      void closeW()            { if ( _fds[W] != -1 ) { ::close( _fds[W] ); _fds[W] = -1; } }
+      FILE * fStdErr()         { return _stderr; }
+      protected:
+       FILE * _stderr;
+       int _fds[2];
+    };
+  } // namespace externalprogram
+
+  /** ExternalProgram extended to offer reading programs stderr.
+   * \see \ref ExternalProgram
+   */
+  class ExternalProgramWithStderr : private externalprogram::EarlyPipe, public ExternalProgram
+  {
+    public:
+      ExternalProgramWithStderr( const Arguments & argv_r, bool defaultLocale_r = false, const Pathname & root_r = "" )
+      : ExternalProgram( argv_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W], defaultLocale_r, root_r )
+      { _initStdErr(); }
+      /** \overlocad Convenience taking just the \a root_r. */
+      ExternalProgramWithStderr( const Arguments & argv_r, const Pathname & root_r )
+      : ExternalProgramWithStderr( argv_r, false, root_r )
+      {}
+
+      ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r, bool defaultLocale_r = false, const Pathname & root_r = "" )
+      : ExternalProgram( argv_r, environment_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W], defaultLocale_r, root_r )
+      { _initStdErr(); }
+      /** \overlocad Convenience taking just the \a root_r.  */
+      ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r, const Pathname & root_r )
+      : ExternalProgramWithStderr( argv_r, environment_r, false, root_r )
+      {}
+  public:
+      /** Return \c FILE* to read programms stderr (O_NONBLOCK set). */
+      using externalprogram::EarlyPipe::fStdErr;
+
+      /** Read data up to \c delim_r from stderr (nonblocking).
+       * \note If \c delim_r is '\0', we read as much data as possible.
+       * \return \c false if data are not yet available (\c retval_r remains untouched then).
+       */
+      bool stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r = false );
+
+      /** Read next complete line from stderr (nonblocking).
+       * \return \c false if data are not yet available (\c retval_r remains untouched then).
+       */
+      bool stderrGetline( std::string & retval_r, bool returnDelim_r = false  )
+      { return stderrGetUpTo( retval_r, '\n', returnDelim_r ); }
+
+    private:
+      /** Close write end of the pipe (childs end). */
+      void _initStdErr()
+      { closeW(); }
+
+    private:
+      std::string _buffer;
+  };
+
+  /** ExternalProgram extended to change the progress group ID after forking.
+   * \see \ref ExternalProgram
+   */
+  class ZYPP_LOCAL ExternalProgramWithSeperatePgid : public ExternalProgram
+  {
+    public:
+      ExternalProgramWithSeperatePgid (const char *const *argv,
+                   Stderr_Disposition stderr_disp = Normal_Stderr,
+                   int stderr_fd = -1, bool default_locale = false,
+                   const Pathname& root = "") : ExternalProgram()
+      {
+        start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str(), true );
+      }
+
+  };
+
 } // namespace zypp
 
 #endif // ZYPP_EXTERNALPROGRAM_H