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