Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
process.cpp
Go to the documentation of this file.
1
27// expand _TEST_EXPORT macros
28#define _RWSTD_TEST_SRC
29
30#include <ctype.h> // for isspace()
31#include <errno.h> // for EACCES, errno
32#include <signal.h> // for SIGXXX, kill()
33#include <stddef.h> // for size_t
34#include <stdarg.h> // for va_copy, va_list, ...
35#include <stdlib.h> // for free(), exit()
36#include <string.h> // for strchr()
37
38#include <sys/types.h> // for pid_t
39
40#include <rw_process.h>
41#include <rw_driver.h> // for rw_note(), ...
42#include <rw_printf.h> // for rw_fprintf()
43
44#ifdef __CYGWIN__
45// use the Windows API on Cygwin
46# define _WIN32
47#endif
48
49#ifndef E2BIG
50# define E2BIG 7 /* AIX, HP-UX, Linux, Solaris */
51#endif
52
53#ifndef EINTR
54# define EINTR 4
55#endif
56
57#ifndef SIGCHLD
58# if defined (_RWSTD_OS_AIX) || defined (_RWSTD_OS_OSF)
59 // AIX, Tru64
60# define SIGCHLD 20
61# elif defined (_RWSTD_OS_LINUX)
62# define SIGCHLD 17
63# else
64 // (System V-based) HP-UX, IRIX, and Solaris
65# define SIGCHLD 18
66# endif
67#endif
68
69// all known Unices
70#ifndef SIGALRM
71# define SIGALRM 14
72#endif
73
74#ifndef SIGHUP
75# define SIGHUP 1
76#endif
77
78#ifndef SIGQUIT
79# define SIGQUIT 3
80#endif
81
82#ifndef SIGKILL
83# define SIGKILL 9
84#endif
85
86
87#ifdef _RWSTD_EDG_ECCP
88
89extern "C" {
90
91int kill (pid_t, int);
92
93} // extern "C"
94
95#endif // _RWSTD_EDG_ECCP
96
97
100rw_vasnprintf (char**, size_t*, const char*, va_list);
101
102
104#ifdef _WIN32
105
106# include <windows.h> // for WaitForSingleObject(), ...
107
108static int
109_rw_map_errno (DWORD err)
110{
111 if (ERROR_WRITE_PROTECT <= err && ERROR_SHARING_BUFFER_EXCEEDED >= err)
112 return EACCES;
113
114 if ( ERROR_INVALID_STARTING_CODESEG <= err
115 && ERROR_INFLOOP_IN_RELOC_CHAIN >= err) {
116
117 return ENOEXEC;
118 }
119
120 switch (err)
121 {
122 case ERROR_FILE_NOT_FOUND:
123 case ERROR_PATH_NOT_FOUND:
124 case ERROR_INVALID_DRIVE:
125 case ERROR_NO_MORE_FILES:
126 case ERROR_BAD_NETPATH:
127 case ERROR_BAD_NET_NAME:
128 case ERROR_INVALID_NAME:
129 case ERROR_BAD_PATHNAME:
130 case ERROR_FILENAME_EXCED_RANGE:
131
132 return ENOENT;
133
134 case ERROR_TOO_MANY_OPEN_FILES:
135 return EMFILE;
136
137 case ERROR_ACCESS_DENIED:
138 case ERROR_CURRENT_DIRECTORY:
139 case ERROR_NETWORK_ACCESS_DENIED:
140 case ERROR_CANNOT_MAKE:
141 case ERROR_FAIL_I24:
142 case ERROR_DRIVE_LOCKED:
143 case ERROR_SEEK_ON_DEVICE:
144 case ERROR_NOT_LOCKED:
145 case ERROR_LOCK_FAILED:
146 return EACCES;
147
148 case ERROR_INVALID_HANDLE:
149 case ERROR_INVALID_TARGET_HANDLE:
150 case ERROR_DIRECT_ACCESS_HANDLE:
151 return EBADF;
152
153 case ERROR_ARENA_TRASHED:
154 case ERROR_NOT_ENOUGH_MEMORY:
155 case ERROR_INVALID_BLOCK:
156 case ERROR_NOT_ENOUGH_QUOTA:
157 return ENOMEM;
158
159 case ERROR_BAD_ENVIRONMENT:
160 return E2BIG;
161
162 case ERROR_BAD_FORMAT:
163 return ENOEXEC;
164
165 case ERROR_NOT_SAME_DEVICE:
166 return EXDEV;
167
168 case ERROR_FILE_EXISTS:
169 return EEXIST;
170
171 case ERROR_NO_PROC_SLOTS:
172 case ERROR_MAX_THRDS_REACHED:
173 case ERROR_NESTING_NOT_ALLOWED:
174 return EAGAIN;
175
176 case ERROR_BROKEN_PIPE:
177 return EPIPE;
178
179 case ERROR_DISK_FULL:
180 return ENOSPC;
181
182 case ERROR_WAIT_NO_CHILDREN:
183 case ERROR_CHILD_NOT_COMPLETE:
184 return ECHILD;
185
186 case ERROR_DIR_NOT_EMPTY:
187 return ENOTEMPTY;
188
189 case ERROR_ALREADY_EXISTS:
190 return EEXIST;
191 }
192
193 return EINVAL;
194}
195
196#else // #if !defined (_WIN32)
197
198# include <sys/types.h>
199# include <sys/wait.h> // for waitpid()
200# include <unistd.h> // for fork(), execv(), access(), sleep()
201# include <setjmp.h> // for setjmp(), longjmp()
202# include <signal.h> // for signal()
203# include <time.h> // for time()
204
205
207// splits command line to the array of parameters
208// note: modifies the cmd string
209// returns the number of parameters in cmd
210// if argv != 0 fills argv up to size elements
211static size_t
212_rw_split_cmd (char* cmd, char** argv, size_t size)
213{
214 RW_ASSERT (0 != cmd);
215
216 size_t ret = 0;
217
218 for (char* end = cmd + strlen (cmd); cmd != end; /*do nothing*/) {
219 // skip the leading spaces
220 while (isspace (*cmd))
221 ++cmd;
222
223 if (end == cmd)
224 break;
225
226 if (argv && ret < size)
227 argv [ret] = cmd;
228
229 ++ret;
230
231 if ('\'' == *cmd || '\"' == *cmd) {
232 char* const cmd1 = cmd + 1;
233 // search the closing quote
234 char* const pos = strchr (cmd1, *cmd);
235 if (pos) {
236 // found, remove the quotes
237 // remove the opening quote
238 memmove (cmd, cmd1, pos - cmd1);
239 // remove the closing quote
240 cmd = pos - 1;
241 memmove (cmd, pos + 1, end - pos);
242 end -= 2;
243 }
244 else {
245 // not found
246 break;
247 }
248 }
249
250 // search the space
251 while (*cmd && !isspace (*cmd))
252 ++cmd;
253
254 if (cmd != end)
255 // found, replace to '\0'
256 *cmd++ = '\0';
257 }
258
259 return ret;
260}
261
262#endif // #if defined (_WIN32)
263
264
266static int
267_rw_vsystem (const char *cmd, va_list va)
268{
269 RW_ASSERT (0 != cmd);
270
271 char *buf = 0;
272
273 rw_vasnprintf (&buf, 0, cmd, va);
274
275 rw_note (0, "file:" __FILE__, __LINE__, "executing \"%s\"", buf);
276
277 // avoid using const in order to prevent gcc warning on Linux
278 // issued for WIFSIGNALED() et al: cast from `const int*' to
279 // `int*' discards qualifiers from pointer target type:
280 // see http://sourceware.org/bugzilla/show_bug.cgi?id=1392
281 /* const */ int ret = system (buf);
282
283 if (ret) {
284
285#ifndef _WIN32
286
287 if (-1 == ret) {
288 // system() failed, e.g., because fork() failed
289 rw_error (0, __FILE__, __LINE__,
290 "system (\"%s\") failed: errno = %{#m} (%{m})",
291 buf);
292 }
293 else if (WIFSIGNALED (ret)) {
294 // command exited with a signal
295 const int signo = WTERMSIG (ret);
296
297 rw_error (0, __FILE__, __LINE__,
298 "the command \"%s\" exited with signal %d (%{K})",
299 buf, signo, signo);
300 }
301 else {
302 // command exited with a non-zero status
303 const int status = WEXITSTATUS (ret);
304
305 rw_error (0, __FILE__, __LINE__,
306 "the command \"%s\" exited with status %d",
307 buf, status);
308 }
309#else // if defined (_WIN32)
310
311 // FIXME: make this more descriptive
312 rw_error (0, __FILE__, __LINE__,
313 "the command \"%s\" failed with code %d",
314 buf, ret);
315
316#endif // _WIN32
317
318 }
319
320 free (buf);
321
322 return ret;
323}
324
325
327_TEST_EXPORT int
328rw_system (const char *cmd, ...)
329{
330 va_list va;
331 va_start (va, cmd);
332
333 const int ret = _rw_vsystem (cmd, va);
334
335 va_end (va);
336 return ret;
337}
338
339
341static rw_pid_t
342_rw_vprocess_create (const char* cmd, va_list va)
343{
344 RW_ASSERT (0 != cmd);
345
346 char *buf = 0;
347
348 rw_vasnprintf (&buf, 0, cmd, va);
349
350 rw_pid_t ret = -1;
351
352#ifdef _WIN32
353
354 STARTUPINFO si = { sizeof (si) };
355 PROCESS_INFORMATION pi;
356
357 if (CreateProcess (0, buf, 0, 0, FALSE,
358 CREATE_NEW_PROCESS_GROUP, 0, 0, &si, &pi)) {
359
360 CloseHandle (pi.hThread);
361 ret = rw_pid_t (pi.hProcess);
362 }
363 else {
364 const DWORD err = GetLastError ();
365
366 rw_error (0, __FILE__, __LINE__,
367 "CreateProcess () failed: GetLastError() = %zu",
368 size_t (err));
369
370 errno = _rw_map_errno (err);
371 }
372
373#else // #if !defined (_WIN32)
374
375 const size_t MAX_PARAMS = 63;
376 char* argv [MAX_PARAMS + 1] = { 0 };
377
378 size_t argc = _rw_split_cmd (buf, argv, MAX_PARAMS);
379
380 if (0 < argc && MAX_PARAMS >= argc)
381 ret = rw_process_create (argv [0], argv);
382 else
383 errno = E2BIG;
384
385#endif // _WIN32
386
387 free (buf);
388
389 return ret;
390}
391
392
395rw_process_create (const char* cmd, ...)
396{
397 va_list va;
398 va_start (va, cmd);
399
400 const rw_pid_t ret = _rw_vprocess_create (cmd, va);
401
402 va_end (va);
403 return ret;
404}
405
406
409rw_process_create (const char* path, char* const argv[])
410{
411#if defined (_WIN32)
412
413 return rw_process_create ("\"%s\" %{ As}", path, argv + 1);
414
415#else // #if !defined (_WIN32)
416
417 if (0 == access (path, X_OK)) {
418
419 const rw_pid_t child_pid = fork ();
420
421 if (0 == child_pid) {
422 // the child process
423 execvp (path, argv);
424
425 // the execvp returns only if an error occurs
426 rw_fprintf (rw_stderr, "%s:%d execvp (%#s, %{As}) failed: "
427 "errno = %{#m} (%{m})\n",
428 __FILE__, __LINE__, path, argv);
429
430 exit (1);
431 }
432 else if (-1 == child_pid)
433 rw_error (0, __FILE__, __LINE__,
434 "fork () failed: errno = %{#m} (%{m})");
435
436 return child_pid;
437 }
438 else
439 rw_error (0, __FILE__, __LINE__,
440 "access (%#s, X_OK) failed: errno = %{#m} (%{m})",
441 path);
442
443 return -1;
444
445#endif // #if defined (_WIN32)
446}
447
448
450#if defined (_WIN32)
451
453rw_waitpid (rw_pid_t pid, int* result, int timeout/* = -1*/)
454{
455 /* Explicitly check for process_id being -1 or -2. In Windows NT,
456 * -1 is a handle on the current process, -2 is a handle on the
457 * current thread, and it is perfectly legal to to wait (forever)
458 * on either */
459 if (-1 == pid || -2 == pid) {
460 errno = ECHILD;
461 return -1;
462 }
463
464 const HANDLE handle = HANDLE (pid);
465
466 const DWORD milliseconds =
467 0 > timeout ? INFINITE : DWORD (timeout * 1000);
468
469 const DWORD res = WaitForSingleObject (handle, milliseconds);
470
471 DWORD err = ERROR_SUCCESS;
472
473 if (WAIT_OBJECT_0 == res) {
474
475 DWORD dwExitCode;
476 if (GetExitCodeProcess (handle, &dwExitCode)) {
477
478 CloseHandle (handle);
479
480 if (dwExitCode)
481 rw_error (0, __FILE__, __LINE__,
482 "the process (pid=%{P}) exited with return code %d",
483 pid, int (dwExitCode));
484
485 if (result)
486 *result = int (dwExitCode);
487
488 return pid;
489 }
490
491 err = GetLastError ();
492 rw_error (0, __FILE__, __LINE__,
493 "GetExitCodeProcess (%{P}, %#p) failed: GetLastError() = %zu",
494 pid, &dwExitCode, size_t (err));
495 }
496 else if (WAIT_FAILED == res) {
497 err = GetLastError ();
498 rw_error (0, __FILE__, __LINE__,
499 "WaitForSingleObject (%{P}, %{?}INFINITE%{:}%zu%{;}) failed: "
500 "GetLastError() = %zu",
501 pid, INFINITE == milliseconds,
502 size_t (milliseconds), size_t (err));
503 }
504 else {
505 // time-out elapsed
506 RW_ASSERT (WAIT_TIMEOUT == res);
507 return 0;
508 }
509
510 if (ERROR_INVALID_HANDLE == err)
511 errno = ECHILD;
512 else
513 errno = _rw_map_errno (err);
514
515 return -1;
516}
517
518#else // #if !defined (_WIN32)
519
520extern "C" {
521
522static int alarm_timeout;
523
524static void handle_alarm_signal (int) {
525 alarm_timeout = 1;
526}
527
528typedef void (*signal_handler_t) (int);
529
530}
531
533rw_waitpid (rw_pid_t pid, int* presult, int timeout/* = -1*/)
534{
535#ifdef _RWSTD_EDG_ECCP
536# define _RWSTD_NO_SIGACTION
537#endif
538
539#ifndef _RWSTD_NO_SIGACTION
540 struct sigaction prev_alarm_action;
541#else
542 signal_handler_t prev_alarm_handler = 0;
543#endif
544 int prev_alarm_timeout = 0;
545
546 alarm_timeout = 0;
547 if (0 < timeout) {
548
549#ifndef _RWSTD_NO_SIGACTION
550 struct sigaction alarm_action;
551 memset (&alarm_action, 0, sizeof alarm_action);
552
553 const signal_handler_t handler_fun = handle_alarm_signal;
554 memcpy (&alarm_action.sa_handler, &handler_fun,
555 sizeof alarm_action.sa_handler);
556
557 sigaction (SIGALRM, &alarm_action, &prev_alarm_action);
558#else
559 prev_alarm_handler = signal (SIGALRM, handle_alarm_signal);
560#endif
561 prev_alarm_timeout = alarm (timeout);
562
563 }
564
565 int result = 0;
566 if (!presult)
567 presult = &result;
568
569 const time_t start = time(0);
570
571 int status = 0;
572 rw_pid_t ret = 0;
573 do {
574
575 ret = waitpid (pid_t (pid), &status, 0);
576
577 if (-1 == ret) {
578
579 if (EINTR == errno && alarm_timeout) {
580
581 // we are expected to return 0 on timeout
582 ret = 0;
583 }
584 else if (EINTR == errno) {
585
586 rw_warn (0, __FILE__, __LINE__,
587 "waitpid (%{P}, %#p, 0) interrupted: "
588 "errno = %{#m} (%{m})",
589 pid, &status);
590
591 continue; // try again
592 }
593 else {
594
595 rw_error (0, __FILE__, __LINE__,
596 "waitpid (%{P}, %#p, 0) failed: "
597 "errno = %{#m} (%{m})",
598 pid, &status);
599 }
600
601 }
602 else if (ret == pid) {
603
604 if (WIFSIGNALED (status)) {
605 // process exited with a signal
606 const int signo = WTERMSIG (status);
607
608 rw_error (0, __FILE__, __LINE__,
609 "the process (pid=%{P}) exited with signal %d (%{K})",
610 pid, signo, signo);
611
612 *presult = signo;
613 }
614 else if (WIFEXITED (status)) {
615 // process exited with a status
616 const int retcode = WEXITSTATUS (status);
617
618 if (retcode)
619 rw_error (0, __FILE__, __LINE__,
620 "the process (pid=%{P}) exited with return code %d",
621 pid, retcode);
622
623 *presult = retcode;
624 }
625 else {
626 *presult = -1;
627 }
628 }
629 else {
630 *presult = -1;
631 }
632
633 } while(false);
634
635 if (0 < timeout) {
636
637 if (prev_alarm_timeout) {
638 const int delta = int (time(0) - start);
639
640 if (delta < prev_alarm_timeout)
641 prev_alarm_timeout -= delta;
642 else
643 prev_alarm_timeout = 1;
644 }
645
646 alarm (prev_alarm_timeout);
647
648#ifndef _RWSTD_NO_SIGACTION
649 sigaction (SIGALRM, &prev_alarm_action, 0);
650#else
651 signal (SIGALRM, prev_alarm_handler);
652#endif
653
654 }
655
656 return ret;
657}
658
659#endif // #if defined (_WIN32)
660
661
662_TEST_EXPORT int
663rw_process_kill (rw_pid_t pid, int signo)
664{
665 // timeout for rw_wait_pid
666 const int timeout = 1;
667
668#if defined (_WIN32)
669
670 // send signal
671 if (!TerminateProcess (HANDLE (pid), DWORD (signo))) {
672
673 const DWORD err = GetLastError ();
674 rw_error (0, __FILE__, __LINE__,
675 "TerminateProcess (%{P}, %i) failed: GetLastError() = %zu",
676 pid, signo, size_t (err));
677
678 if (ERROR_INVALID_HANDLE == err)
679 errno = ESRCH;
680 else if (ERROR_ACCESS_DENIED == err)
681 errno = EPERM;
682 else
683 errno = _rw_map_errno (err);
684
685 return -1;
686 }
687
688 // wait for process termination
689 rw_pid_t res = rw_waitpid (pid, 0, timeout);
690 if (pid == res)
691 return 0;
692
693 if (-1 == res)
694 rw_error (0, __FILE__, __LINE__,
695 "rw_waitpid (%{P}, 0, %i) failed: errno = %{#m} (%{m})",
696 pid, timeout);
697
698 return 1;
699
700#else // #if !defined (_WIN32)
701
702 static const int signals_ [] = {
704 };
705
706 const int* const signals = (-1 == signo) ? signals_ : &signo;
707
708 const unsigned sigcount =
709 (-1 == signo) ? unsigned (sizeof (signals_) / sizeof (*signals_)) : 1;
710
711 int ret = -1;
712
713 for (unsigned i = 0; i < sigcount; ++i) {
714
715 // send signal
716 ret = kill (pid_t (pid), signals [i]);
717
718 if (-1 == ret) {
719 rw_error (0, __FILE__, __LINE__,
720 "kill (%{P}, %{K}) failed: errno = %{#m} (%{m})",
721 pid, signals [i]);
722
723 continue;
724 }
725
726 // wait for process termination
727 rw_pid_t res = rw_waitpid (pid, 0, timeout);
728 if (pid == res)
729 return 0;
730
731 if (-1 == res)
732 rw_error (0, __FILE__, __LINE__,
733 "rw_waitpid (%{P}, 0, %i) failed: errno = %{#m} (%{m})",
734 pid, timeout);
735
736 ret = 1;
737 }
738
739 return ret;
740
741#endif // #if defined (_WIN32)
742}
#define i
Definition 17.names.cpp:126
#define start
Definition 17.names.cpp:547
#define res
Definition 17.names.cpp:531
#define buf
Definition 17.names.cpp:452
#define pos
Definition 17.names.cpp:518
volatile int result
const int end
IMPORT_C SInt32 system(const Char *)
#define SIGINT
#define SIGTERM
#define va_end(ignore)
#define va_start(va, name)
A TOML path.
Definition path.hpp:209
#define ENOENT
Definition exec.cpp:93
int kill(pid_t pid, int sig)
#define ESRCH
Definition exec.cpp:96
#define ECHILD
Definition exec.cpp:102
#define ENOMEM
Definition memattr.cpp:78
#define SIGHUP
Definition process.cpp:75
_TEST_EXPORT int rw_system(const char *cmd,...)
Definition process.cpp:328
#define EINTR
Definition process.cpp:54
_TEST_EXPORT rw_pid_t rw_waitpid(rw_pid_t pid, int *presult, int timeout)
Definition process.cpp:533
#define SIGQUIT
Definition process.cpp:79
_TEST_EXPORT int rw_process_kill(rw_pid_t pid, int signo)
Definition process.cpp:663
void(* signal_handler_t)(int)
Definition process.cpp:528
_TEST_EXPORT rw_pid_t rw_process_create(const char *cmd,...)
Definition process.cpp:395
#define SIGALRM
Definition process.cpp:71
#define E2BIG
Definition process.cpp:50
#define SIGKILL
Definition process.cpp:83
_TEST_EXPORT int rw_vasnprintf(char **, size_t *, const char *, va_list)
Definition printf.cpp:946
_TEST_EXPORT int rw_note(int, const char *, int, const char *,...)
Definition driver.cpp:1664
_TEST_EXPORT int rw_error(int, const char *, int, const char *,...)
Definition driver.cpp:1613
_TEST_EXPORT int rw_warn(int, const char *, int, const char *,...)
Definition driver.cpp:1647
_TEST_EXPORT rw_file *const rw_stderr
Definition printf.cpp:3348
_TEST_EXPORT int rw_fprintf(rw_file *, const char *,...)
Definition printf.cpp:3397
_RWSTD_SSIZE_T rw_pid_t
Definition rw_process.h:33
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
A local time-of-day.
#define EINVAL
Definition cmdopt.cpp:40