diff --git a/app/src/sys/unix/process.c b/app/src/sys/unix/process.c index 8683a2da..451b6491 100644 --- a/app/src/sys/unix/process.c +++ b/app/src/sys/unix/process.c @@ -1,5 +1,6 @@ #include "util/process.h" +#include #include #include #include @@ -50,65 +51,151 @@ search_executable(const char *file) { } enum process_result -process_execute(const char *const argv[], pid_t *pid) { - int fd[2]; +process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin, + int *pipe_stdout, int *pipe_stderr) { + int in[2]; + int out[2]; + int err[2]; + int internal[2]; // communication between parent and children - if (pipe(fd) == -1) { + if (pipe(internal) == -1) { perror("pipe"); return PROCESS_ERROR_GENERIC; } - - enum process_result ret = PROCESS_SUCCESS; + if (pipe_stdin) { + if (pipe(in) == -1) { + perror("pipe"); + close(internal[0]); + close(internal[1]); + return PROCESS_ERROR_GENERIC; + } + } + if (pipe_stdout) { + if (pipe(out) == -1) { + perror("pipe"); + // clean up + if (pipe_stdin) { + close(in[0]); + close(in[1]); + } + close(internal[0]); + close(internal[1]); + return PROCESS_ERROR_GENERIC; + } + } + if (pipe_stderr) { + if (pipe(err) == -1) { + perror("pipe"); + // clean up + if (pipe_stdout) { + close(out[0]); + close(out[1]); + } + if (pipe_stdin) { + close(in[0]); + close(in[1]); + } + close(internal[0]); + close(internal[1]); + return PROCESS_ERROR_GENERIC; + } + } *pid = fork(); if (*pid == -1) { perror("fork"); - ret = PROCESS_ERROR_GENERIC; - goto end; + // clean up + if (pipe_stderr) { + close(err[0]); + close(err[1]); + } + if (pipe_stdout) { + close(out[0]); + close(out[1]); + } + if (pipe_stdin) { + close(in[0]); + close(in[1]); + } + close(internal[0]); + close(internal[1]); + return PROCESS_ERROR_GENERIC; } - if (*pid > 0) { - // parent close write side - close(fd[1]); - fd[1] = -1; - // wait for EOF or receive errno from child - if (read(fd[0], &ret, sizeof(ret)) == -1) { - perror("read"); - ret = PROCESS_ERROR_GENERIC; - goto end; - } - } else if (*pid == 0) { - // child close read side - close(fd[0]); - if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) { - execvp(argv[0], (char *const *)argv); - if (errno == ENOENT) { - ret = PROCESS_ERROR_MISSING_BINARY; - } else { - ret = PROCESS_ERROR_GENERIC; + if (*pid == 0) { + if (pipe_stdin) { + if (in[0] != STDIN_FILENO) { + dup2(in[0], STDIN_FILENO); + close(in[0]); } + close(in[1]); + } + if (pipe_stdout) { + if (out[1] != STDOUT_FILENO) { + dup2(out[1], STDOUT_FILENO); + close(out[1]); + } + close(out[0]); + } + if (pipe_stderr) { + if (err[1] != STDERR_FILENO) { + dup2(err[1], STDERR_FILENO); + close(err[1]); + } + close(err[0]); + } + close(internal[0]); + enum process_result err; + if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) { + execvp(argv[0], (char *const *) argv); perror("exec"); + err = errno == ENOENT ? PROCESS_ERROR_MISSING_BINARY + : PROCESS_ERROR_GENERIC; } else { perror("fcntl"); - ret = PROCESS_ERROR_GENERIC; + err = PROCESS_ERROR_GENERIC; } - // send ret to the parent - if (write(fd[1], &ret, sizeof(ret)) == -1) { + // send err to the parent + if (write(internal[1], &err, sizeof(err)) == -1) { perror("write"); } - // close write side before exiting - close(fd[1]); + close(internal[1]); _exit(1); } -end: - if (fd[0] != -1) { - close(fd[0]); + // parent + assert(*pid > 0); + + close(internal[1]); + + enum process_result res = PROCESS_SUCCESS; + // wait for EOF or receive err from child + if (read(internal[0], &res, sizeof(res)) == -1) { + perror("read"); + res = PROCESS_ERROR_GENERIC; } - if (fd[1] != -1) { - close(fd[1]); + + close(internal[0]); + + if (pipe_stdin) { + close(in[0]); + *pipe_stdin = in[1]; } - return ret; + if (pipe_stdout) { + *pipe_stdout = out[0]; + close(out[1]); + } + if (pipe_stderr) { + *pipe_stderr = err[0]; + close(err[1]); + } + + return res; +} + +enum process_result +process_execute(const char *const argv[], pid_t *pid) { + return process_execute_redirect(argv, pid, NULL, NULL, NULL); } bool @@ -175,3 +262,15 @@ is_regular_file(const char *path) { } return S_ISREG(path_stat.st_mode); } + +ssize_t +read_pipe(int pipe, char *data, size_t len) { + return read(pipe, data, len); +} + +void +close_pipe(int pipe) { + if (close(pipe)) { + perror("close pipe"); + } +} diff --git a/app/src/sys/win/process.c b/app/src/sys/win/process.c index aafd5d34..9a846fad 100644 --- a/app/src/sys/win/process.c +++ b/app/src/sys/win/process.c @@ -23,38 +23,129 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) { } enum process_result -process_execute(const char *const argv[], HANDLE *handle) { +process_execute_redirect(const char *const argv[], HANDLE *handle, + HANDLE *pipe_stdin, HANDLE *pipe_stdout, + HANDLE *pipe_stderr) { + enum process_result ret = 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 (pipe_stdin) { + if (!CreatePipe(&stdin_read_handle, pipe_stdin, &sa, 0)) { + perror("pipe"); + return PROCESS_ERROR_GENERIC; + } + if (!SetHandleInformation(*pipe_stdin, HANDLE_FLAG_INHERIT, 0)) { + LOGE("SetHandleInformation stdin failed"); + goto error_close_stdin; + } + } + if (pipe_stdout) { + if (!CreatePipe(pipe_stdout, &stdout_write_handle, &sa, 0)) { + perror("pipe"); + goto error_close_stdin; + } + if (!SetHandleInformation(*pipe_stdout, HANDLE_FLAG_INHERIT, 0)) { + LOGE("SetHandleInformation stdout failed"); + goto error_close_stdout; + } + } + if (pipe_stderr) { + if (!CreatePipe(pipe_stderr, &stderr_write_handle, &sa, 0)) { + perror("pipe"); + goto error_close_stdout; + } + if (!SetHandleInformation(*pipe_stderr, 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 (pipe_stdin || pipe_stdout || pipe_stderr) { + si.dwFlags = STARTF_USESTDHANDLES; + if (pipe_stdin) { + si.hStdInput = stdin_read_handle; + } + if (pipe_stdout) { + si.hStdOutput = stdout_write_handle; + } + if (pipe_stderr) { + si.hStdError = stderr_write_handle; + } + } char *cmd = malloc(CMD_MAX_LEN); if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) { *handle = NULL; - return PROCESS_ERROR_GENERIC; + goto error_close_stderr; } wchar_t *wide = utf8_to_wide_char(cmd); free(cmd); if (!wide) { LOGC("Could not allocate wide char string"); - return PROCESS_ERROR_GENERIC; + goto error_close_stderr; } - if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, 0, NULL, NULL, &si, + if (!CreateProcessW(NULL, wide, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { free(wide); *handle = NULL; + if (GetLastError() == ERROR_FILE_NOT_FOUND) { - return PROCESS_ERROR_MISSING_BINARY; + ret = PROCESS_ERROR_MISSING_BINARY; } - return PROCESS_ERROR_GENERIC; + goto error_close_stderr; + } + + // These handles are used by the child process, close them for this process + if (pipe_stdin) { + CloseHandle(stdin_read_handle); + } + if (pipe_stdout) { + CloseHandle(stdout_write_handle); + } + if (pipe_stderr) { + CloseHandle(stderr_write_handle); } free(wide); *handle = pi.hProcess; + return PROCESS_SUCCESS; + +error_close_stderr: + if (pipe_stderr) { + CloseHandle(*pipe_stderr); + CloseHandle(stderr_write_handle); + } +error_close_stdout: + if (pipe_stdout) { + CloseHandle(*pipe_stdout); + CloseHandle(stdout_write_handle); + } +error_close_stdin: + if (pipe_stdin) { + CloseHandle(*pipe_stdin); + CloseHandle(stdin_read_handle); + } + + return ret; +} + +enum process_result +process_execute(const char *const argv[], HANDLE *handle) { + return process_execute_redirect(argv, handle, NULL, NULL, NULL); } bool @@ -116,3 +207,19 @@ is_regular_file(const char *path) { } return S_ISREG(path_stat.st_mode); } + +ssize_t +read_pipe(HANDLE pipe, char *data, size_t len) { + DWORD r; + if (!ReadFile(pipe, data, len, &r, NULL)) { + return -1; + } + return r; +} + +void +close_pipe(HANDLE pipe) { + if (!CloseHandle(pipe)) { + LOGW("Cannot close pipe"); + } +} diff --git a/app/src/util/process.h b/app/src/util/process.h index 6aca6bf5..a21374b6 100644 --- a/app/src/util/process.h +++ b/app/src/util/process.h @@ -18,6 +18,7 @@ # define NO_EXIT_CODE -1u // max value as unsigned typedef HANDLE process_t; typedef DWORD exit_code_t; + typedef HANDLE pipe_t; #else @@ -29,6 +30,7 @@ # define NO_EXIT_CODE -1 typedef pid_t process_t; typedef int exit_code_t; + typedef int pipe_t; #endif @@ -42,6 +44,14 @@ enum process_result { enum process_result process_execute(const char *const argv[], process_t *process); +enum process_result +process_execute_redirect(const char *const argv[], process_t *process, + pipe_t *pipe_stdin, pipe_t *pipe_stdout, + pipe_t *pipe_stderr); + +bool +process_terminate(process_t pid); + // kill the process bool process_terminate(process_t pid); @@ -83,4 +93,10 @@ get_local_file_path(const char *name); bool is_regular_file(const char *path); +ssize_t +read_pipe(pipe_t pipe, char *data, size_t len); + +void +close_pipe(pipe_t pipe); + #endif