1 #ifndef CONNECT___NCBI_PIPE__HPP 2 #define CONNECT___NCBI_PIPE__HPP 3 4 /* $Id: ncbi_pipe.hpp 605967 2020-04-17 07:03:48Z lavr $ 5 * =========================================================================== 6 * 7 * PUBLIC DOMAIN NOTICE 8 * National Center for Biotechnology Information 9 * 10 * This software/database is a "United States Government Work" under the 11 * terms of the United States Copyright Act. It was written as part of 12 * the author's official duties as a United States Government employee and 13 * thus cannot be copyrighted. This software/database is freely available 14 * to the public for use. The National Library of Medicine and the U.S. 15 * Government have not placed any restriction on its use or reproduction. 16 * 17 * Although all reasonable efforts have been taken to ensure the accuracy 18 * and reliability of the software and data, the NLM and the U.S. 19 * Government do not and cannot warrant the performance or results that 20 * may be obtained by using this software or data. The NLM and the U.S. 21 * Government disclaim all warranties, express or implied, including 22 * warranties of performance, merchantability or fitness for any particular 23 * purpose. 24 * 25 * Please cite the author in any work or product based on this material. 26 * 27 * =========================================================================== 28 * 29 * Authors: Anton Lavrentiev, Vladimir Ivanov 30 * 31 * 32 */ 33 34 /// @file ncbi_pipe.hpp 35 /// Portable class to work with a spawned process via pipes. 36 /// 37 /// Defines classes: 38 /// CPipe - class to work with a spawned process via pipes 39 /// 40 /// Implemented for: UNIX, MS-Windows 41 42 #include <corelib/ncbi_process.hpp> 43 #include <connect/ncbi_core_cxx.hpp> 44 45 #if !defined(NCBI_OS_MSWIN) && !defined(NCBI_OS_UNIX) 46 # error "Class CPipe is only supported on Windows and Unix" 47 #endif 48 49 50 /** @addtogroup Pipes 51 * 52 * @{ 53 */ 54 55 56 BEGIN_NCBI_SCOPE 57 58 59 // Forward declaration. 60 class CPipeHandle; 61 62 63 ///////////////////////////////////////////////////////////////////////////// 64 /// 65 /// CPipe -- 66 /// 67 /// Spawn a child process (command) with pipes attached to its standard I/O. 68 /// 69 /// The application can then read from stdin/stderr and write to stdin of the 70 /// launched child process using the Read/Write methods of the pipe object. 71 /// 72 /// @sa 73 /// CNamedPipe, CExec 74 75 class NCBI_XCONNECT_EXPORT CPipe : protected CConnIniter 76 { 77 public: 78 /// Flags for creating standard I/O handles of the child process. 79 /// @note@ Flags pertaining to the same stdio handle processed in the 80 /// order of their appearance in the definition below. 81 /// 82 /// Default is 0: 83 /// fStdIn_Open | fStdOut_Open | fStdErr_Close | fCloseOnClose. 84 enum ECreateFlag { 85 fStdIn_Open = 0, ///< Do open child's stdin (default). 86 fStdIn_Close = 0x001, ///< Do not open child's stdin. 87 fStdOut_Open = 0, ///< Do open child's stdout (default). 88 fStdOut_Close = 0x002, ///< Do not open child's stdout. 89 fStdErr_Open = 0x004, ///< Do open child's stderr. 90 fStdErr_Close = 0, ///< Do not open child's stderr (default). 91 fStdErr_Share = 0x008, ///< Keep stderr (share it with child). 92 fStdErr_StdOut = 0x080, ///< Redirect stderr to whatever stdout goes. 93 fKeepOnClose = 0x010, ///< Close(): just return eIO_Timeout 94 ///< if Close() cannot complete within 95 ///< the allotted time; don't close any 96 ///< pipe handles nor signal the child. 97 fCloseOnClose = 0, ///< Close(): always close all pipe handles 98 ///< but do not send any signal to running 99 ///< process if Close()'s timeout expired. 100 fKillOnClose = 0x020, ///< Close(): kill child process if it hasn't 101 ///< terminated within the allotted time. 102 ///< NOTE: If both fKeepOnClose and 103 ///< fKillOnClose are set, the safer 104 ///< fKeepOnClose takes the effect. 105 fSigPipe_Restore = 0x040, ///< Restore SIGPIPE processing for child 106 ///< process to system default. 107 fNewGroup = 0x100 ///< UNIX: new process group will be created, 108 ///< and the child process will become the 109 ///< leader of the new process group. 110 }; 111 typedef unsigned int TCreateFlags; ///< bitwise OR of "ECreateFlag" 112 113 /// Which of the child I/O handles to use. 114 enum EChildIOHandle { 115 fStdIn = (1 << 0), 116 fStdOut = (1 << 1), 117 fStdErr = (1 << 2), 118 fDefault = (1 << 3), 119 eStdIn = fStdIn, 120 eStdOut = fStdOut, 121 eStdErr = fStdErr, 122 eDefault = fDefault ///< see SetReadHandle() 123 }; 124 typedef unsigned int TChildPollMask; ///< bitwise OR of "EChildIOHandle" 125 126 /// Constructor. 127 CPipe(size_t pipe_size = 0/*use default*/); 128 129 /// Constructor. 130 /// 131 /// Call the Open() method to actually open the pipe. 132 /// Throw CPipeException on failure to create the pipe. 133 /// 134 /// @param cmd 135 /// Command name to execute. 136 /// @param args 137 /// Vector of string arguments for the command (argv[0] excluded). 138 /// @param create_flags 139 /// Specifies the options to be applied when creating the pipe. 140 /// @param current_dir 141 /// Current working directory for the new process if specified. 142 /// @param env 143 /// An optional pointer to a vector with environment variables to use 144 /// in the child process. If not speficied, a copy of the parent's 145 /// process environment is used. Note that a new environment completely 146 /// replaces the default environment otherwise inherited from the parent 147 /// process, and so it does not just add / modify some values. 148 /// @sa 149 /// Open 150 CPipe(const string& cmd, 151 const vector<string>& args, 152 TCreateFlags create_flags = 0, 153 const string& current_dir = kEmptyStr, 154 const char* const env[] = 0, 155 size_t pipe_size = 0/*use default*/); 156 157 /// Destructor. 158 /// 159 /// If the pipe was opened then the destructor first closes it by calling 160 /// Close(). 161 /// @sa 162 /// Close 163 ~CPipe(void); 164 165 /// Open pipe. 166 /// 167 /// Execute a command with the vector of arguments "args". The other end 168 /// of the pipe is associated with the spawned command's standard 169 /// input/output/error according to "create_flags". 170 /// 171 /// @param cmd 172 /// Command name to execute. 173 /// Note when specifying both "cmd" with relative path and non-empty 174 /// "current_dir": at run-time the current directory must be considered 175 /// undefined, as it may still be the same of the parent process that 176 /// issues the call, or it can already be changed to the specified 177 /// "current_dir". So, using the absolute path for "cmd" is always 178 /// recommended in such cases. 179 /// @param args 180 /// Vector of string arguments for the command (argv[0] excluded). 181 /// @param create_flags 182 /// Specifies options to be applied when creating the pipe. 183 /// @param current_dir 184 /// Current working directory for the new process. 185 /// The string must be an absolute path. On MS Windows it should 186 /// also contain a drive letter. If this parameter is empty, the new 187 /// process will have the same current directory as the calling process. 188 /// @param env 189 /// An optional pointer to a vector with environment variables to use 190 /// in the child process. If not speficied, a copy of the parent's 191 /// process environment is used. Note that a new environment completely 192 /// replaces the default environment otherwise inherited from the parent 193 /// process, and so it does not just add / modify some values. 194 /// @return 195 /// Completion status. 196 /// @sa 197 /// Read, Write, Close 198 EIO_Status Open(const string& cmd, 199 const vector<string>& args, 200 TCreateFlags create_flags = 0, 201 const string& current_dir = kEmptyStr, 202 const char* const env[] = 0, 203 size_t pipe_size = 0/*use default*/); 204 205 /// Open the standard streams of the current process. 206 /// 207 /// The standard input stream is opened as if it's the output stream of a 208 /// child process, so it can be read from. Similarly, the standard output 209 /// stream is opened as if it's a child input stream, so it can be written. 210 /// The standard error stream is left untouched. 211 /// Throw CPipeExcepionon on errors. 212 /// 213 /// @sa 214 /// Read, Write, Close 215 void OpenSelf(void); 216 217 /// Close pipe. 218 /// 219 /// Sever communication channel with the spawned child process, and then 220 /// wait for the process to terminate. 221 /// 222 /// @note 223 /// A CPipe opened with OpenSelf() always closes with eIO_Success, and 224 /// *exitcode returns as 0 (yet the current process continues to run). 225 /// 226 /// @param exitcode 227 /// Pointer to store the exit code at, if the child process terminated 228 /// successfully, or -1 in case of an error. Can be passed as NULL. 229 /// @return 230 /// Completion status. 231 /// The returned status eIO_Timeout means that child process is still 232 /// running (yet the communication with it has been severed). Any other 233 /// return status means that the pipe is not suitable for further I/O 234 /// until reopened. 235 /// 236 /// eIO_Closed - pipe was already closed; 237 /// eIO_Timeout - the eIO_Close timeout expired, child process is still 238 /// running (return only if fKeepOnClose was set); 239 /// eIO_Success - pipe was successfully closed. The running status of 240 /// the child process depends on the flags: 241 /// fKeepOnClose - process has terminated with "exitcode"; 242 /// fCloseOnClose - process has self-terminated if "exitcode" != -1, 243 /// or is still running otherwise; 244 /// fKillOnClose - process has self-terminated if "exitcode" != -1, 245 /// or has been forcibly terminated otherwise; 246 /// Otherwise - an error was detected. 247 /// @sa 248 /// Open, OpenSelf, fKeepOnClose, fCloseOnClose, fKillOnClose, fNewGroup 249 EIO_Status Close(int* exitcode = 0); 250 251 /// Close specified pipe handle (even for CPipe opened with OpenSelf()). 252 /// 253 /// @param handle 254 /// Pipe handle to close 255 /// @return 256 /// Completion status. 257 /// @sa 258 /// Close, OpenSelf 259 EIO_Status CloseHandle(EChildIOHandle handle); 260 261 /// Set standard output handle to read data from. 262 /// 263 /// @param from_handle 264 /// Handle which used to read data (eStdOut/eStdErr). 265 /// @return 266 /// Return eIO_Success if new handler is eStdOut or eStdErr. 267 /// Return eIO_InvalidArg otherwise. 268 /// @sa 269 /// Read 270 EIO_Status SetReadHandle(EChildIOHandle from_handle); 271 272 /// Get standard output handle to read data from. 273 /// 274 /// @return 275 /// Return either eStdOut(default) or eStdErr 276 /// @sa 277 /// SetReadHandle GetReadHandle(void) const278 EChildIOHandle GetReadHandle(void) const { return m_ReadHandle; } 279 280 /// Read data from pipe. 281 /// 282 /// @param buf 283 /// Buffer into which data is read. 284 /// @param count 285 /// Number of bytes to read. 286 /// @param read 287 /// Number of bytes actually read, which may be less than "count". 288 /// @param from_handle 289 /// Handle to read data from. 290 /// @return 291 /// Always return eIO_Success if some data were read (regardless of pipe 292 /// conditions that may include EOF/error). 293 /// Return other (error) status only if no data at all could be obtained. 294 /// @sa 295 /// Write, SetTimeout 296 EIO_Status Read(void* buf, 297 size_t count, 298 size_t* read = 0, 299 EChildIOHandle from_handle = eDefault); 300 301 /// Write data to pipe. 302 /// 303 /// @param buf 304 /// Buffer from which data is written. 305 /// @param count 306 /// Number of bytes to write. 307 /// @param written 308 /// Number of bytes actually written, which may be less than "count". 309 /// @return 310 /// Return eIO_Success if some data were written. 311 /// Return other (error) code only if no data at all could be written. 312 /// @sa 313 /// Read, SetTimeout 314 EIO_Status Write(const void* buf, 315 size_t count, 316 size_t* written = 0); 317 318 /// Wait for I/O event(s). 319 /// 320 /// Block until at least one of the I/O handles enlisted in poll mask 321 /// becomes available for I/O, or until timeout expires. 322 /// Throw CPipeException on failure to create the pipe. 323 /// NOTE: MS Windows doesn't have mechanism to get status of 'write end' 324 /// of the pipe, so only fStdOut/fStdErr/fDefault can be used for polling 325 /// child stdin/stderr handles. If fStdIn flag is set in the 'mask', 326 /// that it will be copied to resulting mask also. 327 /// 328 /// @param mask 329 /// Mask of I/O handles to poll. 330 /// @param timeout 331 /// Timeout value to set. 332 /// If "timeout" is NULL then set the timeout to be infinite. 333 /// @return 334 /// Mask of I/O handles that ready for I/O. 335 /// Return zero on timeout or if all I/O handles are closed. 336 /// If fDefault is polled and the corresponding Err/Out is ready 337 /// then return fDefault, and not the "real" fStdOut/fStdErr. 338 TChildPollMask Poll(TChildPollMask mask, const STimeout* timeout = 0); 339 340 /// Return a status of the last I/O operation. 341 /// 342 /// @param direction 343 /// Direction to get status for. 344 /// @return 345 /// I/O status for the specified direction. 346 /// eIO_Closed - if the pipe is closed; 347 /// eIO_Unknown - if an error was detected during the last I/O; 348 /// eIO_InvalidArg - if "direction" is not one of: eIO_Read, eIO_Write; 349 /// eIO_Timeout - if the timeout was on last I/O; 350 /// eIO_Success - otherwise. 351 /// @sa 352 /// Read, Write 353 EIO_Status Status(EIO_Event direction) const; 354 355 /// Specify timeout for the pipe I/O. 356 /// 357 /// @param event 358 /// I/O event for which the timeout is set. 359 /// @param timeout 360 /// Timeout value to set. 361 /// If "timeout" is NULL then set the timeout to be infinite. 362 /// - By default, initially all timeouts are infinite; 363 /// - kDefaultTimeout has no effect. 364 /// @return 365 /// Completion status. 366 /// @sa 367 /// Read, Write, Close, GetTimeout 368 EIO_Status SetTimeout(EIO_Event event, const STimeout* timeout); 369 370 /// Get pipe I/O timeout. 371 /// 372 /// @param event 373 /// I/O event for which timeout is obtained. 374 /// @return 375 // Timeout for specified event (or NULL, if the timeout is infinite). 376 /// The returned timeout is guaranteed to be pointing to a valid 377 /// (and correct) structure in memory at least until the pipe is 378 /// closed or SetTimeout() is called for this pipe. 379 /// @sa 380 /// SetTimeout 381 const STimeout* GetTimeout(EIO_Event event) const; 382 383 /// Get the process handle for the piped child. 384 /// 385 /// @return 386 /// Returned value greater than 0 is a child process handle. 387 /// Return 0 if child process is not running. 388 /// @sa 389 /// Open, Close, CProcess class 390 TProcessHandle GetProcessHandle(void) const; 391 392 393 /// Callback interface for ExecWait() 394 /// 395 /// @sa ExecWait 396 class NCBI_XCONNECT_EXPORT IProcessWatcher 397 { 398 public: 399 /// An action which the ExecWait() method should take 400 /// after the Watch() method has returned. 401 enum EAction { 402 eContinue, ///< Continue running 403 eStop, ///< Kill the child process and exit 404 eExit ///< Exit without waiting for the child process 405 }; 406 virtual ~IProcessWatcher(); 407 408 /// This method is called when the process has just 409 /// been started by the ExecWait() method. 410 /// 411 /// @param pid 412 /// Process Id to monitor 413 /// @return 414 /// eStop if the process should be killed, eContinue otherwise OnStart(TProcessHandle)415 virtual EAction OnStart(TProcessHandle /*pid*/) { return eContinue; } 416 417 /// This method is getting called periodically during 418 /// the process execution by the ExecWait() method. 419 /// 420 /// @param pid 421 /// Process Id to monitor 422 /// @return 423 /// eStop if the process should be killed, eContinue otherwise 424 virtual EAction Watch(TProcessHandle /*pid*/) = 0; 425 }; 426 427 /// ExecWait return code 428 enum EFinish { 429 eDone, ///< Process finished normally 430 eCanceled ///< Watcher requested process termination 431 }; 432 433 /// Execute a command with a vector of arguments and wait for its 434 /// completion. 435 /// 436 /// @param cmd 437 /// Command name to execute. 438 /// Beware if both the command contains a relative path and 'current_dir' 439 /// parameter is specified. In this case, the current directory must 440 /// be considered undefined, as it can either still be the same as in the 441 /// parent process or already be changed to 'current_dir'. So using of 442 /// an absolute path is always recommended in such cases. 443 /// @param args 444 /// Vector of string arguments for the command (argv[0] excluded). 445 /// @param in 446 /// Stream this data which will be sent to the child process's stdin 447 /// @param out 448 /// Stream where the child process's stdout will be written to 449 /// @param err 450 /// Stream where the child process's stderr will be written to 451 /// @param exit_code 452 /// The child process's exit_code 453 /// @param current_dir 454 /// Current working directory for the new process. 455 /// The string must be an absolute path. On MS Windows it should 456 /// also contain a drive letter. If this parameter is empty, the new 457 /// process will have the same current directory as the calling process. 458 /// @param env 459 /// An optional pointer to a vector with environment variables to use 460 /// in the child process. If not speficied, a copy of the parent's 461 /// process environment is used. Note that a new environment completely 462 /// replaces the default environment otherwise inherited from the parent 463 /// process, and so it does not just add / modify some values. 464 /// @param watcher 465 /// Call back object to monitor the child process execution status 466 /// @param kill_timeout 467 /// Wait time between first "soft" and second "hard" attempts to 468 /// terminate the process. 469 /// @note that on UNIX in case of a zero or a very small timeout 470 /// the killed process may not be released immediately and continue to 471 /// persist as a zombie process even after this call completes. 472 /// @return 473 /// eDone if process has finished normally, or eCanceled if a watcher 474 /// decided to stop it. 475 /// 476 /// @sa IProcessWatcher, Open 477 static EFinish ExecWait(const string& cmd, 478 const vector<string>& args, 479 CNcbiIstream& in, 480 CNcbiOstream& out, 481 CNcbiOstream& err, 482 int& exit_code, 483 const string& current_dir = kEmptyStr, 484 const char* const env[] = 0, 485 IProcessWatcher* watcher = 0, 486 const STimeout* kill_timeout = 0, 487 size_t pipe_size = 0/*default*/); 488 489 protected: 490 size_t m_PipeSize; ///< Pipe size 491 492 CPipeHandle* m_PipeHandle; ///< Internal OS-specific pipe handle 493 EChildIOHandle m_ReadHandle; ///< Default handle used for read 494 495 // Last I/O status 496 EIO_Status m_ReadStatus; ///< Last read status 497 EIO_Status m_WriteStatus; ///< Last write status 498 499 // Timeouts 500 const STimeout* m_ReadTimeout; ///< eIO_Read timeout 501 const STimeout* m_WriteTimeout; ///< eIO_Write timeout 502 const STimeout* m_CloseTimeout; ///< eIO_Close timeout 503 STimeout m_ReadTimeoutValue; ///< Storage for m_ReadTimeout 504 STimeout m_WriteTimeoutValue; ///< Storage for m_WriteTimeout 505 STimeout m_CloseTimeoutValue; ///< Storage for m_CloseTimeout 506 507 private: 508 // Disable copy constructor and assignment 509 CPipe(const CPipe&); 510 CPipe& operator= (const CPipe&); 511 }; 512 513 514 515 ///////////////////////////////////////////////////////////////////////////// 516 /// CPipeException -- 517 /// 518 /// Define exceptions generated by CPipe. 519 /// 520 /// CPipeException inherits its basic functionality from CCoreException 521 /// and defines additional error codes for CPipe. 522 523 class NCBI_XCONNECT_EXPORT CPipeException 524 : EXCEPTION_VIRTUAL_BASE public CCoreException 525 { 526 public: 527 /// Error types for pipe exceptions. 528 enum EErrCode { 529 eOpen ///< Unable to open pipe 530 }; 531 /// Translate from an error code value to its string representation. 532 virtual const char* GetErrCodeString(void) const override; 533 // Standard exception boiler plate code. 534 NCBI_EXCEPTION_DEFAULT(CPipeException, CCoreException); 535 }; 536 537 538 END_NCBI_SCOPE 539 540 541 /* @} */ 542 543 #endif /* CONNECT__NCBI_PIPE__HPP */ 544