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

Go to the SVN repository for this file.

1 /* $Id: ncbiexec.cpp 99267 2023-03-03 17:10:24Z ucko $
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: Vladimir Ivanov
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <corelib/ncbiexec.hpp>
34 #include <corelib/ncbifile.hpp>
35 #include <corelib/ncbi_system.hpp>
36 #include <corelib/error_codes.hpp>
37 #include "ncbisys.hpp"
38 
39 #if defined(NCBI_OS_MSWIN)
40 # include <process.h>
41 #elif defined(NCBI_OS_UNIX)
42 # include <unistd.h>
43 # include <errno.h>
44 # include <sys/types.h>
45 # include <sys/wait.h>
46 # include <fcntl.h>
47 #endif
48 
49 #define NCBI_USE_ERRCODE_X Corelib_System
50 
52 
53 
55 {
56  if ( (m_Flags & fExitCode) == 0 ) {
57  NCBI_THROW(CExecException, eResult,
58  "CExec:: CResult contains process handle, not exit code");
59  }
60  return m_Result.exitcode;
61 }
62 
64 {
65  if ( (m_Flags & fHandle) == 0 ) {
66  NCBI_THROW(CExecException, eResult,
67  "CExec:: CResult contains process exit code, not handle");
68  }
69  return m_Result.handle;
70 }
71 
72 CExec::CResult::operator intptr_t(void) const
73 {
74  switch (m_Flags) {
75  case fExitCode:
76  return (intptr_t)m_Result.exitcode;
77  case fHandle:
78  return (intptr_t)m_Result.handle;
79  default:
80  NCBI_THROW(CExecException, eResult,
81  "CExec:: CResult undefined conversion");
82  }
83  // Not reached
84  return 0;
85 }
86 
87 
88 #if defined(NCBI_OS_MSWIN)
89 
90 // Convert CExec class mode to the real mode
92 {
93  static const int s_Mode[] = {
94  P_OVERLAY, P_WAIT, P_NOWAIT, P_DETACH
95  };
96 
97  // Translate only master modes and ignore all additional modes on MS Windows.
98  int x_mode = (int) mode & CExec::fModeMask;
99  _ASSERT(0 <= x_mode && x_mode < sizeof(s_Mode)/sizeof(s_Mode[0]));
100  return s_Mode[x_mode];
101 }
102 #endif
103 
104 
105 #if defined(NCBI_OS_UNIX)
106 
107 // Type function to call
109 
110 static int
112  const char *cmdname, const char *const *argv,
113  const char *const *envp = (const char *const*)0)
114 {
115  // Empty environment for Spawn*E
116  const char* empty_env[] = { 0 };
117  if ( !envp ) {
118  envp = empty_env;
119  }
120 
121  // Get master mode
122  CExec::EMode mode = (CExec::EMode)(full_mode & (int)CExec::fModeMask);
123 
124  // Flush stdio
125  fflush(NULL);
126 
127  // Replace the current process image with a new process image.
128  if (mode == CExec::eOverlay) {
130  switch (func) {
131  case eV:
132  return execv(cmdname, const_cast<char**>(argv));
133  case eVP:
134  return execvp(cmdname, const_cast<char**>(argv));
135  case eVE:
136  case eVPE:
137  return execve(cmdname, const_cast<char**>(argv),
138  const_cast<char**>(envp));
139  }
140  return -1;
141  }
142 
143  // Create temporary pipe to get status of execution
144  // of the child process
145  int status_pipe[2];
146  if (pipe(status_pipe) < 0) {
147  NCBI_THROW(CExecException, eSpawn,
148  "CExec:: Failed to create status pipe");
149  }
150  fcntl(status_pipe[0], F_SETFL,
151  fcntl(status_pipe[0], F_GETFL, 0) & ~O_NONBLOCK);
152  fcntl(status_pipe[1], F_SETFD,
153  fcntl(status_pipe[1], F_GETFD, 0) | FD_CLOEXEC);
154 
155  // Fork child process
156  pid_t pid;
157  switch (pid = fork()) {
158  case (pid_t)(-1):
159  // fork failed
160  return -1;
161  case 0:
162  // Now we are in the child process
163 
164  // Close unused pipe handle
165  close(status_pipe[0]);
166 
167  if (mode == CExec::eDetach) {
168  if ( freopen("/dev/null", "r", stdin) ) { /*dummy*/ };
169  if ( freopen("/dev/null", "w", stdout) ) { /*dummy*/ };
170  if ( freopen("/dev/null", "a", stderr) ) { /*dummy*/ };
171  setsid();
172  }
173 
174  if (((int)full_mode & CExec::fNewGroup) == CExec::fNewGroup) {
175  setpgid(0, 0);
176  }
177  int status =-1;
178  switch (func) {
179  case eV:
180  status = execv(cmdname, const_cast<char**>(argv));
181  break;
182  case eVP:
183  status = execvp(cmdname, const_cast<char**>(argv));
184  break;
185  case eVE:
186  case eVPE:
187  status = execve(cmdname, const_cast<char**>(argv),
188  const_cast<char**>(envp));
189  break;
190  }
191  // Error executing exec*(), report error code to parent process
192  int errcode = errno;
193  if ( write(status_pipe[1], &errcode, sizeof(errcode)) ) { /*dummy*/};
194  close(status_pipe[1]);
195  _exit(status);
196  }
197 
198  // Check status pipe.
199  // If it have some data, this is an errno from the child process.
200  // If EOF in status pipe, that child executed successful.
201  // Retry if either blocked or interrupted
202 
203  close(status_pipe[1]);
204 
205  // Try to read errno from forked process
206  ssize_t n;
207  int errcode;
208  while ((n = read(status_pipe[0], &errcode, sizeof(errcode))) < 0) {
209  if (errno != EINTR)
210  break;
211  }
212  close(status_pipe[0]);
213  if (n > 0) {
214  // Child could not run -- reap it and exit with error
215  waitpid(pid, 0, 0);
216  errno = (size_t) n >= sizeof(errcode) ? errcode : 0;
217  return -1;
218  }
219 
220  // The "pid" contains the childs pid
221  if ( mode == CExec::eWait ) {
222  return CExec::Wait(pid);
223  }
224  return pid;
225 }
226 
227 #endif
228 
229 
230 string CExec::QuoteArg(const string& arg)
231 {
232  // Enclose argument in quotes if it is empty,
233  // or contains spaces and does not contain quotes.
234  if ( arg.empty() ||
235  (arg.find(' ') != NPOS && arg.find('"') == NPOS) ) {
236  return '"' + arg + '"';
237  }
238  return arg;
239 }
240 
241 
242 // More advanced quoting function for MS Windows.
243 // Produce string argument accepted by ::CreateProcess() and CExec::Spawn*().
244 //
245 // Used code by Daniel Colascione, Microsoft Corporation:
246 // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
247 
248 string s_QuoteSpawnArg(const string& arg)
249 {
250 #if defined(NCBI_OS_MSWIN)
251 
252  if (!arg.empty() && arg.find_first_of(" \t\n\v\"") == NPOS) {
253  return arg;
254  }
255  string s;
256  s.push_back('"');
257 
258  ITERATE(string, it, arg) {
259  unsigned int n_backslashes = 0;
260 
261  while (it != arg.end() && *it == '\\') {
262  ++it;
263  ++n_backslashes;
264  }
265  if (it == arg.end()) {
266  // Escape all backslashes, but let the terminating
267  // double quotation mark we add below be interpreted
268  // as a meta-character.
269  //
270  s.append(n_backslashes * 2, '\\');
271  break;
272 
273  } else if (*it == '"') {
274  // Escape all backslashes and the following
275  // double quotation mark.
276  //
277  s.append(n_backslashes * 2 + 1, '\\');
278  s.push_back(*it);
279  } else {
280  // Backslashes aren't special here.
281  //
282  s.append(n_backslashes, '\\');
283  s.push_back(*it);
284  }
285  }
286  s.push_back('"');
287  return s;
288 #else
289  return arg;
290 #endif
291 }
292 
293 
294 // On 64-bit platforms, check argument, passed into function with variable
295 // number of arguments, on possible using 0 instead NULL as last argument.
296 // Of course, the argument 'arg' can be aligned on segment boundary,
297 // when 4 low-order bytes are 0, but chance of this is very low.
298 // The function prints out a warning only in Debug mode.
299 #if defined(_DEBUG) && SIZEOF_VOIDP > SIZEOF_INT
300 static void s_CheckExecArg(const char* arg)
301 {
302 # if defined(WORDS_BIGENDIAN)
303  int lo = int(((Uint8)arg >> 32) & 0xffffffffU);
304  int hi = int((Uint8)arg & 0xffffffffU);
305 # else
306  int hi = int(((Uint8)arg >> 32) & 0xffffffffU);
307  int lo = int((Uint8)arg & 0xffffffffU);
308 # endif
309  if (lo == 0 && hi != 0) {
310  ERR_POST_X(10, Warning <<
311  "It is possible that you used 0 instead of NULL "
312  "to terminate the argument list of a CExec::Spawn*() call.");
313  }
314 }
315 #else
316 # define s_CheckExecArg(x)
317 #endif
318 
319 // Macros to get exec arguments
320 
323 
324 #if defined(NCBI_OS_MSWIN)
325 
327  vector<TXString>& xargs, TXArgsOrEnv& t_args,
328  va_list& begin, const char* cmdname, const char* argv)
329 {
330  // Count arguments to allocate memory
331  va_list v_args = begin;
332  size_t xcnt = 2;
333  while ( va_arg(v_args, const char*) ) {
334  xcnt++;
335  }
336  const TXChar **args = new const TXChar*[xcnt+1];
337  if ( !args ) {
338  NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
339  }
340  t_args = args;
341 
342  // Use temporary vector to store quoted/unicoded strings.
343  xargs.push_back( _T_XSTRING(CExec::QuoteArg(cmdname)) );
344  xargs.push_back( _T_XSTRING(s_QuoteSpawnArg(argv)) );
345  // Repeat for each argument in the variable list
346  v_args = begin;
347  for (size_t i=2; i < xcnt; ++i) {
348  xargs.push_back( _T_XSTRING(s_QuoteSpawnArg(va_arg(v_args, const char*))) );
349  }
350  // Prepare array of char* arguments for execution
351  for (size_t i=0; i < xargs.size(); ++i) {
352  args[i] = xargs[i].c_str();
353  }
354  args[xcnt] = NULL;
355  va_arg(v_args, const char**);
356  begin = v_args;
357 }
358 
360  vector<TXString>& xargs, TXArgsOrEnv& t_args, const char* cmdname, const char** argv)
361 {
362  // Count arguments to allocate memory
363  const char** p = argv;
364  size_t xcnt = 1;
365  while ( *(++p) ) {
366  xcnt++;
367  }
368  const TXChar **args = new const TXChar*[xcnt+1];
369  if ( !args ) {
370  NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
371  }
372  t_args = args;
373 
374  // Use temporary vector to store quoted/unicoded strings.
375  xargs.push_back( _T_XSTRING(CExec::QuoteArg(cmdname)) );
376  // Repeat for each argument in the array
377  p = argv;
378  for (size_t i=1; i < xcnt; ++i) {
379  xargs.push_back( _T_XSTRING(s_QuoteSpawnArg(*(++p))) );
380  }
381  // Prepare array of char* arguments for execution
382  for (size_t i=0; i < xargs.size(); ++i) {
383  args[i] = xargs[i].c_str();
384  }
385  args[xcnt] = NULL;
386 }
387 
389  vector<TXString>& xargs, TXArgsOrEnv& t_args, const char** begin)
390 {
391  const char** envp = begin;
392  int xcnt = 0;
393  while ( *(envp++) ) {
394  xcnt++;
395  }
396  const TXChar **args = new const TXChar*[xcnt+1];
397  if ( !args ) {
398  NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
399  }
400  t_args = args;
401 
402  envp = begin;
403  int i_arg=0;
404  while ( i_arg < xcnt ) {
405 # if defined(_UNICODE)
406  xargs.push_back( _T_XSTRING(*(envp++)) );
407 # else
408  args[i_arg] = *(envp++);
409 # endif
410  ++i_arg;
411  }
412 # if defined(_UNICODE)
413  for (size_t i=0; i < xargs.size(); ++i) {
414  args[i] = xargs[i].c_str();
415  }
416 # endif
417  args[i_arg++] = NULL;
418 }
419 #endif //NCBI_OS_MSWIN
420 
421 
422 #if defined(NCBI_OS_MSWIN)
423 #define XGET_EXEC_ARGS(name, ptr) \
424  const TXChar* const *a_##name; \
425  vector<TXString> x_##name; \
426  TXArgsOrEnv t_##name; \
427  va_list vargs; \
428  va_start(vargs, ptr); \
429  s_Create_Args_L(x_##name, t_##name, vargs, cmdname, ptr); \
430  a_##name = t_##name.get();
431 #else
432 #define XGET_EXEC_ARGS(name, ptr) \
433  int xcnt = 2; \
434  va_list vargs; \
435  va_start(vargs, ptr); \
436  while ( va_arg(vargs, const char*) ) xcnt++; \
437  va_end(vargs); \
438  const char ** a_##name = new const char*[xcnt+1]; \
439  if ( !a_##name ) \
440  NCBI_THROW(CCoreException, eNullPtr, kEmptyStr); \
441  TXArgsOrEnv t_##name(a_##name); \
442  a_##name[0] = cmdname; \
443  a_##name[1] = ptr; \
444  va_start(vargs, ptr); \
445  int xi = 1; \
446  while ( xi < xcnt ) { \
447  xi++; \
448  a_##name[xi] = va_arg(vargs, const char*); \
449  s_CheckExecArg(a_##name[xi]); \
450  } \
451  a_##name[xi] = (const char*)0
452 #endif
453 
454 #if defined(NCBI_OS_MSWIN)
455 # define XGET_PTR_ARGS(name, ptr) \
456  const TXChar* const *a_##name; \
457  vector<TXString> x_##name; \
458  TXArgsOrEnv t_##name; \
459  s_Create_Args_V(x_##name, t_##name, cmdname, (const char**)ptr); \
460  a_##name = t_##name.get();
461 # define XGET_PTR_ENVP(name, ptr) \
462  const TXChar* const *a_##name; \
463  vector<TXString> x_##name; \
464  TXArgsOrEnv t_##name; \
465  s_Create_Env(x_##name, t_##name, (const char**)ptr); \
466  a_##name = t_##name.get();
467 #else
468 # define XGET_PTR_ARGS(name, ptr) \
469  const char* const *a_##name = ptr; \
470  char** xptr = const_cast<char**>(ptr); \
471  xptr[0] = const_cast<char*>(cmdname);
472 # define XGET_PTR_ENVP(name, ptr) \
473  const char* const *a_##name = ptr;
474 #endif
475 
476 #if defined(NCBI_OS_MSWIN) && defined(_UNICODE)
477 # define XGET_EXEC_ENVP(name) \
478  const TXChar* const *a_##name; \
479  vector<TXString> x_##name; \
480  TXArgsOrEnv t_##name; \
481  s_Create_Env(x_##name, t_##name, va_arg(vargs, const char**)); \
482  a_##name = t_##name.get();
483 #else
484 # define XGET_EXEC_ENVP(name) \
485  const char * const * a_##name = va_arg(vargs, const char**);
486 #endif
487 
488 
489 // Return result from Spawn method
490 #define RETURN_RESULT(func) \
491  if (status == -1) { \
492  NCBI_THROW(CExecException, eSpawn, "CExec::" #func "() failed"); \
493  } \
494  CResult result; \
495  if ((mode & static_cast<EMode>(fModeMask)) == eWait) { \
496  result.m_Flags = CResult::fExitCode; \
497  result.m_Result.exitcode = (TExitCode)status; \
498  } else { \
499  result.m_Flags = CResult::fHandle; \
500  result.m_Result.handle = (TProcessHandle)status; \
501  } \
502  return result
503 
504 
505 TExitCode CExec::System(const char *cmdline)
506 {
507  int status;
508 #if defined(NCBI_OS_MSWIN)
509  _flushall();
510  status = NcbiSys_system(cmdline ? _T_XCSTRING(cmdline) : NULL);
511 #elif defined(NCBI_OS_UNIX)
512  status = system(cmdline);
513 #endif
514  if (status == -1) {
515  NCBI_THROW(CExecException, eSystem,
516  "CExec::System: call to system failed");
517  }
518 #if defined(NCBI_OS_UNIX)
519  if (cmdline) {
520  return WIFSIGNALED(status) ? WTERMSIG(status) + 0x80
521  : WEXITSTATUS(status);
522  } else {
523  return status;
524  }
525 #else
526  return status;
527 #endif
528 }
529 
530 
532 CExec::SpawnL(EMode mode, const char *cmdname, const char *argv, ...)
533 {
534  intptr_t status;
535  XGET_EXEC_ARGS(args, argv);
536 
537 #if defined(NCBI_OS_MSWIN)
538  _flushall();
539  int realmode = s_GetRealMode(mode);
540  if (realmode == P_OVERLAY) {
542  }
543  status = NcbiSys_spawnv(realmode, _T_XCSTRING(cmdname), a_args);
544 #elif defined(NCBI_OS_UNIX)
545  status = s_SpawnUnix(eV, mode, cmdname, a_args);
546 #endif
548 }
549 
550 
552 CExec::SpawnLE(EMode mode, const char *cmdname, const char *argv, ...)
553 {
554  intptr_t status;
555  XGET_EXEC_ARGS(args, argv);
556  XGET_EXEC_ENVP(envs);
557 
558 #if defined(NCBI_OS_MSWIN)
559  _flushall();
560  int realmode = s_GetRealMode(mode);
561  if (realmode == P_OVERLAY) {
563  }
564  status = NcbiSys_spawnve(realmode, _T_XCSTRING(cmdname), a_args, a_envs);
565 #elif defined(NCBI_OS_UNIX)
566  status = s_SpawnUnix(eVE, mode, cmdname, a_args, a_envs);
567 #endif
569 }
570 
571 
573 CExec::SpawnLP(EMode mode, const char *cmdname, const char *argv, ...)
574 {
575  intptr_t status;
576  XGET_EXEC_ARGS(args, argv);
577 
578 #if defined(NCBI_OS_MSWIN)
579  _flushall();
580  int realmode = s_GetRealMode(mode);
581  if (realmode == P_OVERLAY) {
583  }
584  status = NcbiSys_spawnvp(realmode, _T_XCSTRING(cmdname), a_args);
585 #elif defined(NCBI_OS_UNIX)
586  status = s_SpawnUnix(eVP, mode, cmdname, a_args);
587 #endif
589 }
590 
591 
593 CExec::SpawnLPE(EMode mode, const char *cmdname, const char *argv, ...)
594 {
595  intptr_t status;
596  XGET_EXEC_ARGS(args, argv);
597  XGET_EXEC_ENVP(envs);
598 
599 #if defined(NCBI_OS_MSWIN)
600  _flushall();
601  int realmode = s_GetRealMode(mode);
602  if (realmode == P_OVERLAY) {
604  }
605  status = NcbiSys_spawnvpe(realmode, _T_XCSTRING(cmdname), a_args, a_envs);
606 #elif defined(NCBI_OS_UNIX)
607  status = s_SpawnUnix(eVPE, mode, cmdname, a_args, a_envs);
608 #endif
610 }
611 
612 
614 CExec::SpawnV(EMode mode, const char *cmdname, const char *const *argv)
615 {
616  intptr_t status;
617  XGET_PTR_ARGS(args, argv);
618 
619 #if defined(NCBI_OS_MSWIN)
620  _flushall();
621  int realmode = s_GetRealMode(mode);
622  if (realmode == P_OVERLAY) {
624  }
625  status = NcbiSys_spawnv(realmode, _T_XCSTRING(cmdname), a_args);
626 #elif defined(NCBI_OS_UNIX)
627  status = s_SpawnUnix(eV, mode, cmdname, a_args);
628 #endif
630 }
631 
632 
634 CExec::SpawnVE(EMode mode, const char *cmdname,
635  const char *const *argv, const char * const *envp)
636 {
637  intptr_t status;
638  XGET_PTR_ARGS(args, argv);
639  XGET_PTR_ENVP(envs, envp);
640 
641 #if defined(NCBI_OS_MSWIN)
642  _flushall();
643  int realmode = s_GetRealMode(mode);
644  if (realmode == P_OVERLAY) {
646  }
647  status = NcbiSys_spawnve(realmode, _T_XCSTRING(cmdname), a_args, a_envs);
648 #elif defined(NCBI_OS_UNIX)
649  status = s_SpawnUnix(eVE, mode, cmdname, a_args, a_envs);
650 #endif
652 }
653 
654 
656 CExec::SpawnVP(EMode mode, const char *cmdname, const char *const *argv)
657 {
658  intptr_t status;
659  XGET_PTR_ARGS(args, argv);
660 
661 #if defined(NCBI_OS_MSWIN)
662  _flushall();
663  int realmode = s_GetRealMode(mode);
664  if (realmode == P_OVERLAY) {
666  }
667  status = NcbiSys_spawnvp(realmode, _T_XCSTRING(cmdname), a_args);
668 #elif defined(NCBI_OS_UNIX)
669  status = s_SpawnUnix(eVP, mode, cmdname, a_args);
670 #endif
672 }
673 
674 
676 CExec::SpawnVPE(EMode mode, const char *cmdname,
677  const char *const *argv, const char * const *envp)
678 {
679  intptr_t status;
680  XGET_PTR_ARGS(args, argv);
681  XGET_PTR_ENVP(envs, envp);
682 
683 #if defined(NCBI_OS_MSWIN)
684  _flushall();
685  int realmode = s_GetRealMode(mode);
686  if (realmode == P_OVERLAY) {
688  }
689  status = NcbiSys_spawnvpe(realmode, _T_XCSTRING(cmdname), a_args, a_envs);
690 #elif defined(NCBI_OS_UNIX)
691  status = s_SpawnUnix(eVPE, mode, cmdname, a_args, a_envs);
692 #endif
694 }
695 
696 
697 TExitCode CExec::Wait(TProcessHandle handle, unsigned long timeout)
698 {
699  return CProcess(handle, CProcess::eHandle).Wait(timeout);
700 }
701 
702 
703 // Predefined timeout (in milliseconds)
704 const unsigned long kWaitPrecision = 100;
705 
706 int CExec::Wait(list<TProcessHandle>& handles,
707  EWaitMode mode,
708  list<CResult>& result,
709  unsigned long timeout)
710 {
711  typedef list<TProcessHandle>::iterator THandleIt;
712  result.clear();
713 
714  for (;;) {
715  // Check each process
716  for (THandleIt it = handles.begin(); it != handles.end(); ) {
717  TProcessHandle handle = *it;
718  TExitCode exitcode = Wait(handle, 0);
719  if ( exitcode != -1 ) {
720  CResult res;
721  res.m_Flags = CResult::fBoth;
722  res.m_Result.handle = handle;
723  res.m_Result.exitcode = exitcode;
724  result.push_back(res);
725  THandleIt cur = it;
726  ++it;
727  handles.erase(cur);
728  } else {
729  ++it;
730  }
731  }
732  if ( (mode == eWaitAny && !result.empty()) ||
733  (mode == eWaitAll && handles.empty()) ) {
734  break;
735  }
736  // Wait before next loop
737  unsigned long x_sleep = kWaitPrecision;
738  if (timeout != kInfiniteTimeoutMs) {
739  if (x_sleep > timeout) {
740  x_sleep = timeout;
741  }
742  if ( !x_sleep ) {
743  break;
744  }
745  timeout -= x_sleep;
746  }
747  SleepMilliSec(x_sleep);
748  }
749  // Return number of terminated processes
750  return (int)result.size();
751 }
752 
753 
755  const char *argv, ... /*, NULL */)
756 {
757  intptr_t status = -1;
758 
759 #if defined(NCBI_OS_MSWIN)
760 
761 # if defined(NCBI_COMPILER_MSVC)
762  // This is Microsoft extension, and some compilers do not have it.
763  _flushall();
764 # endif
765  STARTUPINFO StartupInfo;
766  PROCESS_INFORMATION ProcessInfo;
767  const int kMaxCmdLength = 4096;
768  TXString cmdline;
769 
770  // Set startup info
771  memset(&StartupInfo, 0, sizeof(StartupInfo));
772  StartupInfo.cb = sizeof(STARTUPINFOA);
773  StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
774  StartupInfo.wShowWindow = SW_HIDE;
775  DWORD dwCreateFlags = (mode == eDetach) ?
776  DETACHED_PROCESS : CREATE_NEW_CONSOLE;
777 # if defined(_UNICODE)
778  dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
779 # endif
780 
781  // Compose command line
782  cmdline.reserve(kMaxCmdLength);
783  cmdline = _T_XCSTRING(CExec::QuoteArg(cmdname));
784 
785  if (argv) {
786  cmdline += _TX(" ");
787  cmdline += _T_XCSTRING(argv);
788  va_list vargs;
789  va_start(vargs, argv);
790  const char* p = NULL;
791  while ( (p = va_arg(vargs, const char*)) ) {
792  cmdline += _TX(" ");
793  cmdline += _T_XSTRING(s_QuoteSpawnArg(p));
794  }
795  va_end(vargs);
796  }
797 
798  // MS Windows: ignore all extra flags.
799  mode = EMode(mode & fModeMask);
800 
801  // Just check mode parameter
803 
804  // Run program
805  if (CreateProcess(NULL, (LPTSTR)cmdline.c_str(), NULL, NULL, FALSE,
806  dwCreateFlags, NULL, NULL, &StartupInfo, &ProcessInfo))
807  {
808  if (mode == eOverlay) {
810  _exit(0);
811  }
812  else if (mode == eWait) {
813  // wait running process
814  WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
815  DWORD exitcode = -1;
816  GetExitCodeProcess(ProcessInfo.hProcess, &exitcode);
817  status = exitcode;
818  CloseHandle(ProcessInfo.hProcess);
819  }
820  else if (mode == eDetach) {
821  // detached asynchronous spawn,
822  // just close process handle, return 0 for success
823  CloseHandle(ProcessInfo.hProcess);
824  status = 0;
825  }
826  else if (mode == eNoWait) {
827  // asynchronous spawn -- return PID
828  status = (intptr_t)ProcessInfo.hProcess;
829  }
830  CloseHandle(ProcessInfo.hThread);
831  }
832 
833 #elif defined(NCBI_OS_UNIX)
834  XGET_EXEC_ARGS(args, argv);
835  status = s_SpawnUnix(eV, mode, cmdname, a_args);
836 #endif
837 
839 }
840 
841 
842 bool CExec::IsExecutable(const string& path)
843 {
844  CFile f(path);
845  if (f.Exists() &&
846  f.CheckAccess(CFile::fExecute)) {
847  return true;
848  }
849  return false;
850 }
851 
852 
853 string CExec::ResolvePath(const string& filename)
854 {
855  string path = kEmptyStr;
856 
857  // Absolute path
858  if ( CDirEntry::IsAbsolutePath(filename) ) {
859  if ( IsExecutable(filename) ) {
860  path = filename;
861  }
862  } else {
863 
864  // Relative path
865 
866  string tmp = filename;
867 # ifdef NCBI_OS_MSWIN
868  // Add default ".exe" extention to the name of executable file
869  // if it running without extension
870  string dir, title, ext;
871  CDirEntry::SplitPath(filename, &dir, &title, &ext);
872  if ( ext.empty() ) {
873  tmp = CDirEntry::MakePath(dir, title, "exe");
874  }
875 # endif
876  // Check on relative path with sub-directories,
877  // ignore such filenames.
878  size_t sep = tmp.find_first_of("/\\");
879  if ( sep == NPOS ) {
880  // The path looks like "filename".
881  // The behavior for such executables are different on Unix and Windows.
882  // Unix always use PATH env.variable to find it, Windows try
883  // current directory first.
884 # ifdef NCBI_OS_MSWIN
885  if ( CFile(tmp).Exists() ) {
886  // File in the current directory
888  if ( IsExecutable(tmp) ) {
889  path = tmp;
890  }
891  }
892 # endif
893  // Try to find filename among the paths of the PATH
894  // environment variable.
895  if ( path.empty() ) {
896  const TXChar* env = NcbiSys_getenv(_TX("PATH"));
897  if (env && *env) {
898  list<string> split_path;
899 # ifdef NCBI_OS_MSWIN
900  NStr::Split(_T_STDSTRING(env), ";", split_path,
902 # else
903  NStr::Split(env, ":", split_path,
905 # endif
906  ITERATE(list<string>, it, split_path) {
907  string p = CDirEntry::MakePath(*it, tmp);
908  if (CFile(p).Exists() && IsExecutable(p)) {
909  path = p;
910  break;
911  }
912  } /* ITERATE */
913  } /* env */
914  } /* path.empty() */
915  } /* sep == NPOS */
916 
917  if ( path.empty() && CFile(tmp).Exists() ) {
918  // Relative path from the current directory
920  if ( IsExecutable(tmp) ) {
921  path = tmp;
922  }
923  }
924  }
925 
926  // If found - normalize path
927  if ( !path.empty() ) {
928  path = CDirEntry::NormalizePath(path);
929  }
930  return path;
931 }
932 
933 
934 const char* CExecException::GetErrCodeString(void) const
935 {
936  switch (GetErrCode()) {
937  case eSystem: return "eSystem";
938  case eSpawn: return "eSpawn";
939  case eResult: return "eResult";
940  default: return CException::GetErrCodeString();
941  }
942 }
943 
AutoPtr –.
Definition: ncbimisc.hpp:401
CCoreException –.
Definition: ncbiexpt.hpp:1476
CExecException –.
Definition: ncbiexec.hpp:607
The result type for Spawn methods.
Definition: ncbiexec.hpp:120
CFile –.
Definition: ncbifile.hpp:1604
CProcess –.
int close(int fd)
Definition: connection.cpp:45
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NULL
Definition: ncbistd.hpp:225
CDiagContext & GetDiagContext(void)
Get diag context instance.
Definition: logging.cpp:818
#define ERR_POST_X(err_subcode, message)
Error posting with default error code and given error subcode.
Definition: ncbidiag.hpp:550
void PrintStop(void)
Print exit message.
Definition: ncbidiag.cpp:2139
#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 Warning(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1191
virtual const char * GetErrCodeString(void) const
Get error code interpreted as text.
Definition: ncbiexpt.cpp:444
static TExitCode System(const char *cmdline)
Execute the specified command.
Definition: ncbiexec.cpp:505
static CResult RunSilent(EMode mode, const char *cmdname, const char *argv,...)
Run console application in invisible mode.
Definition: ncbiexec.cpp:754
static bool IsExecutable(const string &path)
Check executable permissions for specified file.
Definition: ncbiexec.cpp:842
static CResult SpawnLPE(EMode mode, const char *cmdname, const char *argv,...)
Spawn a new process with specified command-line arguments, environment settings and find file to exec...
Definition: ncbiexec.cpp:593
int TExitCode
Exit code type.
Definition: ncbiexec.hpp:51
struct CExec::CResult::@98 m_Result
Result of Spawn*() methods.
TExitCode GetExitCode(void)
Get exit code.
Definition: ncbiexec.cpp:54
virtual const char * GetErrCodeString(void) const override
Translate from the error code value to its string representation.
Definition: ncbiexec.cpp:934
TProcessHandle handle
Definition: ncbiexec.hpp:142
EMode
Which exec mode the spawned process is called with.
Definition: ncbiexec.hpp:81
static string ResolvePath(const string &filename)
Find executable file.
Definition: ncbiexec.cpp:853
static CResult SpawnL(EMode mode, const char *cmdname, const char *argv,...)
Spawn a new process with specified command-line arguments.
Definition: ncbiexec.cpp:532
TExitCode exitcode
Definition: ncbiexec.hpp:141
static string QuoteArg(const string &arg)
Quote argument.
Definition: ncbiexec.cpp:230
TProcessHandle GetProcessHandle(void)
Get process handle/pid.
Definition: ncbiexec.cpp:63
static CResult SpawnLP(EMode mode, const char *cmdname, const char *argv,...)
Spawn a new process with variable number of command-line arguments and find file to execute from the ...
Definition: ncbiexec.cpp:573
static TExitCode Wait(TProcessHandle handle, unsigned long timeout=kInfiniteTimeoutMs)
Wait until specified process terminates.
Definition: ncbiexec.cpp:697
static CResult SpawnVPE(EMode mode, const char *cmdname, const char *const *argv, const char *const *envp)
Spawn a new process with variable number of command-line arguments and specified environment settings...
Definition: ncbiexec.cpp:676
static CResult SpawnV(EMode mode, const char *cmdname, const char *const *argv)
Spawn a new process with variable number of command-line arguments.
Definition: ncbiexec.cpp:614
static CResult SpawnVP(EMode mode, const char *cmdname, const char *const *argv)
Spawn a new process with variable number of command-line arguments and find file to execute from the ...
Definition: ncbiexec.cpp:656
static CResult SpawnLE(EMode mode, const char *cmdname, const char *argv,...)
Spawn a new process with specified command-line arguments and environment settings.
Definition: ncbiexec.cpp:552
EWaitMode
Mode used to wait processes termination.
Definition: ncbiexec.hpp:495
static CResult SpawnVE(EMode mode, const char *cmdname, const char *const *argv, const char *const *envp)
Spawn a new process with variable number of command-line arguments and specified environment settings...
Definition: ncbiexec.cpp:634
TFlags m_Flags
What m_Result stores.
Definition: ncbiexec.hpp:144
@ fModeMask
Mask for all master modes, all EModeFlags must be above it.
Definition: ncbiexec.hpp:77
@ fNewGroup
After fork() move a process to new group (assign new PGID).
Definition: ncbiexec.hpp:75
@ eWait
Suspends calling thread until execution of new process is complete (synchronous operation).
Definition: ncbiexec.hpp:87
@ eDetach
Like eNoWait, continues to execute calling process; new process is run in background with no access t...
Definition: ncbiexec.hpp:107
@ eOverlay
Overlays calling process with new process, destroying calling process.
Definition: ncbiexec.hpp:84
@ eNoWait
Continues to execute calling process concurrently with new process (asynchronous process).
Definition: ncbiexec.hpp:96
@ eWaitAny
Wait any process to terminate.
Definition: ncbiexec.hpp:496
@ eWaitAll
Wait all processes to terminate.
Definition: ncbiexec.hpp:497
static string NormalizePath(const string &path, EFollowLinks follow_links=eIgnoreLinks)
Normalize a path.
Definition: ncbifile.cpp:820
static bool IsAbsolutePath(const string &path)
Check if a "path" is absolute for the current OS.
Definition: ncbifile.cpp:508
static string MakePath(const string &dir=kEmptyStr, const string &base=kEmptyStr, const string &ext=kEmptyStr)
Assemble a path from basic components.
Definition: ncbifile.cpp:413
static char GetPathSeparator(void)
Get path separator symbol specific for the current platform.
Definition: ncbifile.cpp:433
static string GetCwd(void)
Get the current working directory.
Definition: ncbifile.cpp:3708
static void SplitPath(const string &path, string *dir=0, string *base=0, string *ext=0)
Split a path string into its basic components.
Definition: ncbifile.cpp:358
@ fExecute
Execute / List(directory) permission.
Definition: ncbifile.hpp:1151
int intptr_t
Definition: ncbitype.h:185
uint64_t Uint8
8-byte (64-bit) unsigned integer
Definition: ncbitype.h:105
const unsigned long kInfiniteTimeoutMs
Infinite timeout in milliseconds.
TPid TProcessHandle
int Wait(unsigned long timeout=kInfiniteTimeoutMs, CExitInfo *info=0) const
Wait until process terminates.
@ 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 kEmptyStr
Definition: ncbistr.hpp:123
static list< string > & Split(const CTempString str, const CTempString delim, list< string > &arr, TSplitFlags flags=0, vector< SIZE_TYPE > *token_pos=NULL)
Split a string using specified delimiters.
Definition: ncbistr.cpp:3457
char TXChar
Definition: ncbistr.hpp:172
#define NPOS
Definition: ncbistr.hpp:133
string TXString
Definition: ncbistr.hpp:173
#define _T_STDSTRING(x)
Definition: ncbistr.hpp:180
#define _T_XCSTRING(x)
Definition: ncbistr.hpp:181
#define _TX(x)
Definition: ncbistr.hpp:176
#define _T_XSTRING(x)
Definition: ncbistr.hpp:179
@ fSplit_Truncate
Definition: ncbistr.hpp:2501
@ fSplit_MergeDelimiters
Merge adjacent delimiters.
Definition: ncbistr.hpp:2498
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
Definition of all error codes used in corelib (xncbi.lib).
int i
yy_size_t n
mdb_mode_t mode
Definition: lmdb++.h:38
void SleepMilliSec(unsigned long ml_sec, EInterruptOnSignal onsignal=eRestartOnSignal)
int ssize_t
Definition: ncbiconf_msvc.h:92
void s_Create_Args_L(vector< TXString > &xargs, TXArgsOrEnv &t_args, va_list &begin, const char *cmdname, const char *argv)
Definition: ncbiexec.cpp:326
void s_Create_Env(vector< TXString > &xargs, TXArgsOrEnv &t_args, const char **begin)
Definition: ncbiexec.cpp:388
string s_QuoteSpawnArg(const string &arg)
Definition: ncbiexec.cpp:248
static int s_SpawnUnix(ESpawnFunc func, CExec::EMode full_mode, const char *cmdname, const char *const *argv, const char *const *envp=(const char *const *) 0)
Definition: ncbiexec.cpp:111
#define XGET_EXEC_ARGS(name, ptr)
Definition: ncbiexec.cpp:423
static int s_GetRealMode(CExec::EMode mode)
Definition: ncbiexec.cpp:91
void s_Create_Args_V(vector< TXString > &xargs, TXArgsOrEnv &t_args, const char *cmdname, const char **argv)
Definition: ncbiexec.cpp:359
#define XGET_PTR_ENVP(name, ptr)
Definition: ncbiexec.cpp:461
AutoPtr< const TXChar *, TXArgsDeleter > TXArgsOrEnv
Definition: ncbiexec.cpp:322
#define RETURN_RESULT(func)
Definition: ncbiexec.cpp:490
#define XGET_PTR_ARGS(name, ptr)
Definition: ncbiexec.cpp:455
#define s_CheckExecArg(x)
Definition: ncbiexec.cpp:316
const unsigned long kWaitPrecision
Definition: ncbiexec.cpp:704
ESpawnFunc
Definition: ncbiexec.cpp:108
@ eVE
Definition: ncbiexec.cpp:108
@ eV
Definition: ncbiexec.cpp:108
@ eVP
Definition: ncbiexec.cpp:108
@ eVPE
Definition: ncbiexec.cpp:108
#define XGET_EXEC_ENVP(name)
Definition: ncbiexec.cpp:484
ArrayDeleter< const TXChar * > TXArgsDeleter
Definition: ncbiexec.cpp:321
Defines a portable execute class.
Defines classes: CDirEntry, CFile, CDir, CSymLink, CMemoryFile, CFileUtil, CFileLock,...
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
static char tmp[2048]
Definition: utf8.c:42
TCHAR * LPTSTR
Definition: sqltypes.h:111
unsigned int DWORD
Definition: sqltypes.h:98
#define NcbiSys_spawnvp
Definition: ncbisys.hpp:99
#define NcbiSys_system
Definition: ncbisys.hpp:107
#define NcbiSys_getenv
Definition: ncbisys.hpp:90
#define NcbiSys_spawnve
#define NcbiSys_spawnvpe
Definition: ncbisys.hpp:101
#define NcbiSys_spawnv
Definition: ncbisys.hpp:97
Functor template for deleting array of objects.
Definition: ncbimisc.hpp:358
#define _ASSERT
@ FALSE
Definition: testodbc.c:27
else result
Definition: token2.c:20
static HENV env
Definition: transaction2.c:38
Modified on Tue Nov 28 02:21:51 2023 by modify_doxy.py rev. 669887