#include "util/process.h" #include #include "util/log.h" #include "util/str.h" #define CMD_MAX_LEN 8192 static bool build_cmd(char *cmd, size_t len, const char *const argv[]) { // Windows command-line parsing is WTF: // // only make it work for this very specific program // (don't handle escaping nor quotes) size_t ret = sc_str_join(cmd, argv, ' ', len); if (ret >= len) { LOGE("Command too long (%" SC_PRIsizet " chars)", len - 1); return false; } return true; } enum sc_process_result sc_process_execute_p(const char *const argv[], HANDLE *handle, HANDLE *pin, HANDLE *pout, HANDLE *perr) { enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; HANDLE stdin_read_handle; HANDLE stdout_write_handle; HANDLE stderr_write_handle; if (pin) { if (!CreatePipe(&stdin_read_handle, pin, &sa, 0)) { perror("pipe"); return SC_PROCESS_ERROR_GENERIC; } if (!SetHandleInformation(*pin, HANDLE_FLAG_INHERIT, 0)) { LOGE("SetHandleInformation stdin failed"); goto error_close_stdin; } } if (pout) { if (!CreatePipe(pout, &stdout_write_handle, &sa, 0)) { perror("pipe"); goto error_close_stdin; } if (!SetHandleInformation(*pout, HANDLE_FLAG_INHERIT, 0)) { LOGE("SetHandleInformation stdout failed"); goto error_close_stdout; } } if (perr) { if (!CreatePipe(perr, &stderr_write_handle, &sa, 0)) { perror("pipe"); goto error_close_stdout; } if (!SetHandleInformation(*perr, HANDLE_FLAG_INHERIT, 0)) { LOGE("SetHandleInformation stderr failed"); goto error_close_stderr; } } STARTUPINFOW si; PROCESS_INFORMATION pi; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); if (pin || pout || perr) { si.dwFlags = STARTF_USESTDHANDLES; if (pin) { si.hStdInput = stdin_read_handle; } if (pout) { si.hStdOutput = stdout_write_handle; } if (perr) { si.hStdError = stderr_write_handle; } } char *cmd = malloc(CMD_MAX_LEN); if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) { *handle = NULL; goto error_close_stderr; } wchar_t *wide = sc_str_to_wchars(cmd); free(cmd); if (!wide) { LOGC("Could not allocate wide char string"); goto error_close_stderr; } if (!CreateProcessW(NULL, wide, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { free(wide); *handle = NULL; if (GetLastError() == ERROR_FILE_NOT_FOUND) { ret = SC_PROCESS_ERROR_MISSING_BINARY; } goto error_close_stderr; } // These handles are used by the child process, close them for this process if (pin) { CloseHandle(stdin_read_handle); } if (pout) { CloseHandle(stdout_write_handle); } if (perr) { CloseHandle(stderr_write_handle); } free(wide); *handle = pi.hProcess; return SC_PROCESS_SUCCESS; error_close_stderr: if (perr) { CloseHandle(*perr); CloseHandle(stderr_write_handle); } error_close_stdout: if (pout) { CloseHandle(*pout); CloseHandle(stdout_write_handle); } error_close_stdin: if (pin) { CloseHandle(*pin); CloseHandle(stdin_read_handle); } return ret; } bool sc_process_terminate(HANDLE handle) { return TerminateProcess(handle, 1); } sc_exit_code sc_process_wait(HANDLE handle, bool close) { DWORD code; if (WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0 || !GetExitCodeProcess(handle, &code)) { // could not wait or retrieve the exit code code = SC_EXIT_CODE_NONE; } if (close) { CloseHandle(handle); } return code; } void sc_process_close(HANDLE handle) { bool closed = CloseHandle(handle); assert(closed); (void) closed; } ssize_t sc_pipe_read(HANDLE pipe, char *data, size_t len) { DWORD r; if (!ReadFile(pipe, data, len, &r, NULL)) { return -1; } return r; } void sc_pipe_close(HANDLE pipe) { if (!CloseHandle(pipe)) { LOGW("Cannot close pipe"); } }