NCBI C++ ToolKit
ncbi_pipe.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_pipe.cpp 102932 2024-08-07 15:26:37Z ivanov $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Anton Lavrentiev, Mike DiCuccio, Vladimir Ivanov
27  *
28  * File Description:
29  * Inter-process pipe with a spawned process.
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 /* Cancel __wur (warn unused result) ill effects in GCC */
35 #ifdef _FORTIFY_SOURCE
36 # undef _FORTIFY_SOURCE
37 #endif /*_FORTIFY_SOURCE*/
38 #define _FORTIFY_SOURCE 0
39 #include "ncbi_priv.h"
40 #include <corelib/ncbiexec.hpp>
41 #include <corelib/ncbi_param.hpp>
42 #include <corelib/ncbi_system.hpp>
43 #include <corelib/stream_utils.hpp>
45 #include <connect/error_codes.hpp>
46 #include <connect/ncbi_pipe.hpp>
47 
48 #ifdef NCBI_OS_MSWIN
49 
50 # include <windows.h>
51 
52 #elif defined NCBI_OS_UNIX
53 
54 # include <errno.h>
55 # include <fcntl.h>
56 # include <poll.h>
57 # include <signal.h>
58 # include <unistd.h>
59 # include <sys/time.h>
60 # include <sys/types.h>
61 # include <sys/wait.h>
62 
63 #else
64 # error "The CPipe class is supported only on Windows and Unix"
65 #endif
66 
67 #define NCBI_USE_ERRCODE_X Connect_Pipe
68 
69 
70 #define IS_SET(flags, mask) (((flags) & (mask)) == (mask))
71 
72 
73 #define PIPE_THROW(err, errtxt) \
74  THROW0_TRACE(x_FormatError(int(err), errtxt))
75 
76 
78 
79 
80 //////////////////////////////////////////////////////////////////////////////
81 //
82 // Auxiliary functions
83 //
84 
85 static const STimeout* s_SetTimeout(const STimeout* from, STimeout* to)
86 {
87  if (!from) {
88  return kInfiniteTimeout;
89  }
90  to->sec = from->usec / kMicroSecondsPerSecond+ from->sec;
91  to->usec = from->usec % kMicroSecondsPerSecond;
92  return to;
93 }
94 
95 
97 
98 
99 static string x_FormatError(int error, const string& message)
100 {
101  const char* errstr;
102 
103 #ifdef NCBI_OS_MSWIN
104  string errmsg;
105  if (error) {
106  TCHAR* tmpstr = NULL;
107  DWORD rv = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
108  FORMAT_MESSAGE_FROM_SYSTEM |
109  FORMAT_MESSAGE_MAX_WIDTH_MASK |
110  FORMAT_MESSAGE_IGNORE_INSERTS,
111  NULL, (DWORD) error,
112  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
113  (LPTSTR) &tmpstr, 0, NULL);
114  if (rv && tmpstr) {
115  errmsg = _T_CSTRING(tmpstr);
116  errstr = errmsg.c_str();
117  } else {
118  errstr = "";
119  }
120  if (tmpstr) {
121  ::LocalFree((HLOCAL) tmpstr);
122  }
123  } else
124 #endif // NCBI_OS_MSWIN
125  errstr = 0;
126 
127  int dynamic = 0/*false*/;
128  const char* result = ::NcbiMessagePlusError(&dynamic, message.c_str(),
129  error, errstr);
130  TTempCharPtr retval(const_cast<char*> (result),
131  dynamic ? eTakeOwnership : eNoOwnership);
132  return retval.get() ? retval.get() : message;
133 }
134 
135 
136 static string s_FormatErrorMessage(const string& where, const string& what)
137 {
138  return "[CPipe::" + where + "] " + what;
139 }
140 
141 
143  const STimeout* timeout, int* exitcode)
144 {
145  CProcess::CExitInfo exitinfo;
146  int x_exitcode = process.Wait(NcbiTimeoutToMs(timeout), &exitinfo);
147 
148  EIO_Status status;
149  if (x_exitcode < 0) {
150  if (!exitinfo.IsPresent()) {
151  status = eIO_Unknown;
152  } else if (!exitinfo.IsAlive()) {
153  status = eIO_Unknown;
154 #ifdef NCBI_OS_UNIX
155  if (exitinfo.IsSignaled()) {
156  x_exitcode = -(exitinfo.GetSignal() + 1000);
157  }
158 #endif // NCBI_OS_UNIX
159  } else {
160  status = eIO_Timeout;
163  unsigned long x_timeout;
164  if (!timeout || (timeout->sec | timeout->usec)) {
165  x_timeout = CProcess::kDefaultKillTimeout;
166  } else {
167  x_timeout = 0/*fast but unsafe*/;
168  }
169  bool killed;
170  if (IS_SET(flags, CPipe::fNewGroup)) {
171  killed = process.KillGroup(x_timeout);
172  } else {
173  killed = process.Kill (x_timeout);
174  }
175  status = killed ? eIO_Success : eIO_Unknown;
176  } else {
177  status = eIO_Success;
178  }
179  }
180  }
181  } else {
182  _ASSERT(exitinfo.IsPresent());
183  _ASSERT(exitinfo.IsExited());
184  _ASSERT(exitinfo.GetExitCode() == x_exitcode);
185  status = eIO_Success;
186  }
187 
188  if (exitcode) {
189  *exitcode = x_exitcode;
190  }
191  return status;
192 }
193 
194 
196 {
197  switch (handle) {
198  case CPipe::eStdIn:
199  return "eStdIn";
200  case CPipe::eStdOut:
201  return "eStdOut";
202  case CPipe::eStdErr:
203  return "eStdErr";
204  default:
205  _TROUBLE;
206  break;
207  }
208  return "<Invalid handle " + NStr::NumericToString(int(handle)) + '>';
209 }
210 
211 
212 static string x_CommandLine(const string& cmd,
213  const vector<string>& args)
214 {
215  // Enclose command elements in quotes if necessary.
216  string cmd_line(CExec::QuoteArg(cmd));
217  for (auto&& arg : args) {
218  cmd_line += ' ';
219  cmd_line += CExec::QuoteArg(arg);
220  }
221  return cmd_line;
222 }
223 
224 
225 
226 //////////////////////////////////////////////////////////////////////////////
227 //
228 // Class CPipeHandle handles forwarded requests from CPipe.
229 // This class is reimplemented in a platform-specific fashion where needed.
230 //
231 
232 #if defined(NCBI_OS_MSWIN)
233 
234 
235 const unsigned long kWaitPrecision = 100; // Timeout time slice (milliseconds)
236 
237 
238 static inline bool x_IsDisconnectError(DWORD error)
239 {
240  return (error == ERROR_NO_DATA ||
241  error == ERROR_BROKEN_PIPE ||
242  error == ERROR_PIPE_NOT_CONNECTED ? true : false);
243 }
244 
245 
246 //////////////////////////////////////////////////////////////////////////////
247 //
248 // CPipeHandle -- MS Windows version
249 //
250 
252 {
253 public:
254  CPipeHandle(void);
255  ~CPipeHandle();
256  EIO_Status Open(const string& cmd, const vector<string>& args,
257  CPipe::TCreateFlags create_flags,
258  const string& current_dir,
259  const char* const envp[],
260  size_t pipe_size);
261  void OpenSelf(void);
262  EIO_Status Close(int* exitcode, const STimeout* timeout);
264  EIO_Status Read(void* buf, size_t count, size_t* n_read,
265  const CPipe::EChildIOHandle from_handle,
266  const STimeout* timeout) const;
267  EIO_Status Write(const void* data, size_t count, size_t* written,
268  const STimeout* timeout) const;
270  const STimeout* timeout) const;
272  void Release(void) { x_Clear(); }
273 
274 private:
275  // Clear object state.
276  void x_Clear(void);
277  // Get child's I/O handle.
278  HANDLE x_GetHandle(CPipe::EChildIOHandle from_handle) const;
279  // Trigger blocking mode on specified I/O handle.
280  void x_SetNonBlockingMode(HANDLE fd) const;
281  // Wait on the file descriptors for I/O.
283  const STimeout* timeout) const;
284 private:
285  // I/O handles for child process.
289 
290  // Child process descriptor.
292 
293  // Pipe flags
295 
296  // Flag that indicates whether the m_ChildStd* and m_ProcHandle
297  // member variables contain the relevant handles of the
298  // current process, in which case they won't be closed.
300 };
301 
302 
304  : m_ChildStdIn (INVALID_HANDLE_VALUE),
305  m_ChildStdOut(INVALID_HANDLE_VALUE),
306  m_ChildStdErr(INVALID_HANDLE_VALUE),
307  m_ProcHandle (INVALID_HANDLE_VALUE),
308  m_Flags(0), m_SelfHandles(false)
309 {
310  return;
311 }
312 
313 
315 {
316  static const STimeout kZeroTimeout = {0, 0};
317  Close(0, &kZeroTimeout);
318  x_Clear();
319 }
320 
321 
323  const vector<string>& args,
324  CPipe::TCreateFlags create_flags,
325  const string& current_dir,
326  const char* const envp[],
327  size_t pipe_size)
328 {
329  DEFINE_STATIC_FAST_MUTEX(s_Mutex);
330  CFastMutexGuard guard_mutex(s_Mutex);
331 
333  ERR_POST_X(1, s_FormatErrorMessage("Open", "Pipe busy"));
334  return eIO_Unknown;
335  }
336  m_Flags = create_flags;
337 
338  HANDLE child_stdin = INVALID_HANDLE_VALUE;
339  HANDLE child_stdout = INVALID_HANDLE_VALUE;
340  HANDLE child_stderr = INVALID_HANDLE_VALUE;
341 
342  EIO_Status status = eIO_Unknown;
343 
344  try {
345  // Prepare command line to run
346  string cmd_line = x_CommandLine(cmd, args);
347  string x_cmd_line;
348 
349  if (cmd.find_first_of(":\\/") == NPOS) {
352  ? &app->SetEnvironment()
353  : new CNcbiEnvironment,
354  app
355  ? eNoOwnership
356  : eTakeOwnership);
357  const string& comspec = env->Get("COMSPEC");
358  if (!comspec.empty() && NStr::CompareNocase(comspec, cmd) != 0) {
359  x_cmd_line = CExec::QuoteArg(comspec) + " /C " + cmd_line;
360  }
361  }
362  if (x_cmd_line.empty()) {
363  x_cmd_line = cmd_line;
364  }
365 
366  // Convert environment array to block form
368  if (envp) {
369  // Count block size:
370  // it should have one zero char at least
371  size_t size = 1;
372  int count = 0;
373  while (envp[count]) {
374  size += strlen(envp[count++]) + 1/*'\0'*/;
375  }
376  // Allocate memory
377  TXChar* block = new TXChar[size];
378  env_block.reset(block);
379 
380  // Copy environment strings
381  for (int i = 0; i < count; ++i) {
382 #if defined(NCBI_OS_MSWIN) && defined(_UNICODE)
383  TXString tmp = _T_XSTRING(envp[i]);
384  size_t n = tmp.size();
385  memcpy(block, tmp.data(), n * sizeof(TXChar));
386  block[n++] = _TX('\0');
387 #else
388  size_t n = strlen(envp[i]) + 1;
389  memcpy(block, envp[i], n);
390 #endif // NCBI_OS_MSWIN && _UNICODE
391  block += n;
392  }
393  *block = _TX('\0');
394  }
395 
396  HANDLE stdout_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
397  if (stdout_handle == NULL) {
398  stdout_handle = INVALID_HANDLE_VALUE;
399  }
400  HANDLE stderr_handle = ::GetStdHandle(STD_ERROR_HANDLE);
401  if (stderr_handle == NULL) {
402  stderr_handle = INVALID_HANDLE_VALUE;
403  }
404 
405  // Flush stdio buffers before remap
406  NcbiCout.flush();
407  NcbiCerr.flush();
408  ::fflush(NULL);
409  if (stdout_handle != INVALID_HANDLE_VALUE) {
410  ::FlushFileBuffers(stdout_handle);
411  }
412  if (stderr_handle != INVALID_HANDLE_VALUE) {
413  ::FlushFileBuffers(stderr_handle);
414  }
415 
416  // Set base security attributes
417  SECURITY_ATTRIBUTES attr;
418  attr.nLength = sizeof(attr);
419  attr.bInheritHandle = TRUE;
420  attr.lpSecurityDescriptor = NULL;
421 
422  // Create pipe for child's stdin
424  if (!IS_SET(create_flags, CPipe::fStdIn_Close)) {
425  if (!::CreatePipe(&child_stdin, &m_ChildStdIn,
426  &attr, (DWORD) pipe_size)) {
427  PIPE_THROW(::GetLastError(),
428  "Failed CreatePipe(stdin)");
429  }
430  ::SetHandleInformation(m_ChildStdIn, HANDLE_FLAG_INHERIT, 0);
432  }
433 
434  // Create pipe for child's stdout
436  if (!IS_SET(create_flags, CPipe::fStdOut_Close)) {
437  if (!::CreatePipe(&m_ChildStdOut, &child_stdout, &attr, 0)) {
438  PIPE_THROW(::GetLastError(),
439  "Failed CreatePipe(stdout)");
440  }
441  ::SetHandleInformation(m_ChildStdOut, HANDLE_FLAG_INHERIT, 0);
443  }
444 
445  // Create pipe for child's stderr
447  if (IS_SET(create_flags, CPipe::fStdErr_Open)) {
448  if (!::CreatePipe(&m_ChildStdErr, &child_stderr, &attr, 0)) {
449  PIPE_THROW(::GetLastError(),
450  "Failed CreatePipe(stderr)");
451  }
452  ::SetHandleInformation(m_ChildStdErr, HANDLE_FLAG_INHERIT, 0);
454  } else if (IS_SET(create_flags, CPipe::fStdErr_Share)) {
455  if (stderr_handle != INVALID_HANDLE_VALUE) {
456  HANDLE current_process = ::GetCurrentProcess();
457  if (!::DuplicateHandle(current_process, stderr_handle,
458  current_process, &child_stderr,
459  0, TRUE, DUPLICATE_SAME_ACCESS)) {
460  PIPE_THROW(::GetLastError(),
461  "Failed DuplicateHandle(stderr)");
462  }
463  }
464  } else if (IS_SET(create_flags, CPipe::fStdErr_StdOut)) {
465  child_stderr = child_stdout;
466  }
467 
468  // Create child process
469  STARTUPINFO sinfo;
470  PROCESS_INFORMATION pinfo;
471  ::ZeroMemory(&pinfo, sizeof(pinfo));
472  ::ZeroMemory(&sinfo, sizeof(sinfo));
473  sinfo.cb = sizeof(sinfo);
474  sinfo.hStdError = child_stderr;
475  sinfo.hStdOutput = child_stdout;
476  sinfo.hStdInput = child_stdin;
477  sinfo.dwFlags = STARTF_USESTDHANDLES;
478 # if defined(_UNICODE)
479  DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
480 # else
481  DWORD dwCreationFlags = 0;
482 # endif // _UNICODE
483  if (create_flags & CPipe::fNewGroup)
484  dwCreationFlags |= CREATE_NEW_PROCESS_GROUP;
485  if (!::CreateProcess(NULL,
486  (LPTSTR)(_T_XCSTRING(x_cmd_line)),
487  NULL, NULL, TRUE,
488  dwCreationFlags, env_block.get(),
489  current_dir.empty()
490  ? 0 : _T_XCSTRING(current_dir),
491  &sinfo, &pinfo)) {
492  status = eIO_Closed;
493  PIPE_THROW(::GetLastError(),
494  "Failed CreateProcess('" + cmd_line + "')");
495  }
496  ::CloseHandle(pinfo.hThread);
497  m_ProcHandle = pinfo.hProcess;
498 
500 
501  status = eIO_Success;
502  }
503  catch (string& what) {
504  static const STimeout kZeroZimeout = {0, 0};
505  Close(0, &kZeroZimeout);
506  ERR_POST_X(1, s_FormatErrorMessage("Open", what));
507  x_Clear();
508  }
509  if (child_stdin != INVALID_HANDLE_VALUE) {
510  ::CloseHandle(child_stdin);
511  }
512  if (child_stdout != INVALID_HANDLE_VALUE) {
513  ::CloseHandle(child_stdout);
514  }
515  if (child_stderr != INVALID_HANDLE_VALUE
516  && child_stderr != child_stdout) {
517  ::CloseHandle(child_stderr);
518  }
519 
520  return status;
521 }
522 
523 
525 {
527  PIPE_THROW(0,
528  "Pipe busy");
529  }
530 
531  NcbiCout.flush();
532  ::fflush(stdout);
534  && !::FlushFileBuffers(m_ChildStdIn)) {
535  PIPE_THROW(::GetLastError(),
536  "Failed FlushFileBuffers(stdout)");
537  }
538  if ((m_ChildStdIn = ::GetStdHandle(STD_OUTPUT_HANDLE))
540  PIPE_THROW(::GetLastError(),
541  "Failed GetStdHandle(stdout)");
542  }
543  if ((m_ChildStdOut = ::GetStdHandle(STD_INPUT_HANDLE))
545  PIPE_THROW(::GetLastError(),
546  "Failed GetStdHandle(stdin)");
547  }
548  // NB: GetCurrentProcess() returns HANDLE(-1) which is INVALID_HANDLE_VALUE
549  m_ProcHandle = GetCurrentProcess();
550 
551  m_SelfHandles = true;
552 }
553 
554 
556 {
558  if (m_SelfHandles) {
561  m_SelfHandles = false;
562  } else {
566  }
567 }
568 
569 
570 EIO_Status CPipeHandle::Close(int* exitcode, const STimeout* timeout)
571 {
572  EIO_Status status;
573 
574  if (!m_SelfHandles) {
578 
580  if (exitcode) {
581  *exitcode = -1;
582  }
583  status = eIO_Closed;
584  } else {
586  status = s_Close(process, m_Flags, timeout, exitcode);
587  }
588  } else {
589  if (exitcode) {
590  *exitcode = 0;
591  }
592  status = eIO_Success;
593  }
594 
595  if (status != eIO_Timeout) {
596  x_Clear();
597  }
598  return status;
599 }
600 
601 
603 {
604  switch (handle) {
605  case CPipe::eStdIn:
607  return eIO_Closed;
608  }
611  break;
612  case CPipe::eStdOut:
614  return eIO_Closed;
615  }
618  break;
619  case CPipe::eStdErr:
621  return eIO_Closed;
622  }
625  break;
626  default:
627  _TROUBLE;
628  return eIO_InvalidArg;
629  }
630  return eIO_Success;
631 }
632 
633 
634 EIO_Status CPipeHandle::Read(void* buf, size_t count, size_t* n_read,
635  const CPipe::EChildIOHandle from_handle,
636  const STimeout* timeout) const
637 {
638  _ASSERT(!n_read || !*n_read);
639  _ASSERT(!(from_handle & (from_handle - 1)));
640 
641  EIO_Status status = eIO_Unknown;
642 
643  try {
645  PIPE_THROW(0,
646  "Pipe closed");
647  }
648  HANDLE fd = x_GetHandle(from_handle);
649  if (fd == INVALID_HANDLE_VALUE) {
650  PIPE_THROW(0,
651  "Pipe I/O handle "
652  + x_GetHandleName(from_handle) + " closed");
653  }
654  if (!count) {
655  return eIO_Success;
656  }
657 
658  DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
659  DWORD bytes_avail = 0;
660 
661  // Wait for data from the pipe with a timeout.
662  // Using a loop and periodically try PeekNamedPipe is inefficient,
663  // but Windows doesn't have asynchronous mechanism to read from a pipe.
664  // NOTE: WaitForSingleObject() doesn't work with anonymous pipes.
665  // See CPipe::Poll() for more details.
666 
667  unsigned long x_sleep = 1;
668  for (;;) {
669  BOOL ok = ::PeekNamedPipe(fd, NULL, 0, NULL, &bytes_avail, NULL);
670  if (bytes_avail) {
671  break;
672  }
673  if (!ok) {
674  // Has peer closed the connection?
675  DWORD error = ::GetLastError();
676  if (!x_IsDisconnectError(error)) {
677  // NB: status == eIO_Unknown
679  "Failed PeekNamedPipe("
680  + x_GetHandleName(from_handle) + ')');
681  }
682  return eIO_Closed;
683  }
684 
685  if (!x_timeout) {
686  return eIO_Timeout;
687  }
688  if (x_timeout != INFINITE) {
689  if (x_sleep > x_timeout) {
690  x_sleep = x_timeout;
691  }
692  x_timeout -= x_sleep;
693  }
694  SleepMilliSec(x_sleep);
695  x_sleep <<= 1;
696  if (x_sleep > kWaitPrecision) {
697  x_sleep = kWaitPrecision;
698  }
699  }
700  _ASSERT(bytes_avail);
701 
702  // We must read only "count" bytes of data regardless of
703  // the amount available to read
704  if (bytes_avail > count) {
705  bytes_avail = (DWORD) count;
706  }
707  BOOL ok = ::ReadFile(fd, buf, bytes_avail, &bytes_avail, NULL);
708  if (!bytes_avail) {
709  // NB: status == eIO_Unknown
710  PIPE_THROW(!ok ? ::GetLastError() : 0,
711  "Failed to read data from pipe I/O handle "
712  + x_GetHandleName(from_handle));
713  } else {
714  if (n_read) {
715  *n_read = (size_t) bytes_avail;
716  }
717  status = eIO_Success;
718  }
719  }
720  catch (string& what) {
721  ERR_POST_X(2, s_FormatErrorMessage("Read", what));
722  }
723 
724  return status;
725 }
726 
727 
729  size_t* n_written, const STimeout* timeout) const
730 
731 {
732  _ASSERT(!n_written || !*n_written);
733 
734  EIO_Status status = eIO_Unknown;
735 
736  try {
738  PIPE_THROW(0,
739  "Pipe closed");
740  }
742  status = eIO_Closed;
743  PIPE_THROW(0,
744  "Pipe I/O handle "
745  + x_GetHandleName(CPipe::eStdIn) + " closed");
746  }
747  if (!count) {
748  return eIO_Success;
749  }
750 
751  DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
752  DWORD to_write = (count > numeric_limits<DWORD>::max()
754  : (DWORD) count);
755  DWORD bytes_written = 0;
756 
757  unsigned long x_sleep = 1;
758  for (;;) {
759  BOOL ok = ::WriteFile(m_ChildStdIn, (char*) data, to_write,
760  &bytes_written, NULL);
761  if (bytes_written) {
762  break;
763  }
764  if (!ok) {
765  DWORD error = ::GetLastError();
766  if (x_IsDisconnectError(error)) {
767  status = eIO_Closed;
768  } // NB: status == eIO_Unknown
770  "Failed to write data to pipe I/O handle "
772  }
773 
774  if (!x_timeout) {
775  return eIO_Timeout;
776  }
777  if (x_timeout != INFINITE) {
778  if (x_sleep > x_timeout) {
779  x_sleep = x_timeout;
780  }
781  x_timeout -= x_sleep;
782  }
783  SleepMilliSec(x_sleep);
784  x_sleep <<= 1;
785  if (x_sleep > kWaitPrecision) {
786  x_sleep = kWaitPrecision;
787  }
788  }
789  _ASSERT(bytes_written);
790 
791  if (n_written) {
792  *n_written = bytes_written;
793  }
794  status = eIO_Success;
795  }
796  catch (string& what) {
797  ERR_POST_X(3, s_FormatErrorMessage("Write", what));
798  }
799 
800  return status;
801 }
802 
803 
805  const STimeout* timeout) const
806 {
808 
809  try {
811  PIPE_THROW(0,
812  "Pipe closed");
813  }
817  PIPE_THROW(0,
818  "All pipe I/O handles closed");
819  }
820  poll = x_Poll(mask, timeout);
821  }
822  catch (string& what) {
823  ERR_POST_X(4, s_FormatErrorMessage("Poll", what));
824  }
825 
826  return poll;
827 }
828 
829 
831 {
832  switch (from_handle) {
833  case CPipe::eStdIn:
834  return m_ChildStdIn;
835  case CPipe::eStdOut:
836  return m_ChildStdOut;
837  case CPipe::eStdErr:
838  return m_ChildStdErr;
839  default:
840  _TROUBLE;
841  break;
842  }
843  return INVALID_HANDLE_VALUE;
844 }
845 
846 
848 {
849  // NB: Pipe is in byte-mode.
850  // NOTE: We cannot get a state of a pipe handle opened for writing.
851  // We cannot set a state of a pipe handle opened for reading.
852  DWORD state = PIPE_READMODE_BYTE | PIPE_NOWAIT;
853  if (!::SetNamedPipeHandleState(fd, &state, NULL, NULL)) {
854  PIPE_THROW(::GetLastError(),
855  "Failed to set pipe I/O handle non-blocking");
856  }
857 }
858 
859 
861  const STimeout* timeout) const
862 {
863  DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
864 
865  // We cannot poll child's stdin, so just copy corresponding flag
867 
868  // Wait for data from the pipe with timeout.
869  // Using a loop and periodically try PeekNamedPipe is inefficient,
870  // but Windows doesn't have asynchronous mechanism to read from a pipe.
871  // NOTE: WaitForSingleObject() doesn't work with anonymous pipes.
872 
873  unsigned long x_sleep = 1;
874  for (;;) {
875  if ((mask & CPipe::fStdOut)
877  DWORD bytes_avail = 0;
878  if (!::PeekNamedPipe(m_ChildStdOut, NULL, 0, NULL,
879  &bytes_avail, NULL)) {
880  DWORD error = ::GetLastError();
881  // Has peer closed connection?
882  if (!x_IsDisconnectError(error)) {
884  "Failed PeekNamedPipe(stdout)");
885  }
886  poll |= CPipe::fStdOut;
887  } else if (bytes_avail) {
888  poll |= CPipe::fStdOut;
889  }
890  }
891  if ((mask & CPipe::fStdErr)
893  DWORD bytes_avail = 0;
894  if (!::PeekNamedPipe(m_ChildStdErr, NULL, 0, NULL,
895  &bytes_avail, NULL)) {
896  DWORD error = ::GetLastError();
897  // Has peer closed connection?
898  if (!x_IsDisconnectError(error)) {
900  "Failed PeekNamedPipe(stderr)");
901  }
902  poll |= CPipe::fStdErr;
903  } else if (bytes_avail) {
904  poll |= CPipe::fStdErr;
905  }
906  }
907  if (poll) {
908  break;
909  }
910 
911  if (!x_timeout) {
912  break;
913  }
914  if (x_timeout != INFINITE) {
915  if (x_sleep > x_timeout) {
916  x_sleep = x_timeout;
917  }
918  x_timeout -= x_sleep;
919  }
920  SleepMilliSec(x_sleep);
921  x_sleep <<= 1;
922  if (x_sleep > kWaitPrecision) {
923  x_sleep = kWaitPrecision;
924  }
925  }
926 
927  _ASSERT(!(poll ^ (poll & mask)));
928  return poll;
929 }
930 
931 
932 #elif defined(NCBI_OS_UNIX)
933 
934 
935 //////////////////////////////////////////////////////////////////////////////
936 //
937 // CPipeHandle -- Unix version
938 //
939 
940 
941 // Use poll(2) (now default) instead of select(2) (formerly)
942 NCBI_PARAM_DECL (bool, CONN, PIPE_USE_POLL);
943 NCBI_PARAM_DEF_EX(bool, CONN, PIPE_USE_POLL,
944  true, eParam_Default, CONN_PIPE_USE_POLL);
945 using TUsePoll = NCBI_PARAM_TYPE(CONN, PIPE_USE_POLL);
946 static CSafeStatic<TUsePoll> s_UsePoll;
947 
948 
949 class CPipeHandle
950 {
951 public:
952  CPipeHandle(void);
953  ~CPipeHandle();
954  EIO_Status Open(const string& cmd,
955  const vector<string>& args,
956  CPipe::TCreateFlags create_flags,
957  const string& current_dir,
958  const char* const envp[],
959  size_t /*pipe_size*/);
960  void OpenSelf(void);
961  EIO_Status Close(int* exitcode, const STimeout* timeout);
963  EIO_Status Read(void* buf, size_t count, size_t* read,
964  const CPipe::EChildIOHandle from_handle,
965  const STimeout* timeout) const;
966  EIO_Status Write(const void* data, size_t count, size_t* written,
967  const STimeout* timeout) const;
969  const STimeout* timeout) const;
970  TProcessHandle GetProcessHandle(void) const { return m_Pid; }
971  void Release(void) { x_Clear(); }
972 
973 private:
974  // Clear object state.
975  void x_Clear(void);
976  // Get child's I/O handle.
977  int x_GetHandle(CPipe::EChildIOHandle from_handle) const;
978  // Trigger blocking mode on specified I/O handle.
979  void x_SetNonBlockingMode(int fd) const;
980  // Wait on the file descriptors for I/O.
982  const STimeout* timeout) const;
983 
984 private:
985  // I/O handles for child process.
986  int m_ChildStdIn;
987  int m_ChildStdOut;
988  int m_ChildStdErr;
989 
990  // Child process PID.
991  TPid m_Pid;
992 
993  // Pipe flags
995 
996  // Flag that indicates whether the m_ChildStd* and m_Pid
997  // member variables contain the relevant handles of the
998  // current process, in which case they won't be closed.
999  bool m_SelfHandles;
1000 
1001  // Use poll(2) (now default) instead of select(2) (formerly)
1002  bool m_UsePoll;
1003 };
1004 
1005 
1007  : m_ChildStdIn(-1), m_ChildStdOut(-1), m_ChildStdErr(-1),
1008  m_Pid((TPid)(-1)), m_Flags(0), m_SelfHandles(false)
1009 {
1010  m_UsePoll = s_UsePoll->Get();
1011  ERR_POST_ONCE(Trace << "CPipeHandle using poll(): "
1012  + NStr::BoolToString(m_UsePoll));
1013 }
1014 
1015 
1017 {
1018  static const STimeout kZeroTimeout = {0, 0};
1019  Close(0, &kZeroTimeout);
1020  x_Clear();
1021 }
1022 
1023 
1024 // Auxiliary function to exit from forked process with reporting errno
1025 // on errors into the specified file descriptor
1026 [[noreturn]] static void s_Exit(int status, int fd)
1027 {
1028  int errcode = errno;
1029  (void) ::write(fd, &errcode, sizeof(errcode));
1030  (void) ::close(fd);
1031  ::_exit(status);
1032 }
1033 
1034 
1035 #ifndef HAVE_EXECVPE
1036 
1037 // Emulate the nonexistent execvpe() call.
1038 // On success, execve() does not return; on error -1 is returned,
1039 // and errno is set appropriately.
1040 
1041 static int s_ExecShell(const char* command,
1042  char *const argv[], char *const envp[],
1043  const CNcbiEnvironment& xenv)
1044 {
1045  static const char kDefaultShell[] = "/bin/sh";
1046 
1047  // Count the number of arguments
1048  size_t i;
1049  for (i = 0; argv[i]; ++i);
1050  ++i; // last NULL element
1051  _ASSERT(i > 1);
1052 
1053  // Figure out the shell first
1054  string xshell = xenv.Get("SHELL");
1055  const char* shell = xshell.empty() ? kDefaultShell : xshell.c_str();
1056 
1057  // Construct argument list for the shell
1058  const char** args = new const char*[i + 1];
1060 
1061  args[0] = shell;
1062  args[1] = command;
1063  // NB: skip argv[0]
1064  for (; i > 1; --i) {
1065  args[i] = argv[i - 1];
1066  }
1067 
1068  // Execute the shell
1069  return ::execve(shell, (char**) args, envp);
1070 }
1071 
1072 
1073 // Note this is executing in a separate process: no app/env locks necessary
1074 static int s_ExecVPE(const char* file, char* const argv[], char* const envp[])
1075 {
1076  // CAUTION (security): the current directory is in the path on purpose
1077  // (see man execlp), and also to be in-sync with the
1078  // default behavior on MS-Win.
1079  static const char kDefaultPath[] = ":/bin:/usr/bin";
1080 
1081  // If file name is not specified
1082  if (!file || *file == '\0') {
1083  errno = ENOENT;
1084  return -1;
1085  }
1086 
1089  ? &app->SetEnvironment()
1090  : new CNcbiEnvironment,
1091  app
1092  ? eNoOwnership
1093  : eTakeOwnership);
1094 
1095  // If the file name contains path
1096  if (strchr(file, '/')) {
1097  ::execve(file, argv, envp);
1098  return errno == ENOEXEC ? s_ExecShell(file, argv, envp, *env) : -1;
1099  }
1100 
1101  // Get the PATH environment variable
1102  const string& xpath = env->Get("PATH");
1103  const char* path = xpath.empty() ? kDefaultPath : xpath.c_str();
1104  _ASSERT(path[0]); // NB: strlen(path) >= 1
1105 
1106  size_t file_len = strlen(file) + 1/*'\0'*/;
1107  char* buf = new char[strlen(path) + 1/*'/'*/ + file_len];
1109 
1110  bool eacces_err = false;
1111  for (;;) {
1112  const char* next = strchr(path, ':');
1113  size_t len = next ? (size_t)(next - path) : strlen(path)/*last part*/;
1114  if (len) {
1115  // Copy the directory name into the buffer
1116  memmove(buf, path, len);
1117  // Add slash if needed
1118  if (buf[len - 1] != '/') {
1119  buf[len++] = '/';
1120  }
1121  } else {
1122  // Empty PATH element (e.g.":...","::","...:") -- current directory
1123  buf[0] = '.';
1124  buf[1] = '/';
1125  len = 2;
1126  }
1127  // Add the file name
1128  memcpy(buf + len, file, file_len);
1129 
1130  // Try to execute the file by the generated name
1131  int error;
1132  ::execve(buf, argv, envp);
1133  if ((error = errno) == ENOEXEC) {
1134  return s_ExecShell(buf, argv, envp, *env);
1135  }
1136  _ASSERT(error);
1137  switch (error) {
1138  case EACCES:
1139  // Access denied. Memorize this fact and try next path element.
1140  eacces_err = true;
1141  /*FALLTHRU*/
1142  case ENOENT:
1143  case ENOTDIR:
1144  // Try next path element
1145  break;
1146  default:
1147  // We found an executable file, but could not execute it
1148  return -1;
1149  }
1150  if (!next) {
1151  break;
1152  }
1153  path = next + 1;
1154  }
1155  if (eacces_err) {
1156  errno = EACCES;
1157  }
1158 
1159  return -1;
1160 }
1161 
1162 # define execvpe s_ExecVPE
1163 #endif // !HAVE_EXECVPE
1164 
1165 
1166 static int x_SafeFD(int fd, int safe)
1167 {
1168  if (fd == safe || fd > STDERR_FILENO) {
1169  return fd;
1170  }
1171  int temp = ::fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
1172  ::close(fd);
1173  return temp;
1174 }
1175 
1176 
1177 static bool x_SafePipe(int pipe[2], int n, int safe)
1178 {
1179  bool retval = true;
1180  if ((pipe[0] = x_SafeFD(pipe[0], n == 0 ? safe : -1)) == -1) {
1181  ::close(pipe[1]);
1182  retval = false;
1183  } else if ((pipe[1] = x_SafeFD(pipe[1], n == 1 ? safe : -1)) == -1) {
1184  ::close(pipe[0]);
1185  retval = false;
1186  }
1187  return retval;
1188 }
1189 
1190 
1191 EIO_Status CPipeHandle::Open(const string& cmd,
1192  const vector<string>& args,
1193  CPipe::TCreateFlags create_flags,
1194  const string& current_dir,
1195  const char* const envp[],
1196  size_t /*unused*/)
1197 
1198 {
1199  DEFINE_STATIC_FAST_MUTEX(s_Mutex);
1200  CFastMutexGuard guard_mutex(s_Mutex);
1201 
1202  if (m_Pid != (TPid)(-1)) {
1203  ERR_POST_X(1, s_FormatErrorMessage("Open", "Pipe busy"));
1204  return eIO_Unknown;
1205  }
1206  m_Flags = create_flags;
1207 
1208  // Child process I/O handles: init "our" ends of the pipes
1209  int pipe_in[2], pipe_out[2], pipe_err[2];
1210  pipe_in[0] = -1;
1211  pipe_out[1] = -1;
1212  pipe_err[1] = -1;
1213 
1214  EIO_Status status = eIO_Unknown;
1215 
1216  int status_pipe[2] = {-1, -1};
1217  try {
1218  // Flush stdio
1219  NcbiCout.flush();
1220  NcbiCerr.flush();
1221  ::fflush(NULL);
1222 
1223  // Create pipe for child's stdin
1225  if (!IS_SET(create_flags, CPipe::fStdIn_Close)) {
1226  if (::pipe(pipe_in) < 0
1227  || !x_SafePipe(pipe_in, 0, STDIN_FILENO)) {
1228  pipe_in[0] = -1;
1229  PIPE_THROW(errno,
1230  "Failed to create pipe for stdin");
1231  }
1232  m_ChildStdIn = pipe_in[1];
1234  }
1235 
1236  // Create pipe for child's stdout
1238  if (!IS_SET(create_flags, CPipe::fStdOut_Close)) {
1239  if (::pipe(pipe_out) < 0
1240  || !x_SafePipe(pipe_out, 1, STDOUT_FILENO)) {
1241  pipe_out[1] = -1;
1242  PIPE_THROW(errno,
1243  "Failed to create pipe for stdout");
1244  }
1245  m_ChildStdOut = pipe_out[0];
1247  }
1248 
1249  // Create pipe for child's stderr
1251  if (IS_SET(create_flags, CPipe::fStdErr_Open)) {
1252  if (::pipe(pipe_err) < 0
1253  || !x_SafePipe(pipe_err, 1, STDERR_FILENO)) {
1254  pipe_err[1] = -1;
1255  PIPE_THROW(errno,
1256  "Failed to create pipe for stderr");
1257  }
1258  m_ChildStdErr = pipe_err[0];
1260  }
1261 
1262  // Create temporary pipe to get status of execution
1263  // of the child process
1264  if (::pipe(status_pipe) < 0
1265  || !x_SafePipe(status_pipe, -1, -1)) {
1266  PIPE_THROW(errno,
1267  "Failed to create status pipe");
1268  }
1269  ::fcntl(status_pipe[1], F_SETFD,
1270  ::fcntl(status_pipe[1], F_GETFD, 0) | FD_CLOEXEC);
1271 
1272  // Fork off a child process
1273  switch (m_Pid = ::fork()) {
1274  case (TPid)(-1):
1275  PIPE_THROW(errno,
1276  "Failed fork()");
1277  /*NOTREACHED*/
1278  break;
1279 
1280  case 0:
1281  // *** CHILD PROCESS CONTINUES HERE ***
1282 
1283  // Create new process group if needed
1284  if (IS_SET(create_flags, CPipe::fNewGroup)) {
1285  ::setpgid(0, 0);
1286  }
1287 
1288  // Close unused pipe handle
1289  ::close(status_pipe[0]);
1290 
1291  // Bind child's standard I/O file handles to pipes
1292  if (!IS_SET(create_flags, CPipe::fStdIn_Close)) {
1293  if (pipe_in[0] != STDIN_FILENO) {
1294  if (::dup2(pipe_in[0], STDIN_FILENO) < 0) {
1295  s_Exit(-1, status_pipe[1]);
1296  }
1297  ::close(pipe_in[0]);
1298  }
1299  ::close(pipe_in[1]);
1300  ::fflush(stdin);
1301  } else {
1302  (void) ::freopen("/dev/null", "r", stdin);
1303  }
1304  if (!IS_SET(create_flags, CPipe::fStdOut_Close)) {
1305  if (pipe_out[1] != STDOUT_FILENO) {
1306  if (::dup2(pipe_out[1], STDOUT_FILENO) < 0) {
1307  s_Exit(-1, status_pipe[1]);
1308  }
1309  ::close(pipe_out[1]);
1310  }
1311  ::close(pipe_out[0]);
1312  } else {
1313  (void) ::freopen("/dev/null", "w", stdout);
1314  }
1315  if (IS_SET(create_flags, CPipe::fStdErr_Open)) {
1316  if (pipe_err[1] != STDERR_FILENO) {
1317  if (::dup2(pipe_err[1], STDERR_FILENO) < 0) {
1318  s_Exit(-1, status_pipe[1]);
1319  }
1320  ::close(pipe_err[1]);
1321  }
1322  ::close(pipe_err[0]);
1323  } else if (IS_SET(create_flags, CPipe::fStdErr_Share)) {
1324  /*nothing to do*/;
1325  } else if (IS_SET(create_flags, CPipe::fStdErr_StdOut)) {
1326  _ASSERT(STDOUT_FILENO != STDERR_FILENO);
1327  if (::dup2(STDOUT_FILENO, STDERR_FILENO) < 0) {
1328  s_Exit(-1, status_pipe[1]);
1329  }
1330  } else {
1331  (void) ::freopen("/dev/null", "a", stderr);
1332  }
1333 
1334  // Restore SIGPIPE signal processing
1335  if (IS_SET(create_flags, CPipe::fSigPipe_Restore)) {
1336  ::signal(SIGPIPE, SIG_DFL);
1337  }
1338 
1339  // Prepare program arguments
1340  size_t i;
1341  const char** argv = new const char*[args.size() + 2];
1343  argv[i = 0] = cmd.c_str();
1344  for (auto&& arg : args) {
1345  argv[++i] = arg.c_str();
1346  }
1347  argv[++i] = 0;
1348 
1349  // Change current working directory if specified
1350  if (!current_dir.empty() && current_dir != ".") {
1351  (void) ::chdir(current_dir.c_str());
1352  }
1353  // Execute the program
1354  int status;
1355  if (envp) {
1356  status = execvpe(argv[0],
1357  const_cast<char**>(argv),
1358  const_cast<char**>(envp));
1359  } else {
1360  status = ::execvp(argv[0],
1361  const_cast<char**>(argv));
1362  }
1363  s_Exit(status, status_pipe[1]);
1364 
1365  // *** CHILD PROCESS DOES NOT CONTINUE BEYOND THIS LINE ***
1366  }
1367 
1368  // Close unused pipes' ends
1369  if (!IS_SET(create_flags, CPipe::fStdIn_Close)) {
1370  ::close(pipe_in[0]);
1371  pipe_in[0] = -1;
1372  }
1373  if (!IS_SET(create_flags, CPipe::fStdOut_Close)) {
1374  ::close(pipe_out[1]);
1375  pipe_out[1] = -1;
1376  }
1377  if (IS_SET(create_flags, CPipe::fStdErr_Open)) {
1378  ::close(pipe_err[1]);
1379  pipe_err[1] = -1;
1380  }
1381  ::close(status_pipe[1]);
1382  status_pipe[1] = -1;
1383 
1384  // Check status pipe:
1385  // if it has some data, this is an errno from the child process;
1386  // if there is an EOF, then the child exec()'d successfully.
1387  // Retry if either blocked or interrupted
1388 
1389  // Try to read errno the from forked process
1390  ssize_t n;
1391  int errcode;
1392  while ((n = ::read(status_pipe[0], &errcode, sizeof(errcode))) < 0) {
1393  if (errno != EINTR)
1394  break;
1395  }
1396  ::close(status_pipe[0]);
1397  status_pipe[0] = -1;
1398 
1399  if (n > 0) {
1400  // Child could not run -- reap it and exit with an error
1401  status = eIO_Closed;
1402  ::waitpid(m_Pid, NULL, 0);
1403  PIPE_THROW((size_t) n < sizeof(errcode) ? 0 : errcode,
1404  "Failed to execute '" + x_CommandLine(cmd, args) +'\'');
1405  }
1406 
1407  return eIO_Success;
1408  }
1409  catch (string& what) {
1410  // Close all open file descriptors
1411  if (pipe_in[0] != -1) {
1412  ::close(pipe_in[0]);
1413  }
1414  if (pipe_out[1] != -1) {
1415  ::close(pipe_out[1]);
1416  }
1417  if (pipe_err[1] != -1) {
1418  ::close(pipe_err[1]);
1419  }
1420  if (status_pipe[0] != -1) {
1421  ::close(status_pipe[0]);
1422  }
1423  if (status_pipe[1] != -1) {
1424  ::close(status_pipe[1]);
1425  }
1426  static const STimeout kZeroTimeout = {0, 0};
1427  Close(0, &kZeroTimeout);
1428  ERR_POST_X(1, s_FormatErrorMessage("Open", what));
1429  x_Clear();
1430  }
1431 
1432  return status;
1433 }
1434 
1435 
1436 void CPipeHandle::OpenSelf(void)
1437 {
1438  if (m_Pid != (TPid)(-1)) {
1439  PIPE_THROW(0,
1440  "Pipe busy");
1441  }
1442 
1443  NcbiCout.flush();
1444  ::fflush(stdout);
1445  m_ChildStdIn = fileno(stdout); // NB: a macro on BSD, so no "::" scope
1446  m_ChildStdOut = fileno(stdin);
1447  m_Pid = ::getpid();
1448 
1449  m_SelfHandles = true;
1450 }
1451 
1452 
1453 void CPipeHandle::x_Clear(void)
1454 {
1455  m_Pid = (TPid)(-1);
1456  if (m_SelfHandles) {
1457  m_ChildStdIn = -1;
1458  m_ChildStdOut = -1;
1459  m_SelfHandles = false;
1460  } else {
1464  }
1465 }
1466 
1467 
1468 EIO_Status CPipeHandle::Close(int* exitcode, const STimeout* timeout)
1469 {
1470  EIO_Status status;
1471 
1472  if (!m_SelfHandles) {
1476 
1477  if (m_Pid == (TPid)(-1)) {
1478  if (exitcode) {
1479  *exitcode = -1;
1480  }
1481  status = eIO_Closed;
1482  } else {
1483  CProcess process(m_Pid, CProcess::ePid);
1484  status = s_Close(process, m_Flags, timeout, exitcode);
1485  }
1486  } else {
1487  if (exitcode) {
1488  *exitcode = 0;
1489  }
1490  status = eIO_Success;
1491  }
1492 
1493  if (status != eIO_Timeout) {
1494  x_Clear();
1495  }
1496  return status;
1497 }
1498 
1499 
1501 {
1502  switch (handle) {
1503  case CPipe::eStdIn:
1504  if (m_ChildStdIn == -1) {
1505  return eIO_Closed;
1506  }
1508  m_ChildStdIn = -1;
1509  break;
1510  case CPipe::eStdOut:
1511  if (m_ChildStdOut == -1) {
1512  return eIO_Closed;
1513  }
1515  m_ChildStdOut = -1;
1516  break;
1517  case CPipe::eStdErr:
1518  if (m_ChildStdErr == -1) {
1519  return eIO_Closed;
1520  }
1522  m_ChildStdErr = -1;
1523  break;
1524  default:
1525  _TROUBLE;
1526  return eIO_InvalidArg;
1527  }
1528  return eIO_Success;
1529 }
1530 
1531 
1532 EIO_Status CPipeHandle::Read(void* buf, size_t count, size_t* n_read,
1533  const CPipe::EChildIOHandle from_handle,
1534  const STimeout* timeout) const
1535 {
1536  _ASSERT(!n_read || !*n_read);
1537  _ASSERT(!(from_handle & (from_handle - 1)));
1538 
1539  EIO_Status status = eIO_Unknown;
1540 
1541  try {
1542  if (m_Pid == (TPid)(-1)) {
1543  PIPE_THROW(0,
1544  "Pipe closed");
1545  }
1546  int fd = x_GetHandle(from_handle);
1547  if (fd == -1) {
1548  PIPE_THROW(0,
1549  "Pipe I/O handle "
1550  + x_GetHandleName(from_handle) + " closed");
1551  }
1552  if (!count) {
1553  return eIO_Success;
1554  }
1555 
1556  // Retry if either blocked or interrupted
1557  for (;;) {
1558  // Try to read
1559  ssize_t bytes_read = ::read(fd, buf, count);
1560  if (bytes_read >= 0) {
1561  if (n_read) {
1562  *n_read = (size_t) bytes_read;
1563  }
1564  status = bytes_read ? eIO_Success : eIO_Closed;
1565  break;
1566  }
1567  int error = errno;
1568 
1569  if (error == EAGAIN || error == EWOULDBLOCK) {
1570  // Blocked -- wait for data to come; exit if timeout/error
1571  if ((timeout && !(timeout->sec | timeout->usec))
1572  || !x_Poll(from_handle, timeout)) {
1573  status = eIO_Timeout;
1574  break;
1575  }
1576  continue;
1577  }
1578  if (error != EINTR) {
1579  PIPE_THROW(error,
1580  "Failed to read data from pipe I/O handle "
1581  + x_GetHandleName(from_handle));
1582  }
1584  status = eIO_Interrupt;
1585  break;
1586  }
1587  // Interrupted read -- restart
1588  }
1589  }
1590  catch (string& what) {
1591  ERR_POST_X(2, s_FormatErrorMessage("Read", what));
1592  }
1593 
1594  return status;
1595 }
1596 
1597 
1598 EIO_Status CPipeHandle::Write(const void* data, size_t count,
1599  size_t* n_written, const STimeout* timeout) const
1600 
1601 {
1602  _ASSERT(!n_written || !*n_written);
1603 
1604  EIO_Status status = eIO_Unknown;
1605 
1606  try {
1607  if (m_Pid == (TPid)(-1)) {
1608  PIPE_THROW(0,
1609  "Pipe closed");
1610  }
1611  if (m_ChildStdIn == -1) {
1612  status = eIO_Closed;
1613  PIPE_THROW(0,
1614  "Pipe I/O handle "
1615  + x_GetHandleName(CPipe::eStdIn) + " closed");
1616  }
1617  if (!count) {
1618  return eIO_Success;
1619  }
1620 
1621  // Retry if either blocked or interrupted
1622  for (;;) {
1623  // Try to write
1624  ssize_t bytes_written = ::write(m_ChildStdIn, data, count);
1625  if (bytes_written >= 0) {
1626  if (n_written) {
1627  *n_written = (size_t) bytes_written;
1628  }
1629  status = bytes_written ? eIO_Success : eIO_Unknown;
1630  break;
1631  }
1632  int error = errno;
1633 
1634  if (errno == EAGAIN || errno == EWOULDBLOCK) {
1635  // Blocked -- wait for write readiness; exit if timeout/error
1636  if ((timeout && !(timeout->sec | timeout->usec))
1637  || !x_Poll(CPipe::fStdIn, timeout)) {
1638  status = eIO_Timeout;
1639  break;
1640  }
1641  continue;
1642  }
1643  if (errno != EINTR) {
1644  if (error == EPIPE) {
1645  // Peer has closed its end
1646  status = eIO_Closed;
1647  } // NB: status == eIO_Unknown
1648  PIPE_THROW(errno,
1649  "Failed to write data to pipe I/O handle "
1651  }
1653  status = eIO_Interrupt;
1654  break;
1655  }
1656  // Interrupted write -- restart
1657  }
1658  }
1659  catch (string& what) {
1660  ERR_POST_X(3, s_FormatErrorMessage("Write", what));
1661  }
1662 
1663  return status;
1664 }
1665 
1666 
1668  const STimeout* timeout) const
1669 {
1671 
1672  try {
1673  if (m_Pid == (TPid)(-1)) {
1674  PIPE_THROW(0,
1675  "Pipe closed");
1676  }
1677  if (m_ChildStdIn == -1 &&
1678  m_ChildStdOut == -1 &&
1679  m_ChildStdErr == -1) {
1680  PIPE_THROW(0,
1681  "All pipe I/O handles closed");
1682  }
1683  poll = x_Poll(mask, timeout);
1684  }
1685  catch (string& what) {
1686  ERR_POST_X(4, s_FormatErrorMessage("Poll", what));
1687  }
1688 
1689  return poll;
1690 }
1691 
1692 
1693 int CPipeHandle::x_GetHandle(CPipe::EChildIOHandle from_handle) const
1694 {
1695  switch (from_handle) {
1696  case CPipe::eStdIn:
1697  return m_ChildStdIn;
1698  case CPipe::eStdOut:
1699  return m_ChildStdOut;
1700  case CPipe::eStdErr:
1701  return m_ChildStdErr;
1702  default:
1703  _TROUBLE;
1704  break;
1705  }
1706  return -1;
1707 }
1708 
1709 
1710 void CPipeHandle::x_SetNonBlockingMode(int fd) const
1711 {
1712  if (::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
1713  PIPE_THROW(errno,
1714  "Failed to set pipe I/O handle non-blocking");
1715  }
1716 }
1717 
1718 
1720  const STimeout* timeout) const
1721 {
1723 
1724  if (m_UsePoll) {
1725  struct pollfd poll_fds[3] = {
1726  { m_ChildStdIn, POLLOUT },
1727  { m_ChildStdOut, POLLIN },
1728  { m_ChildStdErr, POLLIN }
1729  };
1730  int timeout_msec(timeout
1731  ? timeout->sec * 1000 + (timeout->usec + 500) / 1000
1732  : -1/*infinite*/);
1733 
1734  // Negative FDs OK, poll ignores them
1735  // Check the mask
1736  if (!(mask & CPipe::fStdIn))
1737  poll_fds[0].fd = -1;
1738  if (!(mask & CPipe::fStdOut))
1739  poll_fds[1].fd = -1;
1740  if (!(mask & CPipe::fStdErr))
1741  poll_fds[2].fd = -1;
1742 
1743  for (;;) { // Auto-resume if interrupted by a signal
1744  int n = ::poll(poll_fds, 3, timeout_msec);
1745 
1746  if (n == 0) {
1747  // timeout
1748  break;
1749  }
1750  if (n > 0) {
1751  // no need to check mask here
1752  if (poll_fds[0].revents) {
1753  poll |= CPipe::fStdIn;
1754  }
1755  if (poll_fds[1].revents) {
1756  poll |= CPipe::fStdOut;
1757  }
1758  if (poll_fds[2].revents) {
1759  poll |= CPipe::fStdErr;
1760  }
1761  break;
1762  }
1763  // n < 0
1764  if ((n = errno) != EINTR) {
1765  PIPE_THROW(n,
1766  "Failed poll()");
1767  }
1769  break;
1770  }
1771  // continue, no need to recreate either timeout or poll_fds
1772  }
1773  } else { // Using select(2), as before
1774  for (;;) { // Auto-resume if interrupted by a signal
1775  struct timeval* tmp;
1776  struct timeval tmo;
1777 
1778  if (timeout) {
1779  // NB: Timeout has already been normalized
1780  tmo.tv_sec = timeout->sec;
1781  tmo.tv_usec = timeout->usec;
1782  tmp = &tmo;
1783  } else {
1784  tmp = 0;
1785  }
1786 
1787  fd_set rfds;
1788  fd_set wfds;
1789  fd_set efds;
1790 
1791  int max = -1;
1792  bool rd = false;
1793  bool wr = false;
1794 
1795  FD_ZERO(&efds);
1796 
1797  if ((mask & CPipe::fStdIn) && m_ChildStdIn != -1) {
1798  wr = true;
1799  FD_ZERO(&wfds);
1800  if (m_ChildStdIn < FD_SETSIZE) {
1801  FD_SET(m_ChildStdIn, &wfds);
1802  FD_SET(m_ChildStdIn, &efds);
1803  }
1804  if (max < m_ChildStdIn) {
1805  max = m_ChildStdIn;
1806  }
1807  }
1808  if ((mask & CPipe::fStdOut) && m_ChildStdOut != -1) {
1809  rd = true;
1810  FD_ZERO(&rfds);
1811  if (m_ChildStdOut < FD_SETSIZE) {
1812  FD_SET(m_ChildStdOut, &rfds);
1813  FD_SET(m_ChildStdOut, &efds);
1814  }
1815  if (max < m_ChildStdOut) {
1816  max = m_ChildStdOut;
1817  }
1818  }
1819  if ((mask & CPipe::fStdErr) && m_ChildStdErr != -1) {
1820  if (!rd) {
1821  rd = true;
1822  FD_ZERO(&rfds);
1823  }
1824  if (m_ChildStdErr < FD_SETSIZE) {
1825  FD_SET(m_ChildStdErr, &rfds);
1826  FD_SET(m_ChildStdErr, &efds);
1827  }
1828  if (max < m_ChildStdErr) {
1829  max = m_ChildStdErr;
1830  }
1831  }
1832  _ASSERT(rd || wr);
1833 
1834  if (max >= FD_SETSIZE) {
1835  PIPE_THROW(0,
1836  "File descriptor " + NStr::NumericToString(max)
1837  + " too large (maximum allowed "
1838  + string(NCBI_AS_STRING(FD_SETSIZE)) + ')');
1839  }
1840 
1841  int n = ::select(max + 1,
1842  rd ? &rfds : 0,
1843  wr ? &wfds : 0, &efds, tmp);
1844 
1845  if (n == 0) {
1846  // timeout
1847  break;
1848  }
1849  if (n > 0) {
1850  if (wr
1851  && (FD_ISSET(m_ChildStdIn, &wfds) ||
1852  FD_ISSET(m_ChildStdIn, &efds))) {
1853  poll |= CPipe::fStdIn;
1854  }
1855  if ((mask & CPipe::fStdOut) && m_ChildStdOut != -1
1856  && (FD_ISSET(m_ChildStdOut, &rfds) ||
1857  FD_ISSET(m_ChildStdOut, &efds))) {
1858  poll |= CPipe::fStdOut;
1859  }
1860  if ((mask & CPipe::fStdErr) && m_ChildStdErr != -1
1861  && (FD_ISSET(m_ChildStdErr, &rfds) ||
1862  FD_ISSET(m_ChildStdErr, &efds))) {
1863  poll |= CPipe::fStdErr;
1864  }
1865  break;
1866  }
1867  if ((n = errno) != EINTR) {
1868  PIPE_THROW(n,
1869  "Failed select()");
1870  }
1872  break;
1873  }
1874  // continue
1875  }
1876  }
1877 
1878  _ASSERT(!(poll ^ (poll & mask)));
1879  return poll;
1880 }
1881 
1882 
1883 #endif // NCBI_OS_UNIX | NCBI_OS_MSWIN
1884 
1885 
1886 //////////////////////////////////////////////////////////////////////////////
1887 //
1888 // CPipe
1889 //
1890 
1891 CPipe::CPipe(size_t pipe_size)
1892  : m_PipeSize(pipe_size),
1893  m_PipeHandle(new CPipeHandle), m_ReadHandle(eStdOut),
1894  m_ReadStatus(eIO_Closed), m_WriteStatus(eIO_Closed),
1895  m_ReadTimeout(0), m_WriteTimeout(0), m_CloseTimeout(0)
1896 {
1897  return;
1898 }
1899 
1900 
1901 CPipe::CPipe(const string& cmd,
1902  const vector<string>& args,
1903  TCreateFlags create_flags,
1904  const string& current_dir,
1905  const char* const envp[],
1906  size_t pipe_size)
1907  : m_PipeSize(pipe_size),
1908  m_PipeHandle(0), m_ReadHandle(eStdOut),
1909  m_ReadStatus(eIO_Closed), m_WriteStatus(eIO_Closed),
1910  m_ReadTimeout(0), m_WriteTimeout(0), m_CloseTimeout(0)
1911 {
1912  unique_ptr<CPipeHandle> pipe_handle_ptr(new CPipeHandle);
1913  EIO_Status status = pipe_handle_ptr->Open(cmd, args, create_flags,
1914  current_dir, envp, pipe_size);
1915  if (status != eIO_Success) {
1916  NCBI_THROW(CPipeException, eOpen,
1917  "[CPipe::CPipe] Failed: " + string(IO_StatusStr(status)));
1918  }
1919  m_PipeHandle = pipe_handle_ptr.release();
1920 }
1921 
1922 
1924 {
1925  Close();
1926  delete m_PipeHandle;
1927 }
1928 
1929 
1931  const vector<string>& args,
1932  TCreateFlags create_flags,
1933  const string& current_dir,
1934  const char* const envp[],
1935  size_t pipe_size)
1936 {
1938  if (pipe_size) {
1939  m_PipeSize = pipe_size;
1940  }
1941 
1943  EIO_Status status = m_PipeHandle->Open(cmd, args, create_flags,
1944  current_dir, envp, m_PipeSize);
1945  m_ReadStatus = status;
1946  m_WriteStatus = status;
1947  return status;
1948 }
1949 
1950 
1952 {
1955  try {
1957  }
1958  catch (string& err) {
1961  NCBI_THROW(CPipeException, eOpen, err);
1962  }
1965 }
1966 
1967 
1968 EIO_Status CPipe::Close(int* exitcode)
1969 {
1971  EIO_Status status = m_PipeHandle->Close(exitcode, m_CloseTimeout);
1972  m_ReadStatus = status == eIO_Timeout ? eIO_Timeout : eIO_Closed;
1973  m_WriteStatus = status == eIO_Timeout ? eIO_Timeout : eIO_Closed;
1974  return status;
1975 }
1976 
1977 
1979 {
1981  if (handle == eDefault) {
1982  handle = m_ReadHandle;
1983  }
1984  EIO_Status status = m_PipeHandle->CloseHandle(handle);
1985  if (handle != eStdIn) {
1986  m_ReadStatus = status;
1987  } else {
1988  m_WriteStatus = status;
1989  }
1990  return status;
1991 }
1992 
1993 
1995 {
1997  if (from_handle == eStdIn) {
1998  return eIO_InvalidArg;
1999  }
2000  m_ReadHandle = from_handle == eDefault ? eStdOut : from_handle;
2001  return eIO_Success;
2002 }
2003 
2004 
2005 EIO_Status CPipe::Read(void* buf, size_t count, size_t* n_read,
2006  EChildIOHandle from_handle)
2007 {
2009  if (n_read) {
2010  *n_read = 0;
2011  }
2012  if (from_handle == eStdIn) {
2013  return eIO_InvalidArg;
2014  }
2015  if (from_handle == eDefault) {
2016  from_handle = m_ReadHandle;
2017  }
2019  if (count && !buf) {
2020  return eIO_InvalidArg;
2021  }
2022  m_ReadStatus = m_PipeHandle->Read(buf, count, n_read, from_handle,
2023  m_ReadTimeout);
2024  return m_ReadStatus;
2025 }
2026 
2027 
2028 EIO_Status CPipe::Write(const void* data, size_t count, size_t* n_written)
2029 {
2031  if (n_written) {
2032  *n_written = 0;
2033  }
2034  if (count && !data) {
2035  return eIO_InvalidArg;
2036  }
2037  m_WriteStatus = m_PipeHandle->Write(data, count, n_written,
2038  m_WriteTimeout);
2039  return m_WriteStatus;
2040 }
2041 
2042 
2044  const STimeout* timeout)
2045 {
2046  _ASSERT(m_PipeHandle && timeout != kDefaultTimeout);
2047  if (!mask || timeout == kDefaultTimeout) {
2048  return 0;
2049  }
2050  TChildPollMask x_mask = mask;
2051  if (mask & fDefault) {
2053  x_mask |= m_ReadHandle;
2054  }
2055  TChildPollMask poll = m_PipeHandle->Poll(x_mask, timeout);
2056  if (mask & fDefault) {
2057  if (poll & m_ReadHandle) {
2058  poll |= fDefault;
2059  }
2060  poll &= mask;
2061  }
2062  // Result may not be a bigger set
2063  _ASSERT(!(poll ^ (poll & mask)));
2064  return poll;
2065 }
2066 
2067 
2069 {
2071  switch (direction) {
2072  case eIO_Read:
2073  return m_ReadStatus;
2074  case eIO_Write:
2075  return m_WriteStatus;
2076  default:
2077  _TROUBLE;
2078  break;
2079  }
2080  return eIO_InvalidArg;
2081 }
2082 
2083 
2085 {
2087  if (timeout == kDefaultTimeout) {
2088  return eIO_Success;
2089  }
2090  switch (event) {
2091  case eIO_Close:
2093  break;
2094  case eIO_Read:
2096  break;
2097  case eIO_Write:
2099  break;
2100  case eIO_ReadWrite:
2103  break;
2104  default:
2105  _TROUBLE;
2106  return eIO_InvalidArg;
2107  }
2108  return eIO_Success;
2109 }
2110 
2111 
2113 {
2115  switch (event) {
2116  case eIO_Close:
2117  return m_CloseTimeout;
2118  case eIO_Read:
2119  return m_ReadTimeout;
2120  case eIO_Write:
2121  return m_WriteTimeout;
2122  default:
2123  _TROUBLE;
2124  break;
2125  }
2126  return kDefaultTimeout;
2127 }
2128 
2129 
2131 {
2133  return m_PipeHandle->GetProcessHandle();
2134 }
2135 
2136 
2138 {
2139 }
2140 
2141 
2142 /* static */
2144  const vector<string>& args,
2145  CNcbiIstream& in,
2146  CNcbiOstream& out,
2147  CNcbiOstream& err,
2148  int& exit_code,
2149  const string& current_dir,
2150  const char* const envp[],
2151  CPipe::IProcessWatcher* watcher,
2152  const STimeout* kill_timeout,
2153  size_t pipe_size)
2154 {
2155  STimeout ktm;
2156 
2157  if (kill_timeout) {
2158  ktm = *kill_timeout;
2159  } else {
2161  }
2162 
2163  CPipe pipe(pipe_size);
2164  EIO_Status status = pipe.Open(cmd, args,
2166  | fNewGroup | fKillOnClose,
2167  current_dir, envp);
2168  if (status != eIO_Success) {
2169  NCBI_THROW(CPipeException, eOpen,
2170  "[CPipe::ExecWait] Cannot execute '"
2171  + x_CommandLine(cmd, args) + '\'');
2172  }
2173  _ASSERT(pipe.m_PipeHandle);
2174 
2175  TProcessHandle pid = pipe.GetProcessHandle();
2176 
2177  if (watcher && watcher->OnStart(pid) != IProcessWatcher::eContinue) {
2178  pipe.SetTimeout(eIO_Close, &ktm);
2179  pipe.Close(&exit_code);
2180  return eCanceled;
2181  }
2182 
2183  EFinish finish = eDone;
2184  try {
2185 #ifndef NCBI_OS_LINUX
2186  const size_t buf_size = 16 * 1024;
2187 #else
2188  const size_t buf_size = 192 * 1024;
2189 #endif // NCBI_OS_LINUX
2190  AutoPtr< char, ArrayDeleter<char> > inbuf(new char[buf_size]);
2191  AutoPtr< char, ArrayDeleter<char> > buf(new char[buf_size]);
2192 
2193  size_t bytes_in_inbuf = 0;
2194  size_t bytes_written = 0;
2195  bool load_in = true;
2196 
2198 
2199  while (mask) {
2200  static const STimeout kNoWait = {0, 0};
2201  static const STimeout kWait = {1, 0};
2202 
2203  TChildPollMask rmask = pipe.Poll(mask, load_in? &kNoWait : &kWait);
2204 
2205  if ((rmask & fStdIn) || load_in) {
2206  bool done_in = false;
2207  if (!bytes_in_inbuf) {
2208  if (!in.good()) {
2209  done_in = true;
2210  } else if ((bytes_in_inbuf =
2211  (size_t) CStreamUtils::Readsome
2212  (in, inbuf.get(), buf_size)) != 0) {
2213  bytes_written = 0;
2214  load_in = false;
2215  } else if (!in.good()) {
2216  done_in = true;
2217  } else if (!(rmask & ~fStdIn)) {
2218  SleepMilliSec(NcbiTimeoutToMs(&kWait) / 100);
2219  }
2220  }
2221  if (bytes_in_inbuf && (rmask & fStdIn)) {
2222  size_t x_written;
2223  status = pipe.Write(inbuf.get() + bytes_written,
2224  bytes_in_inbuf, &x_written);
2225  bytes_in_inbuf -= x_written;
2226  if (status != eIO_Success) {
2227  if (bytes_in_inbuf) {
2228  ERR_POST_X(5,
2230  ("ExecWait",
2231  "Cannot pass input data to '"
2232  + x_CommandLine(cmd, args) + "': "
2233  + string(IO_StatusStr(status))));
2234  }
2235  done_in = true;
2236  } else if (!bytes_in_inbuf) {
2237  load_in = true;
2238  } else {
2239  bytes_written += x_written;
2240  }
2241  }
2242  if (done_in) {
2243  pipe.CloseHandle(eStdIn);
2244  mask &= ~fStdIn;
2245  load_in = false;
2246  }
2247  }
2248 
2249  size_t x_read;
2250  if (rmask & fStdOut) {
2251  status = pipe.Read(buf.get(), buf_size, &x_read);
2252  if (x_read) {
2253  out.write(buf.get(), x_read);
2254  }
2255  if (status != eIO_Success) {
2256  mask &= ~fStdOut;
2257  }
2258  }
2259 
2260  if (rmask & fStdErr) {
2261  status = pipe.Read(buf.get(), buf_size, &x_read, eStdErr);
2262  if (x_read) {
2263  err.write(buf.get(), x_read);
2264  }
2265  if (status != eIO_Success) {
2266  mask &= ~fStdErr;
2267  }
2268  }
2269 
2270  if (!CProcess(pid).IsAlive())
2271  break;
2272  if (watcher) {
2273  switch (watcher->Watch(pid)) {
2275  continue;
2277  break;
2279  pipe.m_PipeHandle->Release();
2280  return eCanceled;
2281  }
2282  // IProcessWatcher::eStop
2283  pipe.SetTimeout(eIO_Close, &ktm);
2284  finish = eCanceled;
2285  break;
2286  }
2287  }
2288  } catch (...) {
2289  pipe.SetTimeout(eIO_Close, &ktm);
2290  pipe.Close(&exit_code);
2291  throw;
2292  }
2293  pipe.Close(&exit_code);
2294  return finish;
2295 }
2296 
2297 
2298 const char* CPipeException::GetErrCodeString(void) const
2299 {
2300  switch (GetErrCode()) {
2301  case eOpen: return "eOpen";
2302  default: break;
2303  }
2305 }
2306 
2307 
ncbi::TMaskedQueryRegions mask
AutoPtr –.
Definition: ncbimisc.hpp:401
static CNcbiApplication * Instance(void)
Singleton method.
Definition: ncbiapp.cpp:264
CNcbiEnvironment –.
Definition: ncbienv.hpp:110
CPipeException –.
Definition: ncbi_pipe.hpp:534
EIO_Status CloseHandle(CPipe::EChildIOHandle handle)
Definition: ncbi_pipe.cpp:602
CPipe::TChildPollMask Poll(CPipe::TChildPollMask mask, const STimeout *timeout) const
Definition: ncbi_pipe.cpp:804
EIO_Status Write(const void *data, size_t count, size_t *written, const STimeout *timeout) const
Definition: ncbi_pipe.cpp:728
void x_SetNonBlockingMode(HANDLE fd) const
Definition: ncbi_pipe.cpp:847
HANDLE m_ChildStdOut
Definition: ncbi_pipe.cpp:287
void OpenSelf(void)
Definition: ncbi_pipe.cpp:524
CPipe::TCreateFlags m_Flags
Definition: ncbi_pipe.cpp:294
HANDLE m_ChildStdErr
Definition: ncbi_pipe.cpp:288
HANDLE m_ChildStdIn
Definition: ncbi_pipe.cpp:286
EIO_Status Read(void *buf, size_t count, size_t *n_read, const CPipe::EChildIOHandle from_handle, const STimeout *timeout) const
Definition: ncbi_pipe.cpp:634
bool m_SelfHandles
Definition: ncbi_pipe.cpp:299
CPipe::TChildPollMask x_Poll(CPipe::TChildPollMask mask, const STimeout *timeout) const
Definition: ncbi_pipe.cpp:860
TProcessHandle GetProcessHandle(void) const
Definition: ncbi_pipe.cpp:271
EIO_Status Open(const string &cmd, const vector< string > &args, CPipe::TCreateFlags create_flags, const string &current_dir, const char *const envp[], size_t pipe_size)
Definition: ncbi_pipe.cpp:322
HANDLE x_GetHandle(CPipe::EChildIOHandle from_handle) const
Definition: ncbi_pipe.cpp:830
void Release(void)
Definition: ncbi_pipe.cpp:272
void x_Clear(void)
Definition: ncbi_pipe.cpp:555
HANDLE m_ProcHandle
Definition: ncbi_pipe.cpp:291
CPipeHandle(void)
Definition: ncbi_pipe.cpp:303
EIO_Status Close(int *exitcode, const STimeout *timeout)
Definition: ncbi_pipe.cpp:570
Callback interface for ExecWait()
Definition: ncbi_pipe.hpp:403
CPipe –.
Definition: ncbi_pipe.hpp:76
Extended exit information for waited process.
CProcess –.
CSafeStatic<>::
T & Get(void)
Create the variable if not created yet, return the reference.
static uch flags
int close(int fd)
Definition: connection.cpp:45
std::ofstream out("events_result.xml")
main entry point for tests
static CS_COMMAND * cmd
Definition: ct_dynamic.c:26
#define false
Definition: bool.h:36
static DLIST_TYPE *DLIST_NAME() next(DLIST_LIST_TYPE *list, DLIST_TYPE *item)
Definition: dlist.tmpl.h:56
static HENV env
Definition: transaction2.c:38
static char tmp[3200]
Definition: utf8.c:42
#define POLLIN
Definition: poll.h:37
#define poll(fds, nfds, timeout)
Definition: poll.h:81
#define FD_SETSIZE
Definition: poll.h:30
#define POLLOUT
Definition: poll.h:38
int BOOL
Definition: sybdb.h:150
char data[12]
Definition: iconv.c:80
void reset(element_type *p=0, EOwnership ownership=eTakeOwnership)
Reset will delete the old pointer (if owned), set content to the new value, and assume the ownership ...
Definition: ncbimisc.hpp:480
CNcbiEnvironment & SetEnvironment(void)
Get a non-const copy of the application's cached environment.
static CNcbiApplicationGuard InstanceGuard(void)
Singleton method.
Definition: ncbiapp.cpp:133
element_type * get(void) const
Get pointer.
Definition: ncbimisc.hpp:469
@ eOn
Definition: ncbi_types.h:111
@ eTakeOwnership
An object can take ownership of another.
Definition: ncbi_types.h:136
@ eNoOwnership
No ownership is assumed.
Definition: ncbi_types.h:135
#define NULL
Definition: ncbistd.hpp:225
#define ERR_POST_ONCE(message)
Error posting only once during program execution.
Definition: ncbidiag.hpp:602
#define ERR_POST_X(err_subcode, message)
Error posting with default error code and given error subcode.
Definition: ncbidiag.hpp:550
const string & Get(const string &name, bool *found=NULL) const
Get environment value by name.
Definition: ncbienv.cpp:109
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
void Trace(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1179
virtual const char * GetErrCodeString(void) const override
Translate from the error code value to its string representation.
Definition: ncbiexpt.cpp:757
static string QuoteArg(const string &arg)
Quote argument.
Definition: ncbiexec.cpp:230
#define NCBI_PARAM_TYPE(section, name)
Generate typename for a parameter from its {section, name} attributes.
Definition: ncbi_param.hpp:149
#define NCBI_PARAM_DECL(type, section, name)
Parameter declaration.
Definition: ncbi_param.hpp:157
#define NCBI_PARAM_DEF_EX(type, section, name, default_value, flags, env)
Definition of a parameter with additional flags.
Definition: ncbi_param.hpp:228
@ eParam_Default
Default flags.
Definition: ncbi_param.hpp:416
virtual EAction Watch(TProcessHandle)=0
This method is getting called periodically during the process execution by the ExecWait() method.
EIO_Status m_ReadStatus
Last read status.
Definition: ncbi_pipe.hpp:505
EIO_Status Write(const void *data, size_t count, size_t *written=0)
Write data to pipe (data always goes to the child's eStdIn handle).
Definition: ncbi_pipe.cpp:2028
EIO_Status Open(const string &cmd, const vector< string > &args, TCreateFlags create_flags=0, const string &current_dir=kEmptyStr, const char *const envp[]=0, size_t pipe_size=0)
Open pipe.
Definition: ncbi_pipe.cpp:1930
EChildIOHandle m_ReadHandle
Default handle used for read.
Definition: ncbi_pipe.hpp:502
EChildIOHandle
Which of the child I/O handles to use.
Definition: ncbi_pipe.hpp:114
~CPipe(void)
Destructor.
Definition: ncbi_pipe.cpp:1923
TChildPollMask Poll(TChildPollMask mask, const STimeout *timeout=0)
Wait for I/O event(s).
Definition: ncbi_pipe.cpp:2043
EFinish
ExecWait return code.
Definition: ncbi_pipe.hpp:437
TProcessHandle GetProcessHandle(void) const
Get the process handle for the piped child.
Definition: ncbi_pipe.cpp:2130
unsigned int TChildPollMask
bitwise OR of "EChildIOHandle"
Definition: ncbi_pipe.hpp:124
static EFinish ExecWait(const string &cmd, const vector< string > &args, CNcbiIstream &in, CNcbiOstream &out, CNcbiOstream &err, int &exit_code, const string &current_dir=kEmptyStr, const char *const envp[]=0, IProcessWatcher *watcher=0, const STimeout *kill_timeout=0, size_t pipe_size=0)
Execute a command with a vector of arguments, and wait for its completion.
Definition: ncbi_pipe.cpp:2143
void OpenSelf(void)
Open the standard streams of the current process.
Definition: ncbi_pipe.cpp:1951
EIO_Status SetReadHandle(EChildIOHandle from_handle)
Set standard output handle to read data from.
Definition: ncbi_pipe.cpp:1994
virtual EAction OnStart(TProcessHandle)
This method is called when the process has just been started by the ExecWait() method.
Definition: ncbi_pipe.hpp:422
const STimeout * m_ReadTimeout
eIO_Read timeout
Definition: ncbi_pipe.hpp:509
size_t m_PipeSize
Pipe size.
Definition: ncbi_pipe.hpp:499
CPipe(size_t pipe_size=0)
Constructor.
Definition: ncbi_pipe.cpp:1891
STimeout m_CloseTimeoutValue
Storage for m_CloseTimeout.
Definition: ncbi_pipe.hpp:514
EIO_Status SetTimeout(EIO_Event event, const STimeout *timeout)
Specify timeout for the pipe I/O.
Definition: ncbi_pipe.cpp:2084
STimeout m_WriteTimeoutValue
Storage for m_WriteTimeout.
Definition: ncbi_pipe.hpp:513
CPipeHandle * m_PipeHandle
Internal OS-specific pipe handle.
Definition: ncbi_pipe.hpp:501
EIO_Status Read(void *buf, size_t count, size_t *read=0, EChildIOHandle from_handle=eDefault)
Read data from the pipe's default read handle.
Definition: ncbi_pipe.cpp:2005
STimeout m_ReadTimeoutValue
Storage for m_ReadTimeout.
Definition: ncbi_pipe.hpp:512
const STimeout * m_CloseTimeout
eIO_Close timeout
Definition: ncbi_pipe.hpp:511
unsigned int TCreateFlags
bitwise OR of "ECreateFlag"
Definition: ncbi_pipe.hpp:111
const STimeout * GetTimeout(EIO_Event event) const
Get pipe I/O timeout.
Definition: ncbi_pipe.cpp:2112
EIO_Status CloseHandle(EChildIOHandle handle)
Close the specified child's pipe handle (even for CPipe opened with OpenSelf()).
Definition: ncbi_pipe.cpp:1978
EIO_Status m_WriteStatus
Last write status.
Definition: ncbi_pipe.hpp:506
virtual const char * GetErrCodeString(void) const override
Translate from an error code value to its string representation.
Definition: ncbi_pipe.cpp:2298
EIO_Status Close(int *exitcode=0)
Close pipe.
Definition: ncbi_pipe.cpp:1968
const STimeout * m_WriteTimeout
eIO_Write timeout
Definition: ncbi_pipe.hpp:510
EIO_Status Status(EIO_Event direction) const
Return a status of the last I/O operation.
Definition: ncbi_pipe.cpp:2068
@ eDefault
see SetReadHandle()
Definition: ncbi_pipe.hpp:122
@ eStdOut
Definition: ncbi_pipe.hpp:120
@ eStdIn
Definition: ncbi_pipe.hpp:119
@ eStdErr
Definition: ncbi_pipe.hpp:121
@ fStdIn
Definition: ncbi_pipe.hpp:115
@ fDefault
see Wait()
Definition: ncbi_pipe.hpp:118
@ fStdOut
Definition: ncbi_pipe.hpp:116
@ fStdErr
Definition: ncbi_pipe.hpp:117
@ eDone
Process finished normally.
Definition: ncbi_pipe.hpp:438
@ eCanceled
Watcher requested to bail out.
Definition: ncbi_pipe.hpp:439
@ fKillOnClose
Close(): kill child process if it hasn't terminated within the allotted time.
Definition: ncbi_pipe.hpp:100
@ fNewGroup
UNIX: new process group will be created, and the child process will become the leader of the new proc...
Definition: ncbi_pipe.hpp:107
@ fKeepOnClose
Close(): just return eIO_Timeout if Close() cannot complete within the allotted time; don't close any...
Definition: ncbi_pipe.hpp:93
@ fSigPipe_Restore
Restore SIGPIPE processing for child process to system default.
Definition: ncbi_pipe.hpp:105
@ fStdErr_Open
Do open child's stderr.
Definition: ncbi_pipe.hpp:89
@ fStdIn_Close
Do not open child's stdin.
Definition: ncbi_pipe.hpp:86
@ fStdOut_Close
Do not open child's stdout.
Definition: ncbi_pipe.hpp:88
@ fStdErr_Share
Keep stderr (share it with child)
Definition: ncbi_pipe.hpp:91
@ fStdErr_StdOut
Redirect stderr to whatever stdout goes.
Definition: ncbi_pipe.hpp:92
@ eContinue
Continue running.
Definition: ncbi_pipe.hpp:408
@ eStop
Kill the child process and exit.
Definition: ncbi_pipe.hpp:409
@ eExit
Exit without waiting for the child process.
Definition: ncbi_pipe.hpp:410
bool IsPresent(void) const
TRUE if the object contains information about the process state.
bool KillGroup(unsigned long timeout=kDefaultKillTimeout) const
Terminate a group of processes.
TPid TProcessHandle
bool IsExited(void) const
TRUE if the process terminated normally.
int GetSignal(void) const
Get the signal number that has caused the process to terminate (UNIX only).
bool Kill(unsigned long timeout=kDefaultKillTimeout)
Terminate process.
int Wait(unsigned long timeout=kInfiniteTimeoutMs, CExitInfo *info=0) const
Wait until process terminates.
static const unsigned long kDefaultKillTimeout
Default wait time (milliseconds) between "soft" and "hard" attempts to terminate a process.
pid_t TPid
Process identifier (PID) and process handle.
bool IsAlive(void) const
TRUE if the process is still alive.
bool IsSignaled(void) const
TRUE if the process terminated by a signal (UNIX only).
int GetExitCode(void) const
Get process exit code.
@ ePid
A real process identifier (pid).
@ eHandle
A process handle (MS Windows).
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define NCBI_AS_STRING(value)
Convert some value to string even if this value is macro itself.
Definition: ncbistl.hpp:146
ESwitch SOCK_SetInterruptOnSignalAPI(ESwitch on_off)
Control restartability of I/O interrupted by signals.
Definition: ncbi_socket.c:8533
IO_PREFIX::ostream CNcbiOstream
Portable alias for ostream.
Definition: ncbistre.hpp:149
#define NcbiCout
Definition: ncbistre.hpp:543
IO_PREFIX::istream CNcbiIstream
Portable alias for istream.
Definition: ncbistre.hpp:146
static streamsize Readsome(CNcbiIstream &is, CT_CHAR_TYPE *buf, streamsize buf_size)
#define NcbiCerr
Definition: ncbistre.hpp:544
static int CompareNocase(const CTempString s1, SIZE_TYPE pos, SIZE_TYPE n, const char *s2)
Case-insensitive compare of a substring with another string.
Definition: ncbistr.cpp:219
char TXChar
Definition: ncbistr.hpp:172
#define NPOS
Definition: ncbistr.hpp:133
string TXString
Definition: ncbistr.hpp:173
static const string BoolToString(bool value)
Convert bool to string.
Definition: ncbistr.cpp:2806
#define _T_CSTRING(x)
Definition: ncbistr.hpp:182
#define _T_XCSTRING(x)
Definition: ncbistr.hpp:181
#define _TX(x)
Definition: ncbistr.hpp:176
#define _T_XSTRING(x)
Definition: ncbistr.hpp:179
static enable_if< is_arithmetic< TNumeric >::value||is_convertible< TNumeric, Int8 >::value, string >::type NumericToString(TNumeric value, TNumToStringFlags flags=0, int base=10)
Convert numeric value to string.
Definition: ncbistr.hpp:673
#define DEFINE_STATIC_FAST_MUTEX(id)
Define static fast mutex and initialize it.
Definition: ncbimtx.hpp:496
const long kMicroSecondsPerSecond
Number of microseconds in one second.
Definition: ncbitime.hpp:91
#define kInfiniteTimeout
Definition: ncbi_types.h:82
unsigned long NcbiTimeoutToMs(const STimeout *timeout)
Definition: ncbi_types.c:40
EIO_Status
I/O status.
Definition: ncbi_core.h:132
unsigned int usec
microseconds (modulo 1,000,000)
Definition: ncbi_types.h:78
const char * IO_StatusStr(EIO_Status status)
Get the text form of an enum status value.
Definition: ncbi_core.c:56
STimeout * NcbiMsToTimeout(STimeout *timeout, unsigned long ms)
Definition: ncbi_types.c:48
unsigned int sec
seconds
Definition: ncbi_types.h:77
const char * NcbiMessagePlusError(int *dynamic, const char *message, int error, const char *descr)
Add current "error" (and maybe its description) to the message: <message>[ {error=[[<error>][,...
Definition: ncbi_util.c:335
EIO_Event
I/O event (or direction).
Definition: ncbi_core.h:118
#define kDefaultTimeout
Definition: ncbi_types.h:81
@ eIO_Timeout
timeout expired before any I/O succeeded
Definition: ncbi_core.h:134
@ eIO_Interrupt
signal arrival prevented any I/O to succeed
Definition: ncbi_core.h:136
@ eIO_Success
everything is fine, no error occurred
Definition: ncbi_core.h:133
@ eIO_Unknown
unknown I/O error (likely fatal but can retry)
Definition: ncbi_core.h:139
@ eIO_InvalidArg
bad argument / parameter value(s) supplied
Definition: ncbi_core.h:137
@ eIO_Write
write
Definition: ncbi_core.h:121
@ eIO_ReadWrite
eIO_Read | eIO_Write (also, eCONN_OnFlush)
Definition: ncbi_core.h:122
@ eIO_Close
also serves as an error indicator in SOCK_Poll
Definition: ncbi_core.h:123
@ eIO_Read
read
Definition: ncbi_core.h:120
#define INVALID_HANDLE_VALUE
A value for an invalid file handle.
Definition: mdb.c:389
#define HANDLE
An abstraction for a file handle.
Definition: mdb.c:383
Definition of all error codes used in connect library (xconnect.lib, xconnext.lib etc).
FILE * file
char * buf
int i
yy_size_t n
int len
const struct ncbi::grid::netcache::search::fields::SIZE size
static const STimeout kZeroTimeout
static string x_FormatError(int error, const string &message)
Definition: ncbi_pipe.cpp:99
AutoPtr< char, CDeleter< char > > TTempCharPtr
Definition: ncbi_pipe.cpp:96
static string s_FormatErrorMessage(const string &where, const string &what)
Definition: ncbi_pipe.cpp:136
const unsigned long kWaitPrecision
Definition: ncbi_pipe.cpp:235
static const STimeout * s_SetTimeout(const STimeout *from, STimeout *to)
Definition: ncbi_pipe.cpp:85
#define IS_SET(flags, mask)
Definition: ncbi_pipe.cpp:70
static string x_GetHandleName(CPipe::EChildIOHandle handle)
Definition: ncbi_pipe.cpp:195
#define PIPE_THROW(err, errtxt)
Definition: ncbi_pipe.cpp:73
static bool x_IsDisconnectError(DWORD error)
Definition: ncbi_pipe.cpp:238
static string x_CommandLine(const string &cmd, const vector< string > &args)
Definition: ncbi_pipe.cpp:212
static EIO_Status s_Close(CProcess &process, CPipe::TCreateFlags flags, const STimeout *timeout, int *exitcode)
Definition: ncbi_pipe.cpp:142
Portable class to work with a spawned process via pipes.
Static variables safety - create on demand, destroy on application termination.
#define TRUE
bool replacment for C indicating true.
Definition: ncbi_std.h:97
void SleepMilliSec(unsigned long ml_sec, EInterruptOnSignal onsignal=eRestartOnSignal)
#define STDIN_FILENO
Definition: ncbicgi.cpp:61
#define STDOUT_FILENO
Definition: ncbicgir.cpp:48
int ssize_t
Definition: ncbiconf_msvc.h:93
Defines a portable execute class.
const char * command
T max(T x_, T y_)
std::istream & in(std::istream &in_, double &x_)
#define memmove(a, b, c)
#define count
char TCHAR
Definition: sqltypes.h:91
TCHAR * LPTSTR
Definition: sqltypes.h:111
unsigned int DWORD
Definition: sqltypes.h:98
Timeout structure.
Definition: ncbi_types.h:76
Definition: poll.h:52
int fd
Definition: poll.h:53
short revents
Definition: poll.h:55
#define _TROUBLE
#define _ASSERT
else result
Definition: token2.c:20
uchar inbuf[1000000]
Definition: unzcrash.c:40
Modified on Fri Sep 20 14:58:20 2024 by modify_doxy.py rev. 669887