43# include <sys/times.h>
45# include <sys/resource.h>
49# define _WIN32_WINNT 0x0500
64# ifndef STATUS_INVALID_PARAMETER
65# define STATUS_INVALID_PARAMETER ((DWORD)0xC000000DL)
67# ifndef STATUS_HEAP_CORRUPTION
68# define STATUS_HEAP_CORRUPTION ((DWORD)0xC0000374L)
70# ifndef STATUS_STACK_BUFFER_OVERRUN
71# define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409L)
73# ifndef STATUS_INVALID_CRUNTIME_PARAMETER
74# define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD)0xC0000417L)
109#include <sys/types.h>
117#if !defined (_WIN32) && !defined (_RWSTD_NO_PURE_C_HEADERS)
141static int alarm_timeout;
149static int kill_signal;
158#define SIGNAL(val) { SIG ## val, #val }
347rw_strcasecmp (
const char*
s1,
const char*
s2)
351 typedef unsigned char UChar;
358 }
while (*
s1++ && *
s2++ && 0 == delta);
368 typedef unsigned char UChar;
372 if (isdigit (signame [0])) {
374 int trans = int (strtol (signame, &junk, 10));
376 if (0 == *junk && 0 == errno)
382 if (
's' == tolower ((
UChar)signame [0])
383 &&
'i' == tolower ((
UChar)signame [1])
384 &&
'g' == tolower ((
UChar)signame [2]))
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;
400 static char def [32];
402 for (
i = 0; signal_names [
i].str; ++
i) {
403 if (signal_names [
i].
val == signo) {
404 return signal_names [
i].str;
409 sprintf (def,
"SIG#%d", signo);
426handle_alrm (
int signo)
442handle_term_signal (
int signo)
478 static const int signals [] = {
482 static const unsigned sigcount =
sizeof (signals) /
sizeof (
int);
489 int waitopts = WUNTRACED | WCONTINUED;
491 int waitopts = WUNTRACED;
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);
542 else if (WIFSIGNALED (status)) {
543 result->exit = WTERMSIG (status);
547 else if (WIFSTOPPED (status))
550 else if (WIFCONTINUED (status))
557 else if ((pid_t)-1 == wait_pid) {
558 if (
EINTR == errno && alarm_timeout) {
565 result->exit = WTERMSIG (stopped);
570 if (0 !=
kill (-child_pid, signals [siginx])) {
603 if (siginx >= sigcount) {
616 else if (
EINTR == errno && !alarm_timeout) {
619 else if (
EINVAL == errno) {
627 else if (
ECHILD == errno) {
629 warn (
"waitpid (%d) error: %s\n", (
int)child_pid,
634 warn (
"waitpid (%d) error: %s\n", (
int)child_pid,
638 else if ((pid_t)0 == wait_pid) {
652 while (siginx < sigcount && 0 ==
kill (-child_pid, signals [siginx])) {
665 if (0 > raise (kill_signal))
681replace_file (
int source,
int dest,
const char*
name)
690 result = dup2 (source, dest);
697 terminate (1,
"closing source for %s redirection failed: %s\n",
698 name, strerror (errno));
711#define LIMIT(val, idx) { RLIMIT_ ## val, options->idx, #val }
724 static const struct {
739 LIMIT (FSIZE, fsize),
742 LIMIT (NOFILE, nofile),
745 LIMIT (STACK, stack),
753 for (
size_t i = 0; limits [
i].name; ++
i) {
756 if (!limits [
i].limit)
759 memcpy (&local, limits [
i].limit,
sizeof local);
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));
786 const struct tms*
const h_tms)
794 c_clk = times (&c_tms);
796 if ((clock_t)-1 == c_clk) {
797 warn (
"Failed to retrieve ending times: %s", strerror (errno));
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;
809 const pid_t child_pid = fork ();
815 if (0 == child_pid) {
816 const char*
const target_name =
get_target ();
819 assert (0 != target_name);
823 const pid_t pgroup = setsid ();
824 if (getpid () != pgroup)
825 terminate (1,
"Error setting process group: %s\n",
831 const int error_cache = dup (2);
832 if (-1 == error_cache)
833 terminate (1,
"Error duplicating stderr: %s\n",
836 error_file =
fdopen (error_cache,
"a");
838 terminate (1,
"Error opening file handle from cloned "
839 "stderr file descriptor: %s\n", strerror (errno));
844 const int intermit =
open (options->
infname, O_RDONLY);
845 replace_file (intermit, 0,
"stdin");
853 O_WRONLY | O_CREAT | O_TRUNC,
854 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
857 terminate (1,
"Error opening %s for output redirection: "
858 "%s\n", options->
outfname, strerror (errno));
860 replace_file (intermit, 1,
"stdout");
864 if (-1 == dup2 (1, 2))
865 terminate (1,
"Redirection of stderr to stdout failed: %s\n",
869 limit_process (options);
872 execv (options->
argv [0], options->
argv);
883 if (-1 == child_pid) {
885 warn (
"Unable to create child process for %s: %s\n", options->
argv [0],
891 clock_t h_clk = times (&h_tms);
893 if ((clock_t)-1 != h_clk)
894 calculate_usage (
result, h_clk, &h_tms);
896 warn (
"Failed to retrieve start times: %s", strerror (errno));
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 }
938merge_argv (
char** argv)
948 for (opts = argv; *opts; ++opts) {
950 for (term = *opts; *term; ++term) {
958 for (opts = argv; *opts; ++opts) {
960 for (term = *opts; *term; ++term) {
979warn_last_error (
const char* action)
981 DWORD
error = GetLastError ();
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);
993 warn (
"%s failed with error %d. Additionally, FormatMessage "
994 "failed with error %d.\n", action, error, GetLastError ());
1010 OSVERSIONINFO OSVer;
1012 OSVer.dwOSVersionInfoSize =
sizeof (OSVERSIONINFO);
1013 if (0 == GetVersionEx (&OSVer)) {
1015 warn_last_error (
"Retrieving host version");
1021 if (VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) {
1029 { CTRL_C_EVENT,
"Sending child process Control-C" },
1030 { CTRL_BREAK_EVENT,
"Sending child process Control-Break" }
1033 for (
unsigned long i = 0;
1034 i <
sizeof (sig_events) /
sizeof (*sig_events); ++
i) {
1037 if (0 == GenerateConsoleCtrlEvent (sig_events [
i].signal_,
1039 warn_last_error (sig_events [
i].msg_);
1041 wait_code = WaitForSingleObject (child.hProcess, 1000);
1042 if (WAIT_TIMEOUT == wait_code)
1045 if (WAIT_OBJECT_0 != wait_code) {
1047 warn_last_error (
"Waiting for 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");
1060inline ULONGLONG fttoull (
const FILETIME& ft)
1062 ULARGE_INTEGER __ft;
1063 __ft.LowPart = ft.dwLowDateTime;
1064 __ft.HighPart = ft.dwHighDateTime;
1065 return __ft.QuadPart;
1071 PROCESS_INFORMATION child;
1072 STARTUPINFO context;
1073 SECURITY_ATTRIBUTES child_sa =
1074 {
sizeof (SECURITY_ATTRIBUTES),
NULL, TRUE};
1075 DWORD real_timeout, wait_code;
1082 real_timeout = (options->
timeout > 0) ? options->
timeout * 1000 : INFINITE;
1085 GetStartupInfo (&context);
1086 context.dwFlags = STARTF_USESTDHANDLES;
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) {
1097 warn_last_error (
"Opening child output stream");
1101 context.hStdError = context.hStdOutput;
1105 CreateFile (options->
infname, GENERIC_READ, FILE_SHARE_READ,
1106 &child_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL);
1108 if (INVALID_HANDLE_VALUE == context.hStdInput) {
1109 CloseHandle (context.hStdOutput);
1111 warn_last_error (
"Opening child input stream");
1116 merged = merge_argv (options->
argv);
1121 UINT old_mode = SetErrorMode (SEM_FAILCRITICALERRORS
1122 | SEM_NOGPFAULTERRORBOX);
1125 if (0 == CreateProcess (options->
argv [0], merged, 0, 0, 1,
1126 CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED, 0, 0, &context, &child)) {
1129 warn_last_error (
"Creating child process");;
1133 SetErrorMode (old_mode);
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");
1149#if _WIN32_WINNT >= 0x0500
1151 if (HANDLE hJob = CreateJobObject (
NULL,
NULL)) {
1152 if (AssignProcessToJobObject (hJob, child.hProcess)) {
1153 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
1155 job_info.BasicLimitInformation.LimitFlags =
1156 JOB_OBJECT_LIMIT_PROCESS_MEMORY;
1159 job_info.ProcessMemoryLimit =
1162 if (!SetInformationJobObject (hJob,
1163 JobObjectExtendedLimitInformation,
1164 &job_info,
sizeof (job_info)))
1165 warn_last_error (
"Setting process limits");
1168 warn_last_error (
"Assigning process to job object");
1170 if (!CloseHandle (hJob))
1171 warn_last_error (
"Closing job object handle");
1174 warn_last_error (
"Creating job object");
1179 GetSystemTimeAsFileTime(&
start);
1181 if (DWORD (-1) == ResumeThread (child.hThread))
1182 warn_last_error (
"Resuming process");
1184 if (0 == CloseHandle (child.hThread))
1185 warn_last_error (
"Closing child main thread handle");
1188 wait_code = WaitForSingleObject (child.hProcess, real_timeout);
1190 switch (wait_code) {
1197 kill_child_process (child,
result);
1201 warn_last_error (
"Waiting for child process");
1205 GetSystemTimeAsFileTime(&end);
1208 const DWORD UNITS_PER_SEC = 10000000;
1212#if _WIN32_WINNT >= 0x0500
1213 FILETIME stime, utime;
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);
1220 warn_last_error (
"Getting child process times");
1224 clock_t ((fttoull (end) - fttoull (
start)) / UNITS_PER_CLOCK);
1227 if (0 == GetExitCodeProcess (child.hProcess, (LPDWORD)&
result->exit)) {
1228 warn_last_error (
"Retrieving child process exit code");
1232 if (0 == CloseHandle (child.hProcess))
1233 warn_last_error (
"Closing child process handle");
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;
IMPORT_C UInt32 sleep(UInt32 seconds)
const char * get_target()
int get_signo(const char *signame)
void exec_file(const struct target_opts *options, struct target_status *result)
void(* alarm_handler)(int)
int kill(pid_t pid, int sig)
const char * get_signame(int signo)
FILE * fdopen(int fd, const char *mode)
@ error
throw a parse_error exception in case of a tag
void warn(const char *const format,...)
int rw_signal(int signo, void(*func)(int))