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