BASIS  version 1.2.3 (revision 2104)
subprocess.h
Go to the documentation of this file.
00001 /**
00002  * @file  subprocess.h
00003  * @brief Module used to execute subprocesses.
00004  *
00005  * Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
00006  * See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007  *
00008  * Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009  */
00010 
00011 #pragma once
00012 #ifndef _SBIA_BASIS_SUBPROCESS_H
00013 #define _SBIA_BASIS_SUBPROCESS_H
00014 
00015 
00016 #include <vector>
00017 #include <string>
00018 
00019 #if (defined (_WIN32) || defined (WIN32) || defined (_WINDOWS))
00020 #  include <windows.h>
00021 #else
00022 #  include <sys/types.h>
00023 #  include <unistd.h>
00024 #endif
00025 
00026 
00027 namespace sbia
00028 {
00029 
00030 namespace basis
00031 {
00032 
00033 
00034 /**
00035  * @class Subprocess
00036  * @brief Platform-independent interface to create and control a subprocess.
00037  */
00038 class Subprocess
00039 {
00040     // -----------------------------------------------------------------------
00041     // types
00042 public:
00043 
00044     typedef std::vector<std::string> CommandLine;
00045     typedef std::vector<std::string> Environment;
00046 
00047 private:
00048 
00049     /// Type used for file handles of pipes between processes.
00050 #if (defined (_WIN32) || defined (WIN32) || defined (_WINDOWS))
00051     typedef HANDLE PipeHandle;
00052 #else
00053     typedef int    PipeHandle;
00054 #endif
00055 
00056     /// Information structure required by system to identify subprocess.
00057 #if (defined (_WIN32) || defined (WIN32) || defined (_WINDOWS))
00058     typedef PROCESS_INFORMATION Information;
00059 #else
00060     struct Information
00061     {
00062         pid_t pid;
00063     };
00064 #endif
00065 
00066     // -----------------------------------------------------------------------
00067     // constants
00068 public:
00069 
00070     /**
00071      * @brief Modes of redirection for standard input/output buffers.
00072      */
00073     enum RedirectMode
00074     {
00075         RM_NONE,  ///< Do not redirect the input/output.
00076         RM_PIPE,  ///< Use a pipe to redirect the input/output from/to the parent.
00077         RM_STDOUT ///< Redirect stderr to stdout.
00078     };
00079 
00080     // -----------------------------------------------------------------------
00081     // construction / destruction
00082 public:
00083 
00084     /**
00085      * @brief Default constructor.
00086      */
00087     Subprocess();
00088 
00089     /**
00090      * @brief Terminate running subprocess and close all related handles.
00091      */
00092     ~Subprocess();
00093 
00094     // -----------------------------------------------------------------------
00095     // helpers
00096 public:
00097 
00098     /**
00099      * @brief Split double quoted string into arguments.
00100      *
00101      * @param [in] cmd Double quoted string. Use '\' to escape double quotes
00102      *                 within arguments.
00103      *
00104      * @returns Argument vector.
00105      */
00106     static CommandLine split(const std::string& cmd);
00107 
00108     /**
00109      * @brief Convert argument vector to double quoted string.
00110      *
00111      * @param [in] args Argument vector.
00112      *
00113      * @returns Double quoted string.
00114      */
00115     static std::string tostring(const CommandLine& args);
00116 
00117     // -----------------------------------------------------------------------
00118     // process control
00119 public:
00120 
00121     /**
00122      * @brief Open new subprocess.
00123      *
00124      * This method creates the subprocess and returns immediately. In order to
00125      * wait for the suprocess to finish, the wait() method has to be called
00126      * explicitly.
00127      *
00128      * @param [in] args   Command-line of subprocess. The first argument has to
00129      *                    be the name/path of the command to be executed.
00130      * @param [in] rm_in  Mode used for redirection of stdin of subprocess.
00131      *                    Can be either RM_NONE or RM_PIPE.
00132      * @param [in] rm_out Mode used for redirection of stdout of subprocess.
00133      *                    Can be either RM_NONE or RM_PIPE.
00134      * @param [in] rm_err Mode used for redirection of stderr of subprocess.
00135      *                    Can be either RM_NONE, RM_PIPE, or RM_STDOUT.
00136      * @param [in] env    Environment for the subprocess. If NULL is given, the
00137      *                    environment of the parent process is used.
00138      *
00139      * @returns Whether the subprocess was created successfully.
00140      */
00141     bool popen(const CommandLine& args,
00142                // attention: stdin, stdout, and stderr are macros defined by windows.h
00143                const RedirectMode rm_in  = RM_NONE,
00144                const RedirectMode rm_out = RM_NONE,
00145                const RedirectMode rm_err = RM_NONE,
00146                const Environment* env    = NULL);
00147 
00148     /**
00149      * @brief Open new subprocess.
00150      *
00151      * This method creates the subprocess and returns immediately. In order to
00152      * wait for the suprocess to finish, the wait() method has to be called
00153      * explicitly.
00154      *
00155      * @param [in] cmd    Command-line given as double quoted string. Arguments
00156      *                    containing whitespaces have to be quoted using double
00157      *                    quotes. Use a backslash (\) to escape double quotes
00158      *                    inside an argument as well as to escape a backslash
00159      *                    itself (required if backslash at end of double quoted
00160      *                    argument, e.g., "this argument \\").
00161      * @param [in] rm_in  Mode used for redirection of stdin of subprocess.
00162      *                    Can be either RM_NONE or RM_PIPE.
00163      * @param [in] rm_out Mode used for redirection of stdout of subprocess.
00164      *                    Can be either RM_NONE or RM_PIPE.
00165      * @param [in] rm_err Mode used for redirection of stderr of subprocess.
00166      *                    Can be either RM_NONE, RM_PIPE, or RM_STDOUT.
00167      * @param [in] env    Environment for the subprocess. If NULL is given, the
00168      *                    environment of the parent process is used.
00169      */
00170     bool popen(const std::string& cmd,
00171                // attention: stdin, stdout, and stderr are macros defined by windows.h
00172                const RedirectMode rm_in  = RM_NONE,
00173                const RedirectMode rm_out = RM_NONE,
00174                const RedirectMode rm_err = RM_NONE,
00175                const Environment* env    = NULL)
00176     {
00177         return popen(split(cmd), rm_in, rm_out, rm_err, env);
00178     }
00179 
00180     /**
00181      * @brief Check if subprocess terminated and update return code.
00182      *
00183      * This method returns immediately and does not wait for the subprocess to
00184      * actually being terminated. For that purpuse, use wait() instead.
00185      *
00186      * @returns Whether the subprocess terminated.
00187      */
00188     bool poll() const;
00189 
00190     /**
00191      * @brief Wait for subprocess to terminate.
00192      *
00193      * This method also sets the exit code as returned by GetExitCode().
00194      */
00195     bool wait();
00196 
00197     /**
00198      * @brief Send signal to subprocess.
00199      *
00200      * On Windows, SIGTERM is an alias for terminate() and SIGKILL an alias for
00201      * kill() which in turn is nothing else but a termination of the subprocess.
00202      * All other signals are only sent to POSIX processes.
00203      */
00204     bool send_signal(int signal);
00205 
00206     /**
00207      * @brief Terminate subprocess.
00208      *
00209      * - On POSIX, the SIGTERM signal is send.
00210      * - On Windows, TerminateProcess() is invoked to terminate the subprocess. 
00211      */
00212     bool terminate();
00213 
00214     /**
00215      * @brief Kill subprocess.
00216      *
00217      * - On POSIX, the SIGKILL signal is send.
00218      * - On Windows, TerminateProcess() is invoked to terminate the subprocess. 
00219      */
00220     bool kill();
00221 
00222     /**
00223      * @returns Wether the subprocess has been signaled, i.e., terminated abnormally.
00224      */
00225     bool signaled() const;
00226 
00227     /**
00228      * @returns ID of subprocess.
00229      */
00230     int pid() const;
00231 
00232     /**
00233      * @returns Exit code of subprocess. Only valid if terminated() is true.
00234      */
00235     int returncode() const;
00236 
00237     // -----------------------------------------------------------------------
00238     // inter-process communication
00239 public:
00240 
00241     /**
00242      * @brief Communicate with subprocess.
00243      *
00244      * @note This method closes the pipes to the subprocess after all data
00245      *       has been sent and received and returns after the subprocess
00246      *       terminated. Therefore, the exit code is set upon return.
00247      *
00248      * @param [in] in  Data send to subprocess via pipe to stdin.
00249      *                 If no pipe was setup during subprocess creation,
00250      *                 this function does nothing and returns false.
00251      * @param [in] out Data read from stdout of subprocess. Can be an empty
00252      *                 string if no pipe was created for stdout.
00253      * @param [in] err Data read from stderr of subprocess. Can be an empty
00254      *                 string if no pipe was created for stderr.
00255      *
00256      * @returns Whether the communication with the subprocess was successful.
00257      */
00258     bool communicate(std::istream& in, std::ostream& out, std::ostream& err);
00259 
00260     /**
00261      * @brief Communicate with subprocess.
00262      *
00263      * @note This method closes the pipes to the subprocess after all data
00264      *       has been received and returns after the subprocess terminated.
00265      *       Therefore, the exit code is set upon return.
00266      *
00267      * @param [in] out Data read from stdout of subprocess. Can be an empty
00268      *                 string if no pipe was created for stdout.
00269      * @param [in] err Data read from stderr of subprocess. Can be an empty
00270      *                 string if no pipe was created for stderr.
00271      *
00272      * @returns Whether the communication with the subprocess was successful.
00273      */
00274     bool communicate(std::ostream& out, std::ostream& err);
00275 
00276     /**
00277      * @brief Communicate with subprocess.
00278      *
00279      * @note This method closes the pipes to the subprocess after all data
00280      *       has been received and returns after the subprocess terminated.
00281      *       Therefore, the exit code is set upon return.
00282      *
00283      * @param [in] out Data read from stdout of subprocess. Can be an empty
00284      *                 string if no pipe was created for stdout.
00285      *
00286      * @returns Whether the communication with the subprocess was successful.
00287      */
00288     bool communicate(std::ostream& out);
00289 
00290     /**
00291      * @brief Write data to stdin of subprocess.
00292      *
00293      * @param [in] buf  Bytes to write.
00294      * @param [in] nbuf Number of bytes from @p buf to write.
00295      *
00296      * @returns Number of bytes written or -1 on error.
00297      */
00298     int write(const void* buf, size_t nbuf);
00299 
00300     /**
00301      * @brief Read data from stdout or stderr of subprocess.
00302      *
00303      * @param [out] buf  Allocated buffer to store read data to.
00304      * @param [in]  nbuf Number of bytes to read from subprocess.
00305      * @param [in]  err  If true and the redirection mode of stderr is RM_PIPE,
00306      *                   the data is read from stderr of the subprocess.
00307      *                   Otherwise, the data is read from stdout.
00308      *
00309      * @returns Number of bytes read or -1 on error.
00310      */
00311     int read(void* buf, size_t nbuf, bool err = false);
00312 
00313     // -----------------------------------------------------------------------
00314     // process execution
00315 public:
00316 
00317     /**
00318      * @brief Execute command as subprocess.
00319      *
00320      * This function is implemented in the same manner as system() on Unix.
00321      * It simply creates a Subprocess instance, executes the subprocess and
00322      * waits for its termination.
00323      *
00324      * Example:
00325      * @code
00326      * Subprocess::CommandLine cmd;
00327      * cmd.push_back("ls");
00328      * cmd.push_back("some directory");
00329      * int status = Subprocess::call(cmd);
00330      * @endcode
00331      *
00332      * @returns Exit code of subprocess or -1 on error.
00333      */
00334     static int call(const CommandLine& cmd);
00335 
00336     /**
00337      * @brief Execute command as subprocess.
00338      *
00339      * This function is implemented in the same manner as system() on Unix.
00340      * It simply creates a Subprocess instance, executes the subprocess and
00341      * waits for its termination.
00342      *
00343      * Example:
00344      * @code
00345      * int status = Subprocess::call("ls \"some directory\"");
00346      * @endcode
00347      *
00348      * @param [in] cmd Command-line given as single string. Quote arguments
00349      *                 containing whitespace characters using ".
00350      *
00351      * @returns Exit code of subprocess or -1 on error.
00352      */
00353     static int call(const std::string& cmd);
00354 
00355     // -----------------------------------------------------------------------
00356     // unsupported operations
00357 private:
00358 
00359     /**
00360      * @brief Copy constructor.
00361      *
00362      * @note Intentionally not implemented.
00363      */
00364     Subprocess(const Subprocess&);
00365 
00366     /**
00367      * @brief Assignment operator.
00368      *
00369      * @note Intentionally not implemented.
00370      */
00371     void operator=(const Subprocess&);
00372 
00373     // -----------------------------------------------------------------------
00374     // members
00375 private:
00376 
00377     Information _info;   ///< Subprocess information.
00378     PipeHandle  _stdin;  ///< Used to write data to stdin of subprocess.
00379     PipeHandle  _stdout; ///< Used to read data from stdout of subprocess.
00380     PipeHandle  _stderr; ///< Used to read data from stderr of subprocess.
00381     mutable int _status; ///< Status of subprocess.
00382 
00383 }; // class Subprocess
00384 
00385 
00386 } // namespace basis
00387 
00388 } // namespace sbia
00389 
00390 
00391 #endif // _SBIA_BASIS_SUBPROCESS_H