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