| /* ********************************************************** |
| * Copyright (c) 2009-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* |
| * Runs an app in the background (since cmake scripts can't do so). |
| * Takes in the app path and arguments (as separate parameters) |
| * and launches the app as a separate process. Prints the process id |
| * of that process to a file, if requested. On Linux prints the |
| * process id to stdout if the -pid option is not provided. |
| * |
| * Note that we can't easily print the pid to stoud on Windows: we |
| * can't dup stdout prior to spawning b/c then cmake will wait for the |
| * child to close that descriptor, which will never happen. If we use |
| * CreateFile("CONOUT$") it opens the raw console and doesn't work |
| * within cygwin shells. We could _spawnv another tool and have it |
| * _execv, but simpler to use pid file. |
| */ |
| |
| #include "configure.h" |
| #ifdef WINDOWS |
| # define _CRT_SECURE_NO_WARNINGS 1 |
| # define _CRT_SECURE_NO_DEPRECATE 1 |
| # include <windows.h> |
| # include <process.h> |
| # include <io.h> |
| #else |
| # include <unistd.h> |
| # include <sys/types.h> |
| # include <string.h> |
| #endif |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #ifdef WINDOWS |
| /* use ISO C++-conformant names */ |
| # define dup _dup |
| # define fileno _fileno |
| # define open _open |
| # define close _close |
| #endif |
| |
| /* S_IREAD and others are not defined in Android */ |
| #ifndef S_IREAD |
| # define S_IREAD S_IRUSR |
| # define S_IWRITE S_IWUSR |
| # define S_IEXEC S_IXUSR |
| #endif |
| |
| int |
| usage(const char *us) |
| { |
| fprintf(stderr, |
| "Usage: %s [-env <var> <value>] [-out <file>] [-pid <file>] <program> " |
| "<args...>\n", |
| us); |
| return 1; |
| } |
| |
| static const char * |
| mygetenv(const char *var) |
| { |
| #ifdef WINDOWS |
| static char buf[2048]; |
| int len = GetEnvironmentVariable(var, buf, sizeof(buf) / sizeof(buf[0])); |
| if (len == 0 || len > sizeof(buf) / sizeof(buf[0])) |
| return NULL; |
| else |
| return (const char *)buf; |
| #else |
| return getenv(var); |
| #endif |
| } |
| |
| #ifdef WINDOWS |
| /* GetProcessId() is XP+ only, and our DDK w2k libpath puts |
| * the w2k kernel32.lib ahead of compiler's kernel32.lib. |
| * For running tests on win2k we'll want win2k support anyway, so |
| * we directly query ntdll. |
| */ |
| typedef LONG NTSTATUS; |
| typedef LONG KPRIORITY; |
| |
| # define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) |
| |
| typedef struct _PROCESS_BASIC_INFORMATION { |
| NTSTATUS ExitStatus; |
| PVOID PebBaseAddress; |
| ULONG_PTR AffinityMask; |
| KPRIORITY BasePriority; |
| ULONG_PTR UniqueProcessId; |
| ULONG_PTR InheritedFromUniqueProcessId; |
| } PROCESS_BASIC_INFORMATION; |
| |
| # ifndef DR_PARAM_IN |
| # define DR_PARAM_IN |
| # endif |
| # ifndef DR_PARAM_OUT |
| # define DR_PARAM_OUT |
| # endif |
| |
| NTSYSAPI NTSTATUS NTAPI |
| NtQueryInformationProcess(DR_PARAM_IN HANDLE ProcessHandle, |
| DR_PARAM_IN ULONG ProcessInformationClass, |
| DR_PARAM_OUT PVOID ProcessInformation, |
| DR_PARAM_IN ULONG ProcessInformationLength, |
| DR_PARAM_OUT PULONG ReturnLength OPTIONAL); |
| |
| static int |
| process_id_from_handle(HANDLE h) |
| { |
| PROCESS_BASIC_INFORMATION info; |
| NTSTATUS res; |
| ULONG got; |
| memset(&info, 0, sizeof(info)); |
| res = NtQueryInformationProcess(h, 0 /*ProcessBasicInformation*/, &info, |
| sizeof(PROCESS_BASIC_INFORMATION), &got); |
| if (!NT_SUCCESS(res) || got != sizeof(PROCESS_BASIC_INFORMATION)) |
| return -1; |
| else /* though pointer-sized field, we truncate to int */ |
| return (int)info.UniqueProcessId; |
| } |
| #endif |
| |
| static void |
| redirect_stdouterr(const char *outfile) |
| { |
| int newout = -1; |
| int stdout_fd = fileno(stdout); |
| int stderr_fd = fileno(stderr); |
| if (outfile != NULL) { |
| newout = open(outfile, O_CREAT | O_WRONLY | O_TRUNC, S_IREAD | S_IWRITE); |
| if (newout < 0) { |
| perror("open new stdout failed"); |
| exit(1); |
| } |
| } |
| /* we must close these in order for cmake's execute_process() |
| * to stop waiting on the parent (setsid() or grandchild make |
| * no difference) |
| */ |
| /* somehow closing both and then dup'ing twice fails under |
| * some circumstances so we do one at a time |
| */ |
| if (close(stdout_fd) != 0 || (outfile != NULL && dup(newout) != stdout_fd)) { |
| fprintf(stderr, "stdout redirect FAILED"); |
| exit(1); |
| } |
| if (close(stderr_fd) != 0 || (outfile != NULL && dup(newout) != stderr_fd)) { |
| fprintf(stderr, "stderr redirect FAILED"); |
| exit(1); |
| } |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| #ifdef WINDOWS |
| HANDLE hchild; |
| int child; |
| #else |
| pid_t child; |
| #endif |
| int rc; |
| int arg_offs = 1; |
| const char *outfile = NULL; |
| const char *pidfile = NULL; |
| |
| if (argc < 2) { |
| return usage(argv[0]); |
| } |
| while (argv[arg_offs][0] == '-') { |
| if (strcmp(argv[arg_offs], "-env") == 0) { |
| if (argc <= arg_offs + 2) |
| return usage(argv[0]); |
| #if VERBOSE |
| fprintf(stderr, "setting env var \"%s\" to \"%s\"\n", argv[arg_offs + 1], |
| argv[arg_offs + 2]); |
| #endif |
| #ifdef WINDOWS |
| rc = SetEnvironmentVariable(argv[arg_offs + 1], argv[arg_offs + 2]); |
| #else |
| rc = setenv(argv[arg_offs + 1], argv[arg_offs + 2], 1 /*overwrite*/); |
| #endif |
| if (rc != 0 || |
| strcmp(mygetenv(argv[arg_offs + 1]), argv[arg_offs + 2]) != 0) { |
| fprintf(stderr, "error in setenv of \"%s\" to \"%s\"\n", |
| argv[arg_offs + 1], argv[arg_offs + 2]); |
| fprintf(stderr, "setenv returned %d\n", rc); |
| fprintf(stderr, "env var \"%s\" is now \"%s\"\n", argv[arg_offs + 1], |
| mygetenv(argv[arg_offs + 1])); |
| exit(1); |
| } |
| arg_offs += 3; |
| } else if (strcmp(argv[arg_offs], "-out") == 0) { |
| if (argc <= arg_offs + 1) |
| return usage(argv[0]); |
| outfile = argv[arg_offs + 1]; |
| arg_offs += 2; |
| } else if (strcmp(argv[arg_offs], "-pid") == 0) { |
| if (argc <= arg_offs + 1) |
| return usage(argv[0]); |
| pidfile = argv[arg_offs + 1]; |
| arg_offs += 2; |
| } else { |
| return usage(argv[0]); |
| } |
| if (argc - arg_offs < 1) |
| return usage(argv[0]); |
| } |
| |
| #ifdef UNIX |
| child = fork(); |
| if (child < 0) { |
| perror("ERROR on fork"); |
| } else if (child == 0) { |
| /* redirect std{out,err} */ |
| redirect_stdouterr(outfile); |
| execv(argv[arg_offs], argv + arg_offs /*include app*/); |
| fprintf(stderr, "execv of %s FAILED", argv[arg_offs]); |
| exit(1); |
| } |
| /* else, parent, and we continue below */ |
| if (pidfile == NULL) |
| printf("%d\n", child); |
| #else |
| /* redirect std{out,err} */ |
| redirect_stdouterr(outfile); |
| /* Do we want _P_DETACH instead of _P_NOWAIT? _P_DETACH doesn't return the |
| * child handle though. |
| */ |
| hchild = (HANDLE)_spawnv(_P_NOWAIT, argv[arg_offs], argv + arg_offs /*include app*/); |
| if (hchild == INVALID_HANDLE_VALUE) { |
| fprintf(stderr, "_spawnv of %s FAILED", argv[arg_offs]); |
| exit(1); |
| } |
| child = process_id_from_handle(hchild); |
| #endif |
| if (pidfile != NULL) { |
| FILE *f = fopen(pidfile, "w"); |
| if (f == NULL) { |
| perror("open pidfile failed"); |
| exit(1); |
| } |
| fprintf(f, "%d\n", child); |
| fclose(f); |
| } |
| return 0; |
| } |