On Linux, waitpid() both waits for the process to terminate and reaps it (closes its handle). On Windows, these actions are separated into WaitForSingleObject() and CloseHandle(). Expose these actions separately, so that it is possible to send a signal to a process while waiting for its termination without race condition. This allows to wait for server termination normally, but kill the process without race condition if it is not terminated after some delay.
130 lines
3.0 KiB
C
130 lines
3.0 KiB
C
#include "util/process.h"
|
|
|
|
#include <assert.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "config.h"
|
|
#include "util/log.h"
|
|
#include "util/str_util.h"
|
|
|
|
static int
|
|
build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
|
// Windows command-line parsing is WTF:
|
|
// <http://daviddeley.com/autohotkey/parameters/parameters.htm#WINPASS>
|
|
// only make it work for this very specific program
|
|
// (don't handle escaping nor quotes)
|
|
size_t ret = xstrjoin(cmd, argv, ' ', len);
|
|
if (ret >= len) {
|
|
LOGE("Command too long (%" PRIsizet " chars)", len - 1);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
enum process_result
|
|
process_execute(const char *const argv[], HANDLE *handle) {
|
|
STARTUPINFOW si;
|
|
PROCESS_INFORMATION pi;
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
|
|
char cmd[256];
|
|
if (build_cmd(cmd, sizeof(cmd), argv)) {
|
|
*handle = NULL;
|
|
return PROCESS_ERROR_GENERIC;
|
|
}
|
|
|
|
wchar_t *wide = utf8_to_wide_char(cmd);
|
|
if (!wide) {
|
|
LOGC("Could not allocate wide char string");
|
|
return PROCESS_ERROR_GENERIC;
|
|
}
|
|
|
|
if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, 0, NULL, NULL, &si,
|
|
&pi)) {
|
|
SDL_free(wide);
|
|
*handle = NULL;
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
|
return PROCESS_ERROR_MISSING_BINARY;
|
|
}
|
|
return PROCESS_ERROR_GENERIC;
|
|
}
|
|
|
|
SDL_free(wide);
|
|
*handle = pi.hProcess;
|
|
return PROCESS_SUCCESS;
|
|
}
|
|
|
|
bool
|
|
process_terminate(HANDLE handle) {
|
|
return TerminateProcess(handle, 1);
|
|
}
|
|
|
|
static bool
|
|
process_wait_internal(HANDLE handle, DWORD *exit_code, bool close) {
|
|
DWORD code;
|
|
if (WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0
|
|
|| !GetExitCodeProcess(handle, &code)) {
|
|
// could not wait or retrieve the exit code
|
|
code = -1; // max value, it's unsigned
|
|
}
|
|
if (exit_code) {
|
|
*exit_code = code;
|
|
}
|
|
if (close) {
|
|
CloseHandle(handle);
|
|
}
|
|
return !code;
|
|
}
|
|
|
|
bool
|
|
process_wait(HANDLE handle, DWORD *exit_code) {
|
|
return process_wait_internal(handle, exit_code, true);
|
|
}
|
|
|
|
bool
|
|
process_wait_noclose(HANDLE handle, DWORD *exit_code) {
|
|
return process_wait_internal(handle, exit_code, false);
|
|
}
|
|
|
|
void
|
|
process_close(HANDLE handle) {
|
|
bool closed = CloseHandle(handle);
|
|
assert(closed);
|
|
(void) closed;
|
|
}
|
|
|
|
char *
|
|
get_executable_path(void) {
|
|
HMODULE hModule = GetModuleHandleW(NULL);
|
|
if (!hModule) {
|
|
return NULL;
|
|
}
|
|
WCHAR buf[MAX_PATH + 1]; // +1 for the null byte
|
|
int len = GetModuleFileNameW(hModule, buf, MAX_PATH);
|
|
if (!len) {
|
|
return NULL;
|
|
}
|
|
buf[len] = '\0';
|
|
return utf8_from_wide_char(buf);
|
|
}
|
|
|
|
bool
|
|
is_regular_file(const char *path) {
|
|
wchar_t *wide_path = utf8_to_wide_char(path);
|
|
if (!wide_path) {
|
|
LOGC("Could not allocate wide char string");
|
|
return false;
|
|
}
|
|
|
|
struct _stat path_stat;
|
|
int r = _wstat(wide_path, &path_stat);
|
|
SDL_free(wide_path);
|
|
|
|
if (r) {
|
|
perror("stat");
|
|
return false;
|
|
}
|
|
return S_ISREG(path_stat.st_mode);
|
|
}
|