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