Index: timeit.c =================================================================== --- timeit.c +++ timeit.c @@ -10,15 +10,19 @@ #include #include #include +#include +#ifdef _WIN32 +#include +#else #include -#include #include - #include -#include #include +#endif /*#ifdef _WIN32*/ +#include + /* Enumeration for our exit status codes. */ enum ExitCode { /* \brief Indicates a failure monitoring the target. */ @@ -51,9 +55,30 @@ */ static int g_timeout_in_seconds = 0; -/* \brief If non-zero, the PID of the process being monitored. */ -static pid_t g_monitored_pid = 0; +#ifdef _WIN32 +typedef HANDLE process_id_t; +typedef DWORD exit_status_t; +/* \brief Job object used to kill the monitored process with all its children*/ +static HANDLE g_job_object = INVALID_HANDLE_VALUE; + +/* \brief Handle of the timer used to close the monitored app by timeout*/ +static HANDLE timer_handle = INVALID_HANDLE_VALUE; + +static HANDLE g_child_stdin_rd = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdin_wr = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdout_rd = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdout_wr = INVALID_HANDLE_VALUE; + +static const int FILE_BUF_SIZE = 4096; +#else +typedef pid_t process_id_t; +typedef int exit_status_t; +#endif /*#ifdef _WIN32*/ + +/* \brief The PID (*nix) or HANDLE (Windows) of the process being monitored. */ +static process_id_t g_monitored_pid; + /* \brief If non-zero, the path to attempt to chdir() to before executing the * target. */ static const char *g_target_exec_directory = 0; @@ -72,91 +97,470 @@ /* @name Resource Limit Variables */ /* @{ */ +#ifndef _WIN32 /* \brief If non-sentinel, the CPU time limit to set for the target. */ -static rlim_t g_target_cpu_limit = ~(rlim_t) 0; +static rlim_t g_target_cpu_limit = ~(rlim_t)0; /* \brief If non-sentinel, the stack size limit to set for the target. */ -static rlim_t g_target_stack_size_limit = ~(rlim_t) 0; +static rlim_t g_target_stack_size_limit = ~(rlim_t)0; /* \brief If non-sentinel, the data size limit to set for the target. */ -static rlim_t g_target_data_size_limit = ~(rlim_t) 0; +static rlim_t g_target_data_size_limit = ~(rlim_t)0; /* \brief If non-sentinel, the RSS size limit to set for the target. */ -static rlim_t g_target_rss_size_limit = ~(rlim_t) 0; +static rlim_t g_target_rss_size_limit = ~(rlim_t)0; /* \brief If non-sentinel, the file size limit to set for the target. */ -static rlim_t g_target_file_size_limit = ~(rlim_t) 0; +static rlim_t g_target_file_size_limit = ~(rlim_t)0; /* \brief If non-sentinel, the core limit to set for the target. */ -static rlim_t g_target_core_limit = ~(rlim_t) 0; +static rlim_t g_target_core_limit = ~(rlim_t)0; /* \brief If non-sentinel, the file count limit to set for the target. */ -static rlim_t g_target_file_count_limit = ~(rlim_t) 0; +static rlim_t g_target_file_count_limit = ~(rlim_t)0; /* \brief If non-sentinel, the subprocess count limit to set for the target. */ -static rlim_t g_target_subprocess_count_limit = ~(rlim_t) 0; +static rlim_t g_target_subprocess_count_limit = ~(rlim_t)0; /* @} */ +#else /*#ifndef _WIN32*/ +/* When an unhandled fatal signal terminates a process, the exit code is 3. */ +#define WIFSIGNALED(x) ((x) == 3) +#define WIFEXITED(x) ((x) != 3) + +/* The signal that terminated a process is not known posthum. */ +#define WTERMSIG(x) SIGTERM +#define WEXITSTATUS(x) (x) + +/* \brief Formats a readable Win error message. */ +static void win_error(PTSTR function_name) { + void *msg_buf, *display_buf; + DWORD error = GetLastError(); + const char *extra_text = "failed with error"; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg_buf, 0, NULL); + + display_buf = (void *)LocalAlloc(LMEM_ZEROINIT, + strlen((const char *)msg_buf) + + strlen((const char *)function_name) + + strlen(extra_text) + 10); + + sprintf(display_buf, "%s %s %d: %s", function_name, extra_text, error, + msg_buf); + fprintf(stderr, display_buf); + + LocalFree(msg_buf); + LocalFree(display_buf); +} + +static void close_handles() { + if (timer_handle != INVALID_HANDLE_VALUE) + CloseHandle(timer_handle); + + if (g_monitored_pid != INVALID_HANDLE_VALUE) + CloseHandle(g_monitored_pid); + + if (g_job_object != INVALID_HANDLE_VALUE) + CloseHandle(g_job_object); + + if (g_child_stdin_rd != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdin_rd); + + if (g_child_stdin_wr != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdin_wr); + + if (g_child_stdout_rd != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdout_rd); + + if (g_child_stdout_wr != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdout_wr); + + timer_handle = g_monitored_pid = g_job_object = g_child_stdin_rd = + g_child_stdin_wr = g_child_stdout_rd = g_child_stdout_wr = + INVALID_HANDLE_VALUE; +} + +static int create_pipe(HANDLE *read_handle, HANDLE *write_handle) { + SECURITY_ATTRIBUTES security_attrs; + + security_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attrs.bInheritHandle = TRUE; + security_attrs.lpSecurityDescriptor = NULL; + + if (CreatePipe(read_handle, write_handle, &security_attrs, 0) == 0) { + win_error("CreatePipe"); + return 0; + } + + return 1; +} + +static int make_handle_not_inherited(HANDLE handle) { + if (SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) == 0) { + win_error("SetHandleInformation"); + return 0; + } + + return 1; +} + +static int set_current_dir(const char *dir); + +static int execute_target_process(char *const argv[]) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_object_info = {0}; + STARTUPINFO proc_start_info; + PROCESS_INFORMATION process_info; + + /*Create unnamed pipes to redirect stdout and stdin for the child process if + * necessary.*/ + /*Read handle to the pipe for stdout and write handle to the pipe for stdin + must not be inherited by the monitored child process*/ + if (g_target_redirect_input) { + if (create_pipe(&g_child_stdin_rd, &g_child_stdin_wr) == 0 || + make_handle_not_inherited(g_child_stdin_wr) == 0) { + close_handles(); + return 0; + } + } + + if (g_target_redirect_output) { + if (create_pipe(&g_child_stdout_rd, &g_child_stdout_wr) == 0 || + make_handle_not_inherited(g_child_stdout_rd) == 0) { + close_handles(); + return 0; + } + } + + /*create a job object*/ + g_job_object = CreateJobObject(NULL, NULL); + + if (g_job_object == INVALID_HANDLE_VALUE) { + win_error("CreateJobObject"); + close_handles(); + return 0; + } + + /*make all associated child processes terminate when the job closes*/ + job_object_info.BasicLimitInformation.LimitFlags = + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + if (SetInformationJobObject(g_job_object, JobObjectExtendedLimitInformation, + &job_object_info, sizeof(job_object_info)) == 0) { + win_error("SetInformationJobObject"); + close_handles(); + return 0; + } + + /* Honor the desired target execute directory. */ + if (g_target_exec_directory && + set_current_dir(g_target_exec_directory) == 0) { + close_handles(); + return 0; + } + + ZeroMemory(&proc_start_info, sizeof(STARTUPINFO)); + proc_start_info.cb = sizeof(STARTUPINFO); + proc_start_info.hStdError = g_child_stdout_wr; + proc_start_info.hStdOutput = g_child_stdout_wr; + proc_start_info.hStdInput = g_child_stdin_rd; + proc_start_info.dwFlags |= STARTF_USESTDHANDLES; + + if (CreateProcess(NULL, strstr(GetCommandLine(), argv[0]), NULL, NULL, TRUE, + 0, NULL, NULL, &proc_start_info, &process_info) == 0) { + win_error("CreateProcess"); + close_handles(); + return 0; + } + + g_monitored_pid = process_info.hProcess; + + /*we do not need thread handle of the monitored process*/ + /*Child-side stdout/stdin handles are also no longer needed*/ + CloseHandle(process_info.hThread); + + if (g_child_stdout_wr != INVALID_HANDLE_VALUE) { + CloseHandle(g_child_stdout_wr); + } + + if (g_child_stdin_rd != INVALID_HANDLE_VALUE) { + CloseHandle(g_child_stdin_rd); + } + + g_child_stdout_wr = g_child_stdin_rd = INVALID_HANDLE_VALUE; + + /*assign the monitored process to the job*/ + if (AssignProcessToJobObject(g_job_object, g_monitored_pid) == 0) { + win_error("AssignProcessToJobObject"); + close_handles(); + return 0; + } + + return 1; +} + +static int redirect_target_input_from_file(const char *file_name) { + HANDLE file_handle = CreateFile(file_name, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (file_handle == INVALID_HANDLE_VALUE) { + win_error("CreateFile"); + close_handles(); + return 0; + } + + DWORD bytes_read, bytes_written; + char buf[FILE_BUF_SIZE]; + + for (;;) { + if (ReadFile(file_handle, buf, FILE_BUF_SIZE, &bytes_read, NULL) == 0 || + bytes_read == 0) { + break; + } + + if (WriteFile(g_child_stdin_wr, buf, bytes_read, &bytes_written, NULL) == + 0) { + break; + } + } + + CloseHandle(file_handle); + + /*close the pipe handle to make the child stop reading*/ + CloseHandle(g_child_stdin_wr); + + return 1; +} + +static int redirect_target_output_to_file(const char *file_name) { + DWORD bytes_read, bytes_written; + char buf[FILE_BUF_SIZE]; + + HANDLE file_handle = CreateFile(file_name, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file_handle == INVALID_HANDLE_VALUE) { + win_error("CreateFile"); + close_handles(); + return 0; + } + + for (;;) { + if (ReadFile(g_child_stdout_rd, buf, FILE_BUF_SIZE, &bytes_read, NULL) == + 0 || + bytes_read == 0) { + break; + } + + if (WriteFile(file_handle, buf, bytes_read, &bytes_written, NULL) == 0) { + break; + } + } + + CloseHandle(file_handle); + + return 1; +} + +#endif /*#ifndef _WIN32*/ + +static void kill_monitored_process() { +#ifdef _WIN32 + if (g_job_object != INVALID_HANDLE_VALUE) { + CloseHandle(g_job_object); + g_job_object = INVALID_HANDLE_VALUE; + } +#else + kill(-g_monitored_pid, SIGKILL); +#endif +} + +static int wait_for_monitored_process(exit_status_t *exit_status) { +#ifdef _WIN32 + if (WaitForSingleObject(g_monitored_pid, INFINITE) < 0) { + win_error("WaitForSingleObject"); + close_handles(); + return 0; + } + + GetExitCodeProcess(g_monitored_pid, exit_status); +#else + int res; + + do { + res = waitpid(g_monitored_pid, exit_status, 0); + } while (res < 0 && errno == EINTR); + if (res < 0) { + perror("waitpid"); + return 0; + } +#endif /*#ifdef _WIN32*/ + + return 1; +} + +struct cpu_usage_t { + double user_time; + double system_time; +}; + +static int get_cpu_usage(struct cpu_usage_t *cpu_usage) { +#ifdef _WIN32 + FILETIME creation_ftime, exit_ftime, kernel_ftime, user_ftime; + SYSTEMTIME kernel_time, user_time; + + if (GetProcessTimes(g_monitored_pid, &creation_ftime, &exit_ftime, + &kernel_ftime, &user_ftime) == 0) { + win_error("GetProcessTimes"); + close_handles(); + return 0; + } + + if (FileTimeToSystemTime(&kernel_ftime, &kernel_time) == 0 || + FileTimeToSystemTime(&user_ftime, &user_time) == 0) { + win_error("FileTimeToSystemTime"); + close_handles(); + return 0; + } + + cpu_usage->user_time = (double)user_time.wHour * 3600 + + user_time.wMinute * 60 + user_time.wSecond + + ((double)user_time.wMilliseconds) / 1000; + cpu_usage->system_time = (double)kernel_time.wHour * 3600 + + kernel_time.wMinute * 60 + kernel_time.wSecond + + ((double)kernel_time.wMilliseconds) / 1000; +#else + struct rusage usage; + + if (getrusage(RUSAGE_CHILDREN, &usage) < 0) { + perror("getrusage"); + return 0; + } + + cpu_usage->user_time = + (double)usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.0; + cpu_usage->system_time = + (double)usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.0; +#endif /*#ifdef _WIN32*/ + + return 1; +} + +static int set_current_dir(const char *dir) { +#ifdef _WIN32 + if (SetCurrentDirectory(dir) == 0) { + win_error("SetCurrentDirectory"); + return 0; + } +#else + if (chdir(dir) < 0) { + perror("chdir"); + return 0; + } +#endif /*#ifdef _WIN32*/ + + return 1; +} + static double sample_wall_time(void) { struct timeval t; gettimeofday(&t, NULL); - return (double) t.tv_sec + t.tv_usec * 1.e-6; + return (double)t.tv_sec + t.tv_usec * 1.e-6; } static void terminate_handler(int signal) { - /* If we are monitoring a process, kill its process group and assume we will +/* If we are monitoring a process, kill its process group and assume we will * complete normally. */ +#ifdef _WIN32 + if (g_monitored_pid != INVALID_HANDLE_VALUE) { +#else if (g_monitored_pid) { +#endif /*#ifdef _WIN32*/ fprintf(stderr, ("%s: error: received signal %d. " "killing monitored process(es): %s\n"), g_program_name, signal, g_target_program); - /* Kill the process group of monitored_pid. */ - kill(-g_monitored_pid, SIGKILL); + /* Kill the monitored process group. */ + kill_monitored_process(); return; } - fprintf(stderr, "%s: error: received signal %d. exiting.\n", - g_program_name, signal); - /* Otherwise, we received a signal we should treat as for ourselves, and exit + fprintf(stderr, "%s: error: received signal %d. exiting.\n", g_program_name, + signal); + +/* Otherwise, we received a signal we should treat as for ourselves, and exit * quickly. */ +#ifdef __WIN32 + close_handles(); +#endif + _exit(EXITCODE_SIGNALLED); } +#ifdef _WIN32 +static VOID CALLBACK timeout_handler(PVOID param, BOOLEAN timer_or_wait_fired) { +#else static void timeout_handler(int signal) { +#endif fprintf(stderr, "%s: TIMING OUT PROCESS: %s\n", g_program_name, g_target_program); /* We should always be monitoring a process when we receive an alarm. Kill its * process group and assume we will terminate normally. */ - kill(-g_monitored_pid, SIGKILL); + kill_monitored_process(); } -int monitor_child_process(pid_t pid, double start_time) { +/* Sets up a signal handler to terminate the process on timeout. */ +static int set_alarm(int timeout_in_seconds) { +#ifdef _WIN32 + if (CreateTimerQueueTimer(&timer_handle, 0, + (WAITORTIMERCALLBACK)timeout_handler, 0, + timeout_in_seconds * 1000, 0, 0) == 0) { + win_error("CreateTimerQueueTimer"); + close_handles(); + return 0; + } +#else + signal(SIGALRM, timeout_handler); + alarm(timeout_in_seconds); +#endif /*#ifdef _WIN32*/ + + return 1; +} + +int monitor_child_process(double start_time) { double real_time, user_time, sys_time; - struct rusage usage; - int res, status; - /* Record the PID we are monitoring, for use in the signal handlers. */ - g_monitored_pid = pid; - /* If we are running with a timeout, set up an alarm now. */ if (g_timeout_in_seconds) { - sigset_t masked; - sigemptyset(&masked); - sigaddset(&masked, SIGALRM); + if (set_alarm(g_timeout_in_seconds) == 0) { + return EXITCODE_MONITORING_FAILURE; + } + } - alarm(g_timeout_in_seconds); +#ifdef _WIN32 + /*if stdin for the monitored process is redirected to be read from a file, + read the file and write its content to the child's stdin*/ + if (g_target_redirect_input) { + if (redirect_target_input_from_file(g_target_redirect_input) == 0) { + return EXITCODE_MONITORING_FAILURE; } + } + if (g_target_redirect_output) { + if (redirect_target_output_to_file(g_target_redirect_output) == 0) { + return EXITCODE_MONITORING_FAILURE; + } + } +#endif /*#ifdef _WIN32*/ + /* Wait for the process to terminate. */ - do { - res = waitpid(pid, &status, 0); - } while (res < 0 && errno == EINTR); - if (res < 0) { - perror("waitpid"); + exit_status_t exit_status; + if (wait_for_monitored_process(&exit_status) == 0) { return EXITCODE_MONITORING_FAILURE; } @@ -164,26 +568,27 @@ real_time = sample_wall_time() - start_time; /* Just in case, kill the child process group. */ - kill(-pid, SIGKILL); + kill_monitored_process(); /* Collect the other resource data on the children. */ - if (getrusage(RUSAGE_CHILDREN, &usage) < 0) { - perror("getrusage"); + struct cpu_usage_t cpu_usage; + if (get_cpu_usage(&cpu_usage) == 0) { return EXITCODE_MONITORING_FAILURE; } - user_time = (double) usage.ru_utime.tv_sec + usage.ru_utime.tv_usec/1000000.0; - sys_time = (double) usage.ru_stime.tv_sec + usage.ru_stime.tv_usec/1000000.0; +#ifdef __WIN32 + close_handles(); +#endif + /* If the process was signalled, report a more interesting status. */ - int exit_status; - if (WIFSIGNALED(status)) { + if (WIFSIGNALED(exit_status)) { fprintf(stderr, "%s: error: child terminated by signal %d\n", - g_program_name, WTERMSIG(status)); + g_program_name, WTERMSIG(exit_status)); /* Propagate the signalled status to the caller. */ - exit_status = 128 + WTERMSIG(status); - } else if (WIFEXITED(status)) { - exit_status = WEXITSTATUS(status); + exit_status = 128 + WTERMSIG(exit_status); + } else if (WIFEXITED(exit_status)) { + exit_status = WEXITSTATUS(exit_status); } else { /* This should never happen, but if it does assume some kind of failure. */ exit_status = EXITCODE_MONITORING_FAILURE; @@ -193,11 +598,11 @@ // would. if (!g_summary_file) { if (g_posix_mode) { - fprintf(stderr, "real %12.4f\nuser %12.4f\nsys %12.4f\n", - real_time, user_time, sys_time); + fprintf(stderr, "real %12.4f\nuser %12.4f\nsys %12.4f\n", real_time, + cpu_usage.user_time, cpu_usage.system_time); } else { - fprintf(stderr, "%12.4f real %12.4f user %12.4f sys\n", - real_time, user_time, sys_time); + fprintf(stderr, "%12.4f real %12.4f user %12.4f sys\n", real_time, + cpu_usage.user_time, cpu_usage.system_time); } } else { /* Otherwise, write the summary data in a simple parsable format. */ @@ -209,8 +614,8 @@ fprintf(fp, "exit %d\n", exit_status); fprintf(fp, "%-10s %.4f\n", "real", real_time); - fprintf(fp, "%-10s %.4f\n", "user", user_time); - fprintf(fp, "%-10s %.4f\n", "sys", sys_time); + fprintf(fp, "%-10s %.4f\n", "user", cpu_usage.user_time); + fprintf(fp, "%-10s %.4f\n", "sys", cpu_usage.system_time); fclose(fp); } @@ -217,6 +622,7 @@ return exit_status; } +#ifndef _WIN32 #define set_resource_limit(resource, value) \ set_resource_limit_actual(#resource, resource, value) static void set_resource_limit_actual(const char *resource_name, int resource, @@ -227,16 +633,16 @@ /* Set the limits to as close as requested, assuming we are not super-user. */ struct rlimit requested; - requested.rlim_cur = requested.rlim_max = \ + requested.rlim_cur = requested.rlim_max = (value < current.rlim_max) ? value : current.rlim_max; if (setrlimit(resource, &requested) < 0) { fprintf(stderr, "%s: warning: unable to set limit for %s (to {%lu, %lu})\n", - g_program_name, resource_name, (unsigned long) requested.rlim_cur, - (unsigned long) requested.rlim_max); + g_program_name, resource_name, (unsigned long)requested.rlim_cur, + (unsigned long)requested.rlim_max); } } -static int execute_target_process(char * const argv[]) { +static int execute_target_process(char *const argv[]) { /* Create a new process group for pid, and the process tree it may spawn. We * do this, because later on we might want to kill pid _and_ all processes * spawned by it and its descendants. @@ -278,38 +684,36 @@ } /* Honor any requested resource limits. */ - if (g_target_cpu_limit != ~(rlim_t) 0) { + if (g_target_cpu_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_CPU, g_target_cpu_limit); } - if (g_target_stack_size_limit != ~(rlim_t) 0) { + if (g_target_stack_size_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_STACK, g_target_stack_size_limit); } - if (g_target_data_size_limit != ~(rlim_t) 0) { + if (g_target_data_size_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_DATA, g_target_data_size_limit); } - if (g_target_rss_size_limit != ~(rlim_t) 0) { + if (g_target_rss_size_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_RSS, g_target_rss_size_limit); } - if (g_target_file_size_limit != ~(rlim_t) 0) { + if (g_target_file_size_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_FSIZE, g_target_file_size_limit); } - if (g_target_core_limit != ~(rlim_t) 0) { + if (g_target_core_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_CORE, g_target_core_limit); } - if (g_target_file_count_limit != ~(rlim_t) 0) { + if (g_target_file_count_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_NOFILE, g_target_file_count_limit); } - if (g_target_subprocess_count_limit != ~(rlim_t) 0) { + if (g_target_subprocess_count_limit != ~(rlim_t)0) { set_resource_limit(RLIMIT_NPROC, g_target_subprocess_count_limit); } - + /* Honor the desired target execute directory. */ - if (g_target_exec_directory) { - if (chdir(g_target_exec_directory) < 0) { - perror("chdir"); + if (g_target_exec_directory && + set_current_dir(g_target_exec_directory) == 0) { return EXITCODE_MONITORING_FAILURE; } - } execvp(argv[0], argv); perror("execv"); @@ -322,10 +726,11 @@ return EXITCODE_EXEC_FAILURE; } +#endif /*#ifndef _WIN32*/ -int execute(char * const argv[]) { +int execute(char *const argv[]) { double start_time; - pid_t pid; + process_id_t pid; /* Set up signal handlers so we can terminate the monitored process(es) on * SIGINT or SIGTERM. */ @@ -332,40 +737,41 @@ signal(SIGINT, terminate_handler); signal(SIGTERM, terminate_handler); - /* Set up a signal handler to terminate the process on timeout. */ - signal(SIGALRM, timeout_handler); - start_time = sample_wall_time(); +#ifdef _WIN32 + if (execute_target_process(argv) == 0) { + return EXITCODE_MONITORING_FAILURE; + } +#else /*#ifdef _WIN32*/ /* Fork the child process. */ - pid = fork(); - if (pid < 0) { + g_monitored_pid = fork(); + if (g_monitored_pid < 0) { perror("fork"); return EXITCODE_MONITORING_FAILURE; } /* If we are in the context of the child process, spawn it. */ - if (pid == 0) { + if (g_monitored_pid == 0) { /* Setup and execute the target process. This never returns except on * failure. */ return execute_target_process(argv); } - /* Otherwise, we are in the context of the monitoring process. */ - return monitor_child_process(pid, start_time); -} +/* Otherwise, we are in the context of the monitoring process. */ +#endif /*#ifdef _WIN32*/ -static int streq(const char *a, const char *b) { - return strcmp(a, b) == 0; + return monitor_child_process(start_time); } +static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } + static void usage(int is_error) { #define WRAPPED "\n " fprintf(stderr, "usage: %s [options] command ... arguments ...\n", g_program_name); fprintf(stderr, "Options:\n"); - fprintf(stderr, " %-20s %s", "-h, --help", - "Show this help text.\n"); + fprintf(stderr, " %-20s %s", "-h, --help", "Show this help text.\n"); fprintf(stderr, " %-20s %s", "-p, --posix", "Report time in /usr/bin/time POSIX format.\n"); fprintf(stderr, " %-20s %s", "-t, --timeout ", @@ -394,13 +800,13 @@ (WRAPPED "Limit the maximum number of open files the target can have.\n")); fprintf(stderr, " %-20s %s", "--limit-subprocess-count ", - (WRAPPED - "Limit the maximum number of simultaneous processes " + (WRAPPED "Limit the maximum number of simultaneous processes " "the target can use.\n")); + _exit(is_error); } -int main(int argc, char * const argv[]) { +int main(int argc, char *const argv[]) { int i; g_program_name = argv[0]; @@ -465,6 +871,9 @@ } if (strncmp(arg, "--limit-", 8) == 0) { +#ifdef _WIN32 + ++i; +#else rlim_t value; if (i + 1 == argc) { @@ -493,6 +902,7 @@ fprintf(stderr, "error: invalid limit argument '%s'\n", arg); usage(/*is_error=*/1); } +#endif /*#ifdef _WIN32*/ continue; }