Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
exec.cpp
Go to the documentation of this file.
1
27/* disable Compaq/HP C++ pure libc headers to allow POSIX symbols
28 such as SIGALRM or SIGKILL to be defined. */
29#undef __PURE_CNAME
30
31#include <assert.h> /* for assert */
32#include <ctype.h> /* for tolower */
33#include <errno.h> /* for errno */
34#include <fcntl.h> /* for O_*, */
35#include <signal.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h> /* for str*, mem* */
39
40#ifndef _WIN32
41# include <unistd.h> /* for close, dup, exec, fork */
42# include <sys/wait.h>
43# include <sys/times.h> /* for times - is this XSI? */
44# ifdef _XOPEN_UNIX
45# include <sys/resource.h> /* for setlimit(), RLIMIT_CORE, ... */
46# endif
47#else
48# ifndef _WIN32_WINNT
49# define _WIN32_WINNT 0x0500
50# endif
51# include <windows.h> /* for PROCESS_INFORMATION, CreateProcess, ... */
52# ifndef SIGTRAP
53# define SIGTRAP 5 // STATUS_BREAKPOINT translated into SIGTRAP
54# endif
55# ifndef SIGBUS
56# define SIGBUS 10 // STATUS_IN_PAGE_ERROR translated into SIGBUS
57# endif
58# ifndef SIGSYS
59# define SIGSYS 12 // STATUS_INVALID_PARAMETER translated into SIGSYS
60# endif
61# ifndef SIGSTKFLT
62# define SIGSTKFLT 16 // STATUS_FLOAT_STACK_CHECK translated into SIGSTKFLT
63# endif
64# ifndef STATUS_INVALID_PARAMETER
65# define STATUS_INVALID_PARAMETER ((DWORD)0xC000000DL)
66# endif
67# ifndef STATUS_HEAP_CORRUPTION
68# define STATUS_HEAP_CORRUPTION ((DWORD)0xC0000374L)
69# endif
70# ifndef STATUS_STACK_BUFFER_OVERRUN
71# define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409L)
72# endif
73# ifndef STATUS_INVALID_CRUNTIME_PARAMETER
74# define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD)0xC0000417L)
75# endif
76#endif
77
78#ifndef SIGHUP
79# define SIGHUP 1 /* Linux value */
80#endif
81#ifndef SIGQUIT
82# define SIGQUIT 3 /* Linux value */
83#endif
84#ifndef SIGKILL
85# define SIGKILL 9 /* Linux value */
86#endif
87#ifndef SIGALRM
88# define SIGALRM 14 /* Linux value */
89#endif
90
91
92#ifndef ENOENT
93# define ENOENT 1 /* Linux value */
94#endif
95#ifndef ESRCH
96# define ESRCH 3 /* Linux value */
97#endif
98#ifndef EINTR
99# define EINTR 4 /* Linux value */
100#endif
101#ifndef ECHILD
102# define ECHILD 10 /* Linux value */
103#endif
104#ifndef EINVAL
105# define EINVAL 22 /* Linux value */
106#endif
107
108#include <sys/stat.h> /* for S_* */
109#include <sys/types.h>
110
111#include "cmdopt.h"
112#include "target.h" /* For struct target_opts */
113#include "util.h"
114
115#include "exec.h"
116
117#if !defined (_WIN32) && !defined (_RWSTD_NO_PURE_C_HEADERS)
118# ifdef __cplusplus
119extern "C" {
120# endif
121
122int kill (pid_t pid, int sig);
123
124FILE* fdopen (int fd, const char *mode);
125
126# ifdef __cplusplus
127} /* extern "C" */
128# endif
129#endif /* !_WIN32 && !_RWSTD_NO_PURE_C_HEADERS */
130
131
141static int alarm_timeout;
142
149static int kill_signal;
150
157#undef SIGNAL
158#define SIGNAL(val) { SIG ## val, #val }
159
168static const struct {
169 int val;
170 const char* str;
171} signal_names [] = {
172#ifdef SIGABRT
173 SIGNAL (ABRT),
174#endif /* SIGABRT */
175#ifdef SIGALRM
176 SIGNAL (ALRM),
177#endif /* SIGALRM */
178#ifdef SIGBUS
179 SIGNAL (BUS),
180#endif /* SIGBUS */
181#ifdef SIGCANCEL
182 SIGNAL (CANCEL),
183#endif /* SIGCANCEL */
184#ifdef SIGCHLD
185 SIGNAL (CHLD),
186#endif /* SIGCHLD */
187#ifdef SIGCKPT
188 SIGNAL (CKPT),
189#endif /* SIGCKPT */
190#ifdef SIGCLD
191 SIGNAL (CLD),
192#endif /* SIGCLD */
193#ifdef SIGCONT
194 SIGNAL (CONT),
195#endif /* SIGCONT */
196#ifdef SIGDIL
197 SIGNAL (DIL),
198#endif /* SIGDIL */
199#ifdef SIGEMT
200 SIGNAL (EMT),
201#endif /* SIGEMT */
202#ifdef SIGFPE
203 SIGNAL (FPE),
204#endif /* SIGFPE */
205#ifdef SIGFREEZE
206 SIGNAL (FREEZE),
207#endif /* SIGFREEZE */
208#ifdef SIGGFAULT
209 SIGNAL (GFAULT),
210#endif /* SIGGFAULT */
211#ifdef SIGHUP
212 SIGNAL (HUP),
213#endif /* SIGHUP */
214#ifdef SIGILL
215 SIGNAL (ILL),
216#endif /* SIGILL */
217#ifdef SIGINFO
218 SIGNAL (INFO),
219#endif /* SIGINFO */
220#ifdef SIGINT
221 SIGNAL (INT),
222#endif /* SIGINT */
223#ifdef SIGIO
224 SIGNAL (IO),
225#endif /* SIGIO */
226#ifdef SIGIOT
227 SIGNAL (IOT),
228#endif /* SIGIOT */
229#ifdef SIGK32
230 SIGNAL (K32),
231#endif /* SIGK32 */
232#ifdef SIGKILL
233 SIGNAL (KILL),
234#endif /* SIGKILL */
235#ifdef SIGLOST
236 SIGNAL (LOST),
237#endif /* SIGLOST */
238#ifdef SIGLWP
239 SIGNAL (LWP),
240#endif /* SIGLWP */
241#ifdef SIGPIPE
242 SIGNAL (PIPE),
243#endif /* SIGPIPE */
244#ifdef SIGPOLL
245 SIGNAL (POLL),
246#endif /* SIGPOLL */
247#ifdef SIGPROF
248 SIGNAL (PROF),
249#endif /* SIGPROF */
250#ifdef SIGPTINTR
251 SIGNAL (PTINTR),
252#endif /* SIGPTINTR */
253#ifdef SIGPTRESCHED
254 SIGNAL (PTRESCHED),
255#endif /* SIGPTRESCHED */
256#ifdef SIGPWR
257 SIGNAL (PWR),
258#endif /* SIGPWR */
259#ifdef SIGQUIT
260 SIGNAL (QUIT),
261#endif /* SIGQUIT */
262#ifdef SIGRESTART
263 SIGNAL (RESTART),
264#endif /* SIGRESTART */
265#ifdef SIGRESV
266 SIGNAL (RESV),
267#endif /* SIGRESV */
268#ifdef SIGSEGV
269 SIGNAL (SEGV),
270#endif /* SIGSEGV */
271#ifdef SIGSTKFLT
272 SIGNAL (STKFLT),
273#endif /* SIGSTKFLT */
274#ifdef SIGSTOP
275 SIGNAL (STOP),
276#endif /* SIGSTOP */
277#ifdef SIGSYS
278 SIGNAL (SYS),
279#endif /* SIGSYS */
280#ifdef SIGTERM
281 SIGNAL (TERM),
282#endif /* SIGTERM */
283#ifdef SIGTHAW
284 SIGNAL (THAW),
285#endif /* SIGTHAW */
286#ifdef SIGTRAP
287 SIGNAL (TRAP),
288#endif /* SIGTRAP */
289#ifdef SIGTSTP
290 SIGNAL (TSTP),
291#endif /* SIGTSTP */
292#ifdef SIGTTIN
293 SIGNAL (TTIN),
294#endif /* SIGTTIN */
295#ifdef SIGTTOU
296 SIGNAL (TTOU),
297#endif /* SIGTTOU */
298#ifdef SIGUNUSED
299 SIGNAL (UNUSED),
300#endif /* SIGUNUSED */
301#ifdef SIGURG
302 SIGNAL (URG),
303#endif /* SIGURG */
304#ifdef SIGUSR1
305 SIGNAL (USR1),
306#endif /* SIGUSR1 */
307#ifdef SIGUSR2
308 SIGNAL (USR2),
309#endif /* SIGUSR2 */
310#ifdef SIGVTALRM
311 SIGNAL (VTALRM),
312#endif /* SIGVTALRM */
313#ifdef SIGWAITING
314 SIGNAL (WAITING),
315#endif /* SIGWAITING */
316#ifdef SIGWINCH
317 SIGNAL (WINCH),
318#endif /* SIGWINCH */
319#ifdef SIGWINDOW
320 SIGNAL (WINDOW),
321#endif /* SIGWINDOW */
322#ifdef SIGXCPU
323 SIGNAL (XCPU),
324#endif /* SIGXCPU */
325#ifdef SIGXFSZ
326 SIGNAL (XFSZ),
327#endif /* SIGXFSZ */
328#ifdef SIGXRES
329 SIGNAL (XRES),
330#endif /* SIGXRES */
331 { -1, 0 }
332};
333
346static int
347rw_strcasecmp (const char* s1, const char* s2)
348{
349 int delta;
350
351 typedef unsigned char UChar;
352
353 assert (0 != s1);
354 assert (0 != s2);
355
356 do {
357 delta = tolower ((UChar)*s1) - tolower ((UChar)*s2);
358 } while (*s1++ && *s2++ && 0 == delta);
359
360 return delta;
361}
362
363
364int
365get_signo (const char* signame)
366{
367 size_t i;
368 typedef unsigned char UChar;
369
370 assert (0 != signame);
371
372 if (isdigit (signame [0])) {
373 char *junk;
374 int trans = int (strtol (signame, &junk, 10));
375
376 if (0 == *junk && 0 == errno)
377 return trans;
378 else
379 return -1;
380 }
381
382 if ( 's' == tolower ((UChar)signame [0])
383 && 'i' == tolower ((UChar)signame [1])
384 && 'g' == tolower ((UChar)signame [2]))
385 signame += 3;
386
387 for (i = 0; signal_names [i].str; ++i) {
388 if (0 == rw_strcasecmp (signal_names [i].str, signame)) {
389 return signal_names [i].val;
390 }
391 }
392
393 return -1;
394}
395
396const char*
397get_signame (int signo)
398{
399 size_t i;
400 static char def [32];
401
402 for (i = 0; signal_names [i].str; ++i) {
403 if (signal_names [i].val == signo) {
404 return signal_names [i].str;
405 }
406 }
407
408 /* We've run out of known signal numbers, so use a default name */
409 sprintf (def, "SIG#%d", signo);
410 return def;
411}
412
413#ifndef _WIN32
421#ifdef __cplusplus
422extern "C" {
423#endif
424
425static void
426handle_alrm (int signo)
427{
428 if (SIGALRM == signo)
429 alarm_timeout = 1;
430}
431
441static void
442handle_term_signal (int signo)
443{
444 kill_signal = signo;
445 alarm_timeout = 1;
446}
447
448typedef void (*alarm_handler)(int);
449
450#ifdef __cplusplus
451}
452#endif
453
472static void
473wait_for_child (pid_t child_pid, int timeout, struct target_status* result)
474{
475 /* note that processes with no controlling terminal ignore
476 the SIGINT and SIGQUIT signals
477 */
478 static const int signals [] = {
480 };
481
482 static const unsigned sigcount = sizeof (signals) / sizeof (int);
483
484 unsigned siginx = 0;
485
486 int stopped = 0;
487
488#ifdef WCONTINUED
489 int waitopts = WUNTRACED | WCONTINUED;
490#else
491 int waitopts = WUNTRACED;
492#endif
493
494 int status;
495
496 assert (1 < child_pid);
497
498 /* Clear timeout */
499 alarm_timeout = 0;
500
501 /* Set handler (if needed).
502 */
503 rw_signal (SIGALRM, handle_alrm);
504
505 /* Set handlers for SIGHUP, SIGINT, SIGQUIT, SIGTERM so we can kill the
506 child process prior to dieing.
507 */
508 kill_signal = 0;
509
510 rw_signal (SIGHUP, handle_term_signal);
511 rw_signal (SIGINT, handle_term_signal);
512 rw_signal (SIGQUIT, handle_term_signal);
513 rw_signal (SIGTERM, handle_term_signal);
514
515 if (timeout > 0)
516 alarm (timeout);
517
518 while (1) {
519 const pid_t wait_pid = waitpid (child_pid, &status, waitopts);
520 if (child_pid == wait_pid) {
521 if (WIFEXITED (status)) {
522 result->exit = WEXITSTATUS (status);
523
524 /* from POSIX, 2.8.2 Exit Status for Commands:
525 *
526 * If a command is not found, the exit status shall be 127.
527 * If the command name is found, but it is not an executable
528 * utility, the exit status shall be 126. Applications that
529 * invoke utilities without using the shell should use these
530 * exit status values to report similar errors.
531 */
532 switch (result->exit) {
533 case 126:
534 result->status = ST_EXECUTE;
535 break;
536 case 127:
537 result->status = ST_EXIST;
538 break;
539 }
540 break; /*we've got an exit state, so let's bail*/
541 }
542 else if (WIFSIGNALED (status)) {
543 result->exit = WTERMSIG (status);
544 result->signaled = 1;
545 break; /*we've got an exit state, so let's bail*/
546 }
547 else if (WIFSTOPPED (status))
548 stopped = status;
549#ifdef WIFCONTINUED /*Perhaps we should guard WIFSTOPPED with this guard also */
550 else if (WIFCONTINUED (status))
551 stopped = 0;
552#endif
553 else {
554 /* huh? */
555 }
556 }
557 else if ((pid_t)-1 == wait_pid) {
558 if (EINTR == errno && alarm_timeout) {
559 /* timeout elapsed, so send a signal to the child */
560 if (stopped) {
561 /* If the child process is stopped, it is incapable of
562 recieving signals. Therefore, we'll record this
563 and break out of the loop.
564 */
565 result->exit = WTERMSIG (stopped);
566 result->signaled = 1;
567 break;
568 }
569
570 if (0 != kill (-child_pid, signals [siginx])) {
571 if (ESRCH == errno)
572 /* ESRCH means 'No process (group) found'. Since
573 there aren't any processes in the process group,
574 we'll continue so we can collect the return value
575 if needed.
576 */
577 continue;
578 /* In addition to ESRCH, kill () may also set errno to
579 EINVAL or EPERM, according to the POSIX spec, in
580 addition to any platform specific extensions.
581 EPERM means 'No permissions to signal any recieving
582 process'. It is unlikely that this situation will
583 change, but we will try the remaining signals in the
584 signals array, in the same manner as if the signal had
585 been sent correctly.
586 EINVAL means 'The signal is an invalid or unsupported
587 signal number'. As the signal number macros used in
588 the signal array are hard coded, issues should be
589 detected at compile time, not run time. This is not a
590 fatal situation, so the remainder of signals in the
591 signal array will be tried, as if this transmission
592 had been successfull.
593 The correct behavior for any platform-specific
594 extensions needs to be evaluated, but we are treating
595 them like EPERM or EINVAL at this time. */
596 }
597
598 /* Consider recording the signal used here.*/
599
600 ++siginx;
601
602 /* Step to the next signal */
603 if (siginx >= sigcount) {
604 /* Still running, but we've run out of signals to try
605 Therefore, we'll set error flags and break out of
606 the loop.
607 */
608 result->status = ST_NOT_KILLED;
609 break;
610 }
611
612 /* Reset the alarm */
613 alarm_timeout = 0;
614 alarm (1);
615 }
616 else if (EINTR == errno && !alarm_timeout) {
617 /* continue iterating */
618 }
619 else if (EINVAL == errno) {
620 if (waitopts)
621 /* bad waitpid options, reset to 0 and try again */
622 waitopts = 0;
623 else {
624 /* Now what? */
625 }
626 }
627 else if (ECHILD == errno) {
628 /* should not happen */
629 warn ("waitpid (%d) error: %s\n", (int)child_pid,
630 strerror (errno));
631 }
632 else {
633 /* waitpid () error */
634 warn ("waitpid (%d) error: %s\n", (int)child_pid,
635 strerror (errno));
636 }
637 }
638 else if ((pid_t)0 == wait_pid) {
639 /* should not happen */
640 }
641 else {
642 /* what the heck? */
643 }
644 }
645
646 /* Clear alarm */
647 alarm (0);
648
649 /* Kill/cleanup any grandchildren. */
650 /* On solaris, this logic tries to avoid the situation where grandchild
651 process times are rolled into the timing of a later process */
652 while (siginx < sigcount && 0 == kill (-child_pid, signals [siginx])) {
653 ++siginx;
654 sleep (1);
655 }
656
657 /* Check if we were signaled to quit. */
658 if (kill_signal) {
659 /* Reset the handlers to normal */
664
665 if (0 > raise (kill_signal))
666 terminate (1, "raise(%s) failed: %s\n",
667 get_signame (kill_signal), strerror (errno));
668 }
669}
670
671
680static void
681replace_file (int source, int dest, const char* name)
682{
683 int result;
684
685 assert (source != dest);
686 assert (0 <= source);
687 assert (0 <= dest);
688 assert (0 != name);
689
690 result = dup2 (source, dest);
691 if (-1 == result)
692 terminate (1, "redirecting to %s failed: %s\n", name,
693 strerror (errno));
694
695 result = close (source);
696 if (-1 == result)
697 terminate (1, "closing source for %s redirection failed: %s\n",
698 name, strerror (errno));
699}
700
701#ifdef _XOPEN_UNIX
710#undef LIMIT
711#define LIMIT(val, idx) { RLIMIT_ ## val, options->idx, #val }
712
721static void
722limit_process (const struct target_opts* options)
723{
724 static const struct {
725 int resource;
726 rw_rlimit* limit;
727 const char* name;
728 } limits[] = {
729#ifdef RLIMIT_CORE
730 LIMIT (CORE, core),
731#endif /* RLIMIT_CORE */
732#ifdef RLIMIT_CPU
733 LIMIT (CPU, cpu),
734#endif /* RLIMIT_CPU */
735#ifdef RLIMIT_DATA
736 LIMIT (DATA, data),
737#endif /* RLIMIT_DATA */
738#ifdef RLIMIT_FSIZE
739 LIMIT (FSIZE, fsize),
740#endif /* RLIMIT_FSIZE */
741#ifdef RLIMIT_NOFILE
742 LIMIT (NOFILE, nofile),
743#endif /* RLIMIT_NOFILE */
744#ifdef RLIMIT_STACK
745 LIMIT (STACK, stack),
746#endif /* RLIMIT_STACK */
747#ifdef RLIMIT_AS
748 LIMIT (AS, as),
749#endif /* RLIMIT_AS */
750 { 0, 0, 0 }
751 };
752
753 for (size_t i = 0; limits [i].name; ++i) {
754 struct rlimit local;
755
756 if (!limits [i].limit)
757 continue;
758
759 memcpy (&local, limits [i].limit, sizeof local);
760
761 if (setrlimit (limits [i].resource, &local)) {
762 warn ("error setting process limits for %s (soft: %lu, hard: "
763 "%lu): %s\n", limits [i].name, local.rlim_cur,
764 local.rlim_max, strerror (errno));
765 }
766 }
767}
768#endif /* _XOPEN_UNIX */
769
784static void
785calculate_usage (struct target_status* result, const clock_t h_clk,
786 const struct tms* const h_tms)
787{
788 struct tms c_tms;
789 clock_t c_clk;
790
791 assert (0 != result);
792 assert (0 != h_tms);
793
794 c_clk = times (&c_tms);
795
796 if ((clock_t)-1 == c_clk) {
797 warn ("Failed to retrieve ending times: %s", strerror (errno));
798 return;
799 }
800
801 /* time calculations */
802 result->wall_time = c_clk - h_clk;
803 result->usr_time = c_tms.tms_cutime - h_tms->tms_cutime;
804 result->sys_time = c_tms.tms_cstime - h_tms->tms_cstime;
805}
806
807void exec_file (const struct target_opts* options, struct target_status* result)
808{
809 const pid_t child_pid = fork ();
810
811 assert (0 != options);
812 assert (0 != options->argv);
813 assert (0 != options->argv [0]);
814
815 if (0 == child_pid) { /* child */
816 const char* const target_name = get_target ();
817 FILE* error_file;
818
819 assert (0 != target_name);
820
821 /* Set process group ID (so entire group can be killed)*/
822 {
823 const pid_t pgroup = setsid ();
824 if (getpid () != pgroup)
825 terminate (1, "Error setting process group: %s\n",
826 strerror (errno));
827 }
828
829 /* Cache stdout for use if execv () fails */
830 {
831 const int error_cache = dup (2);
832 if (-1 == error_cache)
833 terminate (1, "Error duplicating stderr: %s\n",
834 strerror (errno));
835
836 error_file = fdopen (error_cache,"a");
837 if (0 == error_file)
838 terminate (1, "Error opening file handle from cloned "
839 "stderr file descriptor: %s\n", strerror (errno));
840 }
841
842 /* Redirect stdin */
843 {
844 const int intermit = open (options->infname, O_RDONLY);
845 replace_file (intermit, 0, "stdin");
846 }
847
848 /* Redirect stdout */
849 {
850 int intermit;
851
852 intermit = open (options->outfname,
853 O_WRONLY | O_CREAT | O_TRUNC,
854 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
855
856 if (-1 == intermit)
857 terminate (1, "Error opening %s for output redirection: "
858 "%s\n", options->outfname, strerror (errno));
859
860 replace_file (intermit, 1, "stdout");
861 }
862
863 /* Redirect stderr */
864 if (-1 == dup2 (1, 2))
865 terminate (1, "Redirection of stderr to stdout failed: %s\n",
866 strerror (errno));
867
868#ifdef _XOPEN_UNIX
869 limit_process (options);
870#endif /* _XOPEN_UNIX */
871
872 execv (options->argv [0], options->argv);
873
874 /* POSIX specifies status of 127 when the executable doesn't
875 * exist and 126 for all other exec failures
876 */
877 if (ENOENT == errno)
878 exit (127);
879
880 exit (126);
881 }
882
883 if (-1 == child_pid) {
884 result->status = ST_EXECUTE;
885 warn ("Unable to create child process for %s: %s\n", options->argv [0],
886 strerror (errno));
887 }
888 else {
889 /* parent */
890 struct tms h_tms;
891 clock_t h_clk = times (&h_tms);
892 wait_for_child (child_pid, options->timeout, result);
893 if ((clock_t)-1 != h_clk)
894 calculate_usage (result, h_clk, &h_tms);
895 else
896 warn ("Failed to retrieve start times: %s", strerror (errno));
897 }
898}
899#else /* _WIN32 */
900
901// map between NT_STATUS value and corresponding UNIX signal
902static const struct {
903 DWORD nt_status;
904 int signal;
905} nt_status_map [] = {
906 { STATUS_BREAKPOINT, SIGTRAP },
907 { STATUS_ACCESS_VIOLATION, SIGSEGV },
908 { STATUS_STACK_OVERFLOW, SIGSEGV },
909 { STATUS_HEAP_CORRUPTION, SIGSEGV },
910 { STATUS_STACK_BUFFER_OVERRUN, SIGSEGV },
911 { STATUS_IN_PAGE_ERROR, SIGBUS },
912 { STATUS_ILLEGAL_INSTRUCTION, SIGILL },
913 { STATUS_PRIVILEGED_INSTRUCTION, SIGILL },
914 { STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE },
915 { STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE },
916 { STATUS_FLOAT_INEXACT_RESULT, SIGFPE },
917 { STATUS_FLOAT_INVALID_OPERATION, SIGFPE },
918 { STATUS_FLOAT_OVERFLOW, SIGFPE },
919 { STATUS_FLOAT_UNDERFLOW, SIGFPE },
920 { STATUS_INTEGER_DIVIDE_BY_ZERO, SIGFPE },
921 { STATUS_INTEGER_OVERFLOW, SIGFPE },
922 { STATUS_FLOAT_STACK_CHECK, SIGSTKFLT },
923 { STATUS_INVALID_PARAMETER, SIGSYS },
924 { STATUS_INVALID_CRUNTIME_PARAMETER, SIGSYS }
925};
926
927
937static char*
938merge_argv (char** argv)
939{
940 size_t len = 0;
941 char** opts;
942 char* term;
943 char* merge;
944 char* pos;
945
946 assert (0 != argv);
947
948 for (opts = argv; *opts; ++opts) {
949 len += 3; /* for open ", close " and trailing space or null */
950 for (term = *opts; *term; ++term) {
951 if ('"' == *term || escape_code == *term)
952 ++len; /* Escape embedded "s and ^s*/
953 ++len;
954 }
955 }
956
957 pos = merge = (char*)RW_MALLOC (len);
958 for (opts = argv; *opts; ++opts) {
959 *(pos++) = '"';
960 for (term = *opts; *term; ++term) {
961 if ('"' == *term || escape_code == *term)
962 *(pos++) = escape_code; /* Escape embedded "s and ^s*/
963 *(pos++) = *term;
964 }
965 *(pos++) = '"';
966 *(pos++) = ' ';
967 }
968 *(pos-1) = '\0'; /* convert trailing space to null */
969 return merge;
970}
971
978static DWORD
979warn_last_error (const char* action)
980{
981 DWORD error = GetLastError ();
982
983 if (error) {
984 LPTSTR error_text = 0;
985 if (FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
986 FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
987 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
988 (LPTSTR)&error_text, 0, NULL)) {
989 warn ("%s failed with error %d: %s\n", action, error, error_text);
990 LocalFree (error_text);
991 }
992 else {
993 warn ("%s failed with error %d. Additionally, FormatMessage "
994 "failed with error %d.\n", action, error, GetLastError ());
995 }
996 }
997 return error;
998}
999
1007static void
1008kill_child_process (PROCESS_INFORMATION child, struct target_status* result)
1009{
1010 OSVERSIONINFO OSVer;
1011
1012 OSVer.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1013 if (0 == GetVersionEx (&OSVer)) {
1014 result->status = ST_SYSTEM_ERROR;
1015 warn_last_error ("Retrieving host version");
1016 return;
1017 }
1018 /* Try to soft kill child process group if it didn't terminate, but only
1019 on NT */
1020
1021 if (VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) {
1022
1023 struct sig_event
1024 {
1025 DWORD signal_;
1026 const char* msg_;
1027 }
1028 sig_events [] = {
1029 { CTRL_C_EVENT, "Sending child process Control-C" },
1030 { CTRL_BREAK_EVENT, "Sending child process Control-Break" }
1031 };
1032
1033 for (unsigned long i = 0;
1034 i < sizeof (sig_events) / sizeof (*sig_events); ++i) {
1035 DWORD wait_code;
1036
1037 if (0 == GenerateConsoleCtrlEvent (sig_events [i].signal_,
1038 child.dwProcessId))
1039 warn_last_error (sig_events [i].msg_);
1040
1041 wait_code = WaitForSingleObject (child.hProcess, 1000);
1042 if (WAIT_TIMEOUT == wait_code)
1043 continue;
1044
1045 if (WAIT_OBJECT_0 != wait_code) {
1046 result->status = ST_SYSTEM_ERROR;
1047 warn_last_error ("Waiting for child process");
1048 }
1049 return;
1050 }
1051 }
1052 /* Then hard kill the child process */
1053 if (0 == TerminateProcess (child.hProcess, 3))
1054 warn_last_error ("Terminating child process");
1055 else if (WAIT_FAILED == WaitForSingleObject (child.hProcess, 1000))
1056 warn_last_error ("Waiting for child process");
1057}
1058
1059/* FILETIME to ULONGLONG */
1060inline ULONGLONG fttoull (const FILETIME& ft)
1061{
1062 ULARGE_INTEGER __ft;
1063 __ft.LowPart = ft.dwLowDateTime;
1064 __ft.HighPart = ft.dwHighDateTime;
1065 return __ft.QuadPart;
1066}
1067
1068void exec_file (const struct target_opts* options, struct target_status* result)
1069{
1070 char* merged;
1071 PROCESS_INFORMATION child;
1072 STARTUPINFO context;
1073 SECURITY_ATTRIBUTES child_sa = /* SA for inheritable handle. */
1074 {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
1075 DWORD real_timeout, wait_code;
1076 FILETIME start, end;
1077
1078 assert (0 != options);
1079 assert (0 != options->argv);
1080 assert (0 != options->argv [0]);
1081
1082 real_timeout = (options->timeout > 0) ? options->timeout * 1000 : INFINITE;
1083
1084 /* Borrow our startup info */
1085 GetStartupInfo (&context);
1086 context.dwFlags = STARTF_USESTDHANDLES;
1087
1088 /* Create I/O handles */
1089 {
1090 /* Output redirection */
1091
1092 context.hStdOutput = CreateFile (options->outfname, GENERIC_WRITE,
1093 FILE_SHARE_WRITE, &child_sa, CREATE_ALWAYS,
1094 FILE_ATTRIBUTE_NORMAL, NULL);
1095 if (INVALID_HANDLE_VALUE == context.hStdOutput) {
1096 result->status = ST_SYSTEM_ERROR;
1097 warn_last_error ("Opening child output stream");
1098 return;
1099 }
1100
1101 context.hStdError = context.hStdOutput;
1102
1103 /* Input redirection */
1104 context.hStdInput =
1105 CreateFile (options->infname, GENERIC_READ, FILE_SHARE_READ,
1106 &child_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1107
1108 if (INVALID_HANDLE_VALUE == context.hStdInput) {
1109 CloseHandle (context.hStdOutput);
1110 result->status = ST_SYSTEM_ERROR;
1111 warn_last_error ("Opening child input stream");
1112 return;
1113 }
1114 }
1115
1116 merged = merge_argv (options->argv);
1117
1118 /* set appropriate error mode (the child process inherits this
1119 error mode) to disable displaying the critical-error-handler
1120 and general-protection-fault message boxes */
1121 UINT old_mode = SetErrorMode (SEM_FAILCRITICALERRORS
1122 | SEM_NOGPFAULTERRORBOX);
1123
1124 /* Create the child process in suspended state */
1125 if (0 == CreateProcess (options->argv [0], merged, 0, 0, 1,
1126 CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED, 0, 0, &context, &child)) {
1127 /* record the status if we failed to create the process */
1128 result->status = ST_SYSTEM_ERROR;
1129 warn_last_error ("Creating child process");;
1130 }
1131
1132 /* restore the previous error mode */
1133 SetErrorMode (old_mode);
1134
1135 /* Clean up handles */
1136 if (context.hStdInput)
1137 if (0 == CloseHandle (context.hStdInput))
1138 warn_last_error ("Closing child input stream");
1139 if (0 == CloseHandle (context.hStdOutput))
1140 warn_last_error ("Closing child output stream");
1141
1142 /* Clean up argument string*/
1143 free (merged);
1144
1145 /* Return if we failed to create the child process */
1146 if (ST_SYSTEM_ERROR == result->status)
1147 return;
1148
1149#if _WIN32_WINNT >= 0x0500
1150 if (options->as) {
1151 if (HANDLE hJob = CreateJobObject (NULL, NULL)) {
1152 if (AssignProcessToJobObject (hJob, child.hProcess)) {
1153 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
1154
1155 job_info.BasicLimitInformation.LimitFlags =
1156 JOB_OBJECT_LIMIT_PROCESS_MEMORY;
1157
1158 const rw_rlimit* as = options->as;
1159 job_info.ProcessMemoryLimit =
1160 as->rlim_cur < as->rlim_max ? as->rlim_cur : as->rlim_max;
1161
1162 if (!SetInformationJobObject (hJob,
1163 JobObjectExtendedLimitInformation,
1164 &job_info, sizeof (job_info)))
1165 warn_last_error ("Setting process limits");
1166 }
1167 else
1168 warn_last_error ("Assigning process to job object");
1169
1170 if (!CloseHandle (hJob))
1171 warn_last_error ("Closing job object handle");
1172 }
1173 else
1174 warn_last_error ("Creating job object");
1175 }
1176#endif // _WIN32_WINNT >= 0x0500
1177
1178 /* Check the wall clock before resuming the process */
1179 GetSystemTimeAsFileTime(&start);
1180
1181 if (DWORD (-1) == ResumeThread (child.hThread))
1182 warn_last_error ("Resuming process");
1183
1184 if (0 == CloseHandle (child.hThread))
1185 warn_last_error ("Closing child main thread handle");
1186
1187 /* Wait for the child process to terminate */
1188 wait_code = WaitForSingleObject (child.hProcess, real_timeout);
1189
1190 switch (wait_code) {
1191 case WAIT_OBJECT_0:
1192 break;
1193 case WAIT_TIMEOUT:
1194 /* Child process didn't shut down, so note that we killed it. */
1195 result->status = ST_KILLED;
1196
1197 kill_child_process (child, result);
1198 break;
1199 default:
1200 result->status = ST_SYSTEM_ERROR;
1201 warn_last_error ("Waiting for child process");
1202 }
1203
1204 /* Calculate wall clock time elapsed while the process ran */
1205 GetSystemTimeAsFileTime(&end);
1206
1207 /* 100 nanosecond units in a second */
1208 const DWORD UNITS_PER_SEC = 10000000;
1209 const DWORD UNITS_PER_CLOCK = UNITS_PER_SEC / CLOCKS_PER_SEC;
1210 assert (UNITS_PER_CLOCK * CLOCKS_PER_SEC == UNITS_PER_SEC);
1211
1212#if _WIN32_WINNT >= 0x0500
1213 FILETIME stime, utime;
1214
1215 if (GetProcessTimes (child.hProcess, &start, &end, &stime, &utime)) {
1216 result->usr_time = clock_t (fttoull (utime) / UNITS_PER_CLOCK);
1217 result->sys_time = clock_t (fttoull (stime) / UNITS_PER_CLOCK);
1218 }
1219 else
1220 warn_last_error ("Getting child process times");
1221#endif // _WIN32_WINNT >= 0x0500
1222
1223 result->wall_time =
1224 clock_t ((fttoull (end) - fttoull (start)) / UNITS_PER_CLOCK);
1225
1226
1227 if (0 == GetExitCodeProcess (child.hProcess, (LPDWORD)&result->exit)) {
1228 warn_last_error ("Retrieving child process exit code");
1229 result->status = ST_SYSTEM_ERROR;
1230 }
1231
1232 if (0 == CloseHandle (child.hProcess))
1233 warn_last_error ("Closing child process handle");
1234
1235 for (int i = 0; i < sizeof (nt_status_map) / sizeof (*nt_status_map); ++i) {
1236 if (nt_status_map [i].nt_status == DWORD (result->exit)) {
1237 result->exit = nt_status_map [i].signal;
1238 result->signaled = 1;
1239 break;
1240 }
1241 }
1242}
1243
1244#endif /* _WIN32 */
#define i
Definition 17.names.cpp:126
#define s1
Definition 17.names.cpp:242
#define start
Definition 17.names.cpp:547
#define s2
Definition 17.names.cpp:268
#define UINT
Definition 17.names.cpp:565
#define len
Definition 17.names.cpp:488
#define mode
Definition 17.names.cpp:497
#define pos
Definition 17.names.cpp:518
#define INT
Definition 17.names.cpp:483
volatile int result
const char *const data[]
#define open(f, m)
#define close(f)
#define INFO(op)
const int end
void terminate()
IMPORT_C UInt32 sleep(UInt32 seconds)
#define assert(expr)
Definition _cassert.h:40
#define NULL
#define SIG_DFL
#define SIGINT
#define SIGILL
#define SIGTERM
#define SIGFPE
#define SIGSEGV
#define FILE
#define CLOCKS_PER_SEC
const char * get_target()
Definition runall.cpp:411
const char * name
Definition codecvt1.cpp:72
#define ENOENT
Definition exec.cpp:93
int get_signo(const char *signame)
Definition exec.cpp:365
void exec_file(const struct target_opts *options, struct target_status *result)
Definition exec.cpp:807
#define SIGHUP
Definition exec.cpp:79
void(* alarm_handler)(int)
Definition exec.cpp:448
#define EINVAL
Definition exec.cpp:105
int kill(pid_t pid, int sig)
#define ESRCH
Definition exec.cpp:96
#define EINTR
Definition exec.cpp:99
#define ECHILD
Definition exec.cpp:102
#define SIGQUIT
Definition exec.cpp:82
const char * get_signame(int signo)
Definition exec.cpp:397
int val
Definition exec.cpp:169
#define SIGNAL(val)
Definition exec.cpp:158
#define SIGALRM
Definition exec.cpp:88
#define SIGKILL
Definition exec.cpp:85
const char * str
Definition exec.cpp:170
FILE * fdopen(int fd, const char *mode)
@ error
throw a parse_error exception in case of a tag
unsigned char UChar
Definition collate.cpp:122
rw_rlim_t rlim_cur
Definition target.h:51
rw_rlim_t rlim_max
Definition target.h:52
unsigned timeout
Definition target.h:77
char ** argv
Definition target.h:76
const char * infname
Definition target.h:81
rw_rlimit * as
Definition target.h:91
const char * outfname
Definition target.h:82
@ ST_KILLED
Definition target.h:111
@ ST_EXIST
Definition target.h:101
@ ST_EXECUTE
Definition target.h:103
@ ST_SYSTEM_ERROR
Definition target.h:110
@ ST_NOT_KILLED
Definition target.h:112
const char escape_code
Definition cmdopt.cpp:59
void warn(const char *const format,...)
Definition util.cpp:58
int rw_signal(int signo, void(*func)(int))
Definition util.cpp:259
#define RW_MALLOC(size)
Definition util.h:51