blob: d642bec1993fdcb5defe72092204c129dd528e9d [file]
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
#include <stdarg.h>
#include <stdio.h>
#include <wchar.h>
#include <windows.h>
#define PROVIDER_NAME L"JB-Restarter"
#define ERR_OPEN_PROCESS (0xE0000000 + 100)
#define ERR_MAIN_WAIT_FAILED (0xE0000000 + 101)
#define ERR_ARGS (0xE0000000 + 102)
#define ERR_COMMAND_TOO_LONG (0xE0000000 + 110)
#define ERR_CREATE_PROCESS (0xE0000000 + 111)
#define ERR_COMMAND_WAIT_FAILED (0xE0000000 + 112)
#define ERR_GET_EXIT_CODE (0xE0000000 + 113)
#define WARN_COMMAND_FAILED (0x80000000 + 200)
#define COMMAND_SIZE 32768
#define MESSAGE_SIZE (COMMAND_SIZE + 1024)
static HANDLE event_log = NULL;
static void log_event(unsigned int event_id, const char *format, ...);
static void wait_for_parent(const wchar_t *pid_arg);
static void run_command(int cmd_argc, wchar_t *cmd_argv[], BOOL last);
int wmain(int argc, wchar_t *argv[]) {
if (argc < 3) {
printf("usage: %ls <pid> (n_args args ...)+\n", argv[0]);
return 0;
}
event_log = RegisterEventSourceW(NULL, PROVIDER_NAME);
wait_for_parent(argv[1]);
int arg_index = 2;
while (arg_index < argc - 1) {
unsigned long cmd_argc = wcstoul(argv[arg_index++], NULL, 10);
if (cmd_argc == 0 || cmd_argc > argc - arg_index) {
log_event(ERR_ARGS, "unexpected '%s' @ %d", argv[arg_index - 1], arg_index - 1);
break;
} else {
wchar_t **cmd_argv = argv + arg_index;
arg_index += cmd_argc;
run_command((int)cmd_argc, cmd_argv, arg_index >= argc - 1);
}
}
if (event_log != NULL) {
DeregisterEventSource(event_log);
}
return 0;
}
static void wait_for_parent(const wchar_t *pid_arg) {
DWORD pid = wcstoul(pid_arg, NULL, 10);
HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (parent == NULL) {
log_event(ERR_OPEN_PROCESS, "OpenProcess(%lu): %lu", pid, GetLastError());
return;
}
DWORD res = WaitForSingleObject(parent, INFINITE);
if (res != WAIT_OBJECT_0) {
log_event(ERR_MAIN_WAIT_FAILED, "WaitForSingleObject: %08lX/%lu", res, GetLastError());
}
CloseHandle(parent);
}
static void run_command(int cmd_argc, wchar_t *cmd_argv[], BOOL last) {
wchar_t cmd_line[COMMAND_SIZE], *p = cmd_line;
for (int i = 0; i < cmd_argc; i++) {
wchar_t *arg = cmd_argv[i];
size_t arg_len = wcslen(arg);
BOOL space = wcschr(arg, L' ') != NULL;
if (space) *p++ = L'"';
if (wcscpy_s(p, COMMAND_SIZE - (p - cmd_line), arg) != 0) {
*p = L'\0';
log_event(ERR_COMMAND_TOO_LONG, "[%ls %ls ...]", cmd_line, arg);
return;
} else {
p += arg_len;
}
if (space) *p++ = L'"';
*p++ = i < cmd_argc - 1 ? L' ' : L'\0';
}
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
log_event(ERR_CREATE_PROCESS, "CreateProcess(%ls): %lu", cmd_line, GetLastError());
return;
}
if (!last) {
DWORD res = WaitForSingleObject(pi.hProcess, INFINITE);
if (res != WAIT_OBJECT_0) {
log_event(ERR_COMMAND_WAIT_FAILED, "WaitForSingleObject: %08lX/%lu", res, GetLastError());
}
DWORD ec;
if (!GetExitCodeProcess(pi.hProcess, &ec)) {
log_event(ERR_GET_EXIT_CODE, "GetExitCode: %lu", GetLastError());
} else if (ec != 0) {
log_event(WARN_COMMAND_FAILED, "[%ls]: %lu", cmd_line, ec);
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
static void log_event(unsigned int event_id, const char *format, ...) {
va_list ap;
va_start(ap, format);
if (event_log != NULL) {
char message[MESSAGE_SIZE];
int n = vsnprintf_s(message, MESSAGE_SIZE, _TRUNCATE, format, ap);
if (n > 0) {
int severity = event_id >> 30;
WORD type = (WORD)(severity == 3 ? EVENTLOG_ERROR_TYPE : severity == 2 ? EVENTLOG_WARNING_TYPE : EVENTLOG_INFORMATION_TYPE);
ReportEventW(event_log, type, 0, event_id, NULL, 0, (DWORD)n, NULL, message);
}
}
va_end(ap);
}