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