6#include "../include/process.h"
7#include "../include/file.h"
32static char* find_in_path(
const char* command,
const char*
const* environment) {
33 if (!command)
return NULL;
37 if ((command[0] !=
'\0' && command[1] ==
':') ||
40 return strdup(command);
44 if (command[0] ==
'/' || command[0] ==
'.') {
45 return strdup(command);
50 const char* path_env = NULL;
51 for (
int i = 0; environment && environment[i]; i++) {
52 if (strncmp(environment[i],
"PATH=", 5) == 0) {
53 path_env = environment[i] + 5;
63 char* path_copy = strdup(path_env);
64 if (!path_copy)
return NULL;
67 char* dir = strtok_r(path_copy, PATH_SEP, &saveptr);
71 snprintf(full_path,
sizeof(full_path),
"%s%s%s", dir, DIR_SEP, command);
74 if (ACCESS(full_path, X_OK) == 0) {
76 return strdup(full_path);
81 snprintf(exe_path,
sizeof(exe_path),
"%s.exe", full_path);
83 if (ACCESS(exe_path, X_OK) == 0) {
85 return strdup(exe_path);
88 if (ACCESS(full_path, X_OK) == 0) {
90 return strdup(full_path);
94 dir = strtok_r(NULL, PATH_SEP, &saveptr);
104 PROCESS_INFORMATION process_info;
130 .working_directory = NULL,
131 .inherit_environment =
true,
139 .merge_stderr =
false,
146 DWORD error = GetLastError();
148 case ERROR_INVALID_PARAMETER:
149 return PROCESS_ERROR_INVALID_ARGUMENT;
150 case ERROR_NOT_ENOUGH_MEMORY:
151 return PROCESS_ERROR_MEMORY;
152 case ERROR_ACCESS_DENIED:
153 return PROCESS_ERROR_PERMISSION_DENIED;
154 case ERROR_BROKEN_PIPE:
155 case ERROR_PIPE_BUSY:
156 case ERROR_PIPE_NOT_CONNECTED:
157 return PROCESS_ERROR_IO;
159 return PROCESS_ERROR_UNKNOWN;
164 return PROCESS_ERROR_INVALID_ARGUMENT;
166 return PROCESS_ERROR_MEMORY;
169 return PROCESS_ERROR_PERMISSION_DENIED;
172 return PROCESS_ERROR_IO;
174 return PROCESS_ERROR_WAIT_FAILED;
176 return PROCESS_ERROR_UNKNOWN;
204 case PROCESS_SUCCESS:
206 case PROCESS_ERROR_INVALID_ARGUMENT:
207 return "Invalid argument";
208 case PROCESS_ERROR_FORK_FAILED:
209 return "Fork failed";
210 case PROCESS_ERROR_EXEC_FAILED:
211 return "Exec failed";
212 case PROCESS_ERROR_PIPE_FAILED:
213 return "Pipe creation failed";
214 case PROCESS_ERROR_MEMORY:
215 return "Memory allocation failed";
216 case PROCESS_ERROR_WAIT_FAILED:
217 return "Wait for process failed";
218 case PROCESS_ERROR_KILL_FAILED:
219 return "Failed to terminate process";
220 case PROCESS_ERROR_TERMINATE_FAILED:
221 return "Failed to terminate process";
222 case PROCESS_ERROR_PERMISSION_DENIED:
223 return "Permission denied";
224 case PROCESS_ERROR_IO:
226 case PROCESS_ERROR_TIMEOUT:
227 return "Operation timed out";
228 case PROCESS_ERROR_WOULD_BLOCK:
229 return "Operation would block (no data available)";
230 case PROCESS_ERROR_PIPE_CLOSED:
231 return "Pipe was closed";
232 case PROCESS_ERROR_UNKNOWN:
233 return "Unknown error";
235 return "Invalid error code";
242 return PROCESS_ERROR_INVALID_ARGUMENT;
247 return PROCESS_ERROR_MEMORY;
251 SECURITY_ATTRIBUTES security_attrs;
252 memset(&security_attrs, 0,
sizeof(security_attrs));
253 security_attrs.nLength =
sizeof(security_attrs);
254 security_attrs.bInheritHandle = TRUE;
256 if (!CreatePipe(&(*pipeHandle)->read_fd, &(*pipeHandle)->write_fd, &security_attrs, 0)) {
259 return PROCESS_ERROR_PIPE_FAILED;
263 if (pipe(fds) != 0) {
266 return PROCESS_ERROR_PIPE_FAILED;
269 (*pipeHandle)->read_fd = fds[0];
270 (*pipeHandle)->write_fd = fds[1];
273 return PROCESS_SUCCESS;
285 return PROCESS_ERROR_INVALID_ARGUMENT;
289 DWORD mode = nonblocking ? PIPE_NOWAIT : PIPE_WAIT;
290 if (!SetNamedPipeHandleState(pipe->read_fd, &mode, NULL, NULL)) {
291 return process_system_error();
293 if (!SetNamedPipeHandleState(pipe->write_fd, &mode, NULL, NULL)) {
294 return process_system_error();
300 flags = fcntl(pipe->read_fd, F_GETFL);
302 return process_system_error();
308 flags &= ~O_NONBLOCK;
311 if (fcntl(pipe->read_fd, F_SETFL, flags) == -1) {
312 return process_system_error();
316 flags = fcntl(pipe->write_fd, F_GETFL);
318 return process_system_error();
324 flags &= ~O_NONBLOCK;
327 if (fcntl(pipe->write_fd, F_SETFL, flags) == -1) {
328 return process_system_error();
332 return PROCESS_SUCCESS;
336 if (!pipe || !buffer || pipe->read_closed) {
337 return PROCESS_ERROR_INVALID_ARGUMENT;
346 DWORD bytes_read_win = 0;
347 OVERLAPPED overlapped;
348 memset(&overlapped, 0,
sizeof(overlapped));
349 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
351 if (!overlapped.hEvent) {
352 return process_system_error();
355 if (!ReadFile(pipe->read_fd, buffer, (DWORD)size, NULL, &overlapped)) {
356 DWORD error = GetLastError();
357 if (error != ERROR_IO_PENDING) {
358 CloseHandle(overlapped.hEvent);
359 if (error == ERROR_BROKEN_PIPE) {
360 return PROCESS_ERROR_PIPE_CLOSED;
362 return process_system_error();
366 DWORD wait_result = WaitForSingleObject(overlapped.hEvent, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
368 if (wait_result == WAIT_OBJECT_0) {
369 if (!GetOverlappedResult(pipe->read_fd, &overlapped, &bytes_read_win, FALSE)) {
370 CloseHandle(overlapped.hEvent);
371 DWORD error = GetLastError();
372 if (error == ERROR_BROKEN_PIPE) {
373 return PROCESS_ERROR_PIPE_CLOSED;
375 return process_system_error();
378 *bytes_read = bytes_read_win;
380 }
else if (wait_result == WAIT_TIMEOUT) {
381 CancelIo(pipe->read_fd);
382 CloseHandle(overlapped.hEvent);
384 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
386 CloseHandle(overlapped.hEvent);
387 return process_system_error();
390 CloseHandle(overlapped.hEvent);
393 if (timeout_ms >= 0) {
396 FD_SET(pipe->read_fd, &read_fds);
398 struct timeval timeout;
399 timeout.tv_sec = timeout_ms / 1000;
400 timeout.tv_usec = (long)((timeout_ms % 1000) * 1000);
403 int select_result = select(pipe->read_fd + 1, &read_fds, NULL, NULL, &timeout);
405 if (select_result == -1) {
407 return PROCESS_ERROR_WOULD_BLOCK;
408 return process_system_error();
409 }
else if (select_result == 0) {
412 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
417 ssize_t result = read(pipe->read_fd, buffer, size);
419 if (errno == EAGAIN || errno == EWOULDBLOCK) {
421 return PROCESS_ERROR_WOULD_BLOCK;
422 }
else if (errno == EPIPE) {
424 return PROCESS_ERROR_PIPE_CLOSED;
425 }
else if (errno == EBADF) {
427 return PROCESS_ERROR_PIPE_CLOSED;
429 return process_system_error();
434 return PROCESS_ERROR_PIPE_CLOSED;
438 *bytes_read = (size_t)result;
442 return PROCESS_SUCCESS;
446 if (!pipe || !buffer || pipe->write_closed) {
447 return PROCESS_ERROR_INVALID_ARGUMENT;
455 DWORD bytes_written_win = 0;
456 OVERLAPPED overlapped;
457 memset(&overlapped, 0,
sizeof(overlapped));
458 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
460 if (!overlapped.hEvent) {
461 return process_system_error();
464 if (!WriteFile(pipe->write_fd, buffer, (DWORD)size, NULL, &overlapped)) {
465 DWORD error = GetLastError();
466 if (error != ERROR_IO_PENDING) {
467 CloseHandle(overlapped.hEvent);
468 if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) {
469 return PROCESS_ERROR_PIPE_CLOSED;
471 return process_system_error();
475 DWORD wait_result = WaitForSingleObject(overlapped.hEvent, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
477 if (wait_result == WAIT_OBJECT_0) {
478 if (!GetOverlappedResult(pipe->write_fd, &overlapped, &bytes_written_win, FALSE)) {
479 CloseHandle(overlapped.hEvent);
480 DWORD error = GetLastError();
481 if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) {
482 return PROCESS_ERROR_PIPE_CLOSED;
484 return process_system_error();
487 *bytes_written = bytes_written_win;
489 }
else if (wait_result == WAIT_TIMEOUT) {
490 CancelIo(pipe->write_fd);
491 CloseHandle(overlapped.hEvent);
492 return PROCESS_ERROR_TIMEOUT;
494 CloseHandle(overlapped.hEvent);
495 return process_system_error();
498 CloseHandle(overlapped.hEvent);
500 if (timeout_ms >= 0) {
504 FD_SET(pipe->write_fd, &write_fds);
506 struct timeval timeout;
507 struct timeval* timeout_ptr = NULL;
508 timeout.tv_sec = timeout_ms / 1000;
509 timeout.tv_usec = ((long)timeout_ms % 1000) * 1000;
510 timeout_ptr = &timeout;
511 int select_result = select(pipe->write_fd + 1, NULL, &write_fds, NULL, timeout_ptr);
512 if (select_result == -1) {
514 return PROCESS_ERROR_WOULD_BLOCK;
515 return process_system_error();
516 }
else if (select_result == 0) {
519 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
524 ssize_t result = write(pipe->write_fd, buffer, size);
526 if (errno == EAGAIN || errno == EWOULDBLOCK) {
528 return PROCESS_ERROR_WOULD_BLOCK;
529 }
else if (errno == EPIPE) {
531 return PROCESS_ERROR_PIPE_CLOSED;
532 }
else if (errno == EBADF) {
534 return PROCESS_ERROR_PIPE_CLOSED;
536 return process_system_error();
540 *bytes_written = (size_t)result;
544 return PROCESS_SUCCESS;
553 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
554 CloseHandle(pipe->read_fd);
555 pipe->read_closed =
true;
556 pipe->read_fd = INVALID_NATIVE_HANDLE;
558 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
559 CloseHandle(pipe->write_fd);
560 pipe->write_closed =
true;
561 pipe->write_fd = INVALID_NATIVE_HANDLE;
564 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
565 close(pipe->read_fd);
566 pipe->read_closed =
true;
567 pipe->read_fd = INVALID_NATIVE_HANDLE;
569 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
570 close(pipe->write_fd);
571 pipe->write_closed =
true;
572 pipe->write_fd = INVALID_NATIVE_HANDLE;
580 if (!pipe)
return PROCESS_ERROR_INVALID_ARGUMENT;
581 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
583 CloseHandle(pipe->read_fd);
585 close(pipe->read_fd);
587 pipe->read_closed =
true;
588 pipe->read_fd = INVALID_NATIVE_HANDLE;
590 return PROCESS_SUCCESS;
594 if (!pipe)
return PROCESS_ERROR_INVALID_ARGUMENT;
595 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
597 CloseHandle(pipe->write_fd);
599 close(pipe->write_fd);
601 pipe->write_closed =
true;
602 pipe->write_fd = INVALID_NATIVE_HANDLE;
604 return PROCESS_SUCCESS;
627static void append_escaped_win32_arg(
char* dest,
const char* arg) {
629 if (*arg !=
'\0' && !strpbrk(arg,
" \t\n\v\"")) {
636 for (
const char* p = arg; *p !=
'\0';) {
646 for (
int k = 0; k < num_bs * 2; k++) strcat(dest,
"\\");
648 }
else if (*p ==
'"') {
650 for (
int k = 0; k < num_bs * 2 + 1; k++) strcat(dest,
"\\");
655 for (
int k = 0; k < num_bs; k++) strcat(dest,
"\\");
656 size_t len = strlen(dest);
658 dest[len + 1] =
'\0';
666static ProcessError win32_create_process(ProcessHandle** handle,
const char* command,
const char*
const argv[],
671 size_t cmdline_len = 0;
674 while (argv[arg_count] != NULL) {
675 cmdline_len += strlen(argv[arg_count]) * 2 + 4;
678 if (arg_count == 0) {
679 return PROCESS_ERROR_INVALID_ARGUMENT;
682 char* cmdline = (
char*)malloc(cmdline_len + 1);
684 return PROCESS_ERROR_MEMORY;
688 for (
int i = 0; i < arg_count; i++) {
689 if (i > 0) strcat(cmdline,
" ");
690 append_escaped_win32_arg(cmdline, argv[i]);
694 STARTUPINFOA startup_info;
695 memset(&startup_info, 0,
sizeof(startup_info));
696 startup_info.cb =
sizeof(startup_info);
697 startup_info.dwFlags = STARTF_USESTDHANDLES;
698 startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
699 startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
700 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
702 if (options->io.stdin_pipe) startup_info.hStdInput = options->io.stdin_pipe->read_fd;
703 if (options->io.stdout_pipe) startup_info.hStdOutput = options->io.stdout_pipe->write_fd;
705 if (options->io.stderr_pipe) {
706 startup_info.hStdError = options->io.stderr_pipe->write_fd;
707 }
else if (options->io.merge_stderr) {
708 startup_info.hStdError = startup_info.hStdOutput;
711 DWORD creation_flags = 0;
712 if (options->detached) {
713 creation_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
716 PROCESS_INFORMATION process_info;
717 BOOL success = CreateProcessA(command, cmdline, NULL, NULL, TRUE, creation_flags, (LPVOID)(options->environment),
718 options->working_directory, &startup_info, &process_info);
723 return process_system_error();
728 CloseHandle(process_info.hProcess);
729 CloseHandle(process_info.hThread);
730 return PROCESS_ERROR_MEMORY;
733 (*handle)->process_info = process_info;
734 (*handle)->detached = options->detached;
736 return PROCESS_SUCCESS;
739static ProcessError unix_create_process(ProcessHandle** handle,
const char* command,
const char*
const argv[],
742 int stdin_pipe[2] = {-1, -1};
743 int stdout_pipe[2] = {-1, -1};
744 int stderr_pipe[2] = {-1, -1};
747 if (options->io.stdin_pipe) {
748 stdin_pipe[0] = options->io.stdin_pipe->read_fd;
749 stdin_pipe[1] = options->io.stdin_pipe->write_fd;
752 if (options->io.stdout_pipe) {
753 stdout_pipe[0] = options->io.stdout_pipe->read_fd;
754 stdout_pipe[1] = options->io.stdout_pipe->write_fd;
757 if (options->io.stderr_pipe) {
758 stderr_pipe[0] = options->io.stderr_pipe->read_fd;
759 stderr_pipe[1] = options->io.stderr_pipe->write_fd;
766 return PROCESS_ERROR_FORK_FAILED;
773 if (options->working_directory) {
774 if (chdir(options->working_directory) != 0) {
780 if (options->io.stdin_pipe) {
781 dup2(stdin_pipe[0], STDIN_FILENO);
782 close(stdin_pipe[0]);
783 close(stdin_pipe[1]);
787 if (options->io.stdout_pipe) {
788 dup2(stdout_pipe[1], STDOUT_FILENO);
789 close(stdout_pipe[0]);
790 close(stdout_pipe[1]);
794 if (options->io.stderr_pipe) {
795 dup2(stderr_pipe[1], STDERR_FILENO);
796 close(stderr_pipe[0]);
797 close(stderr_pipe[1]);
798 }
else if (options->io.merge_stderr) {
799 dup2(STDOUT_FILENO, STDERR_FILENO);
803 if (options->detached) {
810 if (options->inherit_environment) {
811 execvp(command, (
char*
const*)argv);
814 const char*
const* env = options->environment ? options->environment : (
const char*
const[]){NULL};
815 char* cmd_path = find_in_path(command, env);
817 execve(cmd_path, (
char*
const*)argv, (
char*
const*)env);
821 execve(command, (
char*
const*)argv, (
char*
const*)env);
833 return PROCESS_ERROR_MEMORY;
836 (*handle)->pid = pid;
837 (*handle)->detached = options->detached;
839 return PROCESS_SUCCESS;
845 if (!handle || !command || !argv || !argv[0]) {
846 return PROCESS_ERROR_INVALID_ARGUMENT;
852 effective_options = *options;
854 effective_options = DEFAULT_OPTIONS;
858 return win32_create_process(handle, command, argv, &effective_options);
860 return unix_create_process(handle, command, argv, &effective_options);
875static inline void set_process_result(
int status,
ProcessResult* result) {
876 if (WIFEXITED(status)) {
877 result->exit_code = WEXITSTATUS(status);
878 result->exited_normally =
true;
879 }
else if (WIFSIGNALED(status)) {
880 result->exit_code = WTERMSIG(status);
881 result->exited_normally =
false;
882 result->term_signal = WTERMSIG(status);
884 result->exit_code = -1;
885 result->exited_normally =
false;
891void NANOSLEEP(
long seconds,
long nanoseconds) {
895 unsigned long total_milliseconds = (unsigned)(seconds * 1000 + nanoseconds / 1000000);
896 Sleep(total_milliseconds);
900 req.tv_sec = seconds;
901 req.tv_nsec = nanoseconds;
902 nanosleep(&req, NULL);
908 return PROCESS_ERROR_INVALID_ARGUMENT;
911 if (handle->detached) {
913 return PROCESS_ERROR_INVALID_ARGUMENT;
919 WaitForSingleObject(handle->process_info.hProcess, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
921 if (wait_result == WAIT_TIMEOUT) {
922 return PROCESS_ERROR_WAIT_FAILED;
923 }
else if (wait_result != WAIT_OBJECT_0) {
924 return process_system_error();
929 if (!GetExitCodeProcess(handle->process_info.hProcess, &exit_code)) {
930 return process_system_error();
933 result->exit_code = (int)exit_code;
934 result->exited_normally =
true;
935 result->term_signal = 0;
940 pid_t wait_result = 0;
942 if (timeout_ms < 0) {
944 wait_result = waitpid(handle->pid, &status, 0);
947 int remaining_timeout_ms = timeout_ms;
950 while ((wait_result = waitpid(handle->pid, &status, WNOHANG)) == 0 && remaining_timeout_ms > 0) {
952 ts.tv_sec = remaining_timeout_ms / 1000;
953 ts.tv_nsec = (remaining_timeout_ms % 1000) * 1000000L;
956 NANOSLEEP(ts.tv_sec, ts.tv_nsec);
959 remaining_timeout_ms -= (int)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000));
963 if (wait_result < 0) {
964 return process_system_error();
967 if (wait_result == 0) {
969 return PROCESS_ERROR_WAIT_FAILED;
973 set_process_result(status, result);
977 return PROCESS_SUCCESS;
990 return PROCESS_ERROR_INVALID_ARGUMENT;
995 if (!TerminateProcess(handle->process_info.hProcess, 1)) {
996 return PROCESS_ERROR_TERMINATE_FAILED;
1001 if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
1002 return PROCESS_ERROR_TERMINATE_FAILED;
1006 if (handle->pid <= 0) {
1007 return PROCESS_ERROR_INVALID_ARGUMENT;
1012 if (kill(handle->pid, SIGKILL) != 0) {
1013 return PROCESS_ERROR_KILL_FAILED;
1017 if (kill(handle->pid, SIGTERM) != 0) {
1018 return PROCESS_ERROR_KILL_FAILED;
1022 return PROCESS_SUCCESS;
1030 if (err != PROCESS_SUCCESS) {
1039 *exit_code = res.exit_code;
1042 if (err != PROCESS_SUCCESS) {
1046 if (res.exit_code != 0) {
1047 return PROCESS_ERROR_EXEC_FAILED;
1049 return PROCESS_SUCCESS;
1064 unsigned int mode) {
1065 if (!redirection || !filepath) {
1066 return PROCESS_ERROR_INVALID_ARGUMENT;
1070 if (!*redirection) {
1071 return PROCESS_ERROR_MEMORY;
1075 int fd = open(filepath, flags, mode);
1078 *redirection = NULL;
1079 return process_system_error();
1082 (*redirection)->fd = fd;
1083 (*redirection)->close_on_exec =
true;
1085 return PROCESS_SUCCESS;
1097 if (!redirection || fd < 0) {
1098 return PROCESS_ERROR_INVALID_ARGUMENT;
1102 if (!*redirection) {
1103 return PROCESS_ERROR_MEMORY;
1106 (*redirection)->fd = fd;
1107 (*redirection)->close_on_exec = close_on_exec;
1109 return PROCESS_SUCCESS;
1122 if (redirection->close_on_exec && redirection->fd >= 0) {
1123 close(redirection->fd);
1140 const ExtProcessOptions* options) {
1141 if (!handle || !command || !argv || !argv[0]) {
1142 return PROCESS_ERROR_INVALID_ARGUMENT;
1148 return PROCESS_ERROR_FORK_FAILED;
1155 if (options->working_directory) {
1156 if (chdir(options->working_directory) != 0) {
1162 if (options->io.stdin_pipe) {
1163 if (dup2(options->io.stdin_pipe->read_fd, STDIN_FILENO) == -1) {
1165 return PROCESS_ERROR_IO;
1169 close(options->io.stdin_pipe->read_fd);
1170 close(options->io.stdin_pipe->write_fd);
1174 if (options->io.stdout_pipe) {
1175 if (dup2(options->io.stdout_pipe->write_fd, STDOUT_FILENO) == -1) {
1177 return PROCESS_ERROR_IO;
1179 close(options->io.stdout_pipe->read_fd);
1180 close(options->io.stdout_pipe->write_fd);
1181 }
else if (options->io.stdout_file) {
1183 if (dup2(options->io.stdout_file->fd, STDOUT_FILENO) == -1) {
1185 return PROCESS_ERROR_IO;
1187 if (options->io.stdout_file->close_on_exec) {
1188 close(options->io.stdout_file->fd);
1193 if (options->io.stderr_pipe) {
1194 if (dup2(options->io.stderr_pipe->write_fd, STDERR_FILENO) == -1) {
1196 return PROCESS_ERROR_IO;
1198 close(options->io.stderr_pipe->read_fd);
1199 close(options->io.stderr_pipe->write_fd);
1200 }
else if (options->io.stderr_file) {
1202 if (dup2(options->io.stderr_file->fd, STDERR_FILENO) == -1) {
1204 return PROCESS_ERROR_IO;
1206 if (options->io.stderr_file->close_on_exec) {
1207 close(options->io.stderr_file->fd);
1209 }
else if (options->io.merge_stderr) {
1210 if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
1212 return PROCESS_ERROR_IO;
1217 if (options->detached) {
1224 if (options->environment) {
1225 if (execve(command, (
char*
const*)argv, (
char*
const*)options->environment) == -1) {
1228 }
else if (options->inherit_environment) {
1229 if (execvp(command, (
char*
const*)argv) == -1) {
1233 char* empty_env[] = {NULL};
1234 if (execve(command, (
char*
const*)argv, empty_env) == -1) {
1246 return PROCESS_ERROR_MEMORY;
1249 (*handle)->pid = pid;
1250 (*handle)->detached = options->detached;
1252 return PROCESS_SUCCESS;
1262 if (pipe(stdout_pipe) < 0) {
1263 perror(
"pipe (stdout)");
1264 return PROCESS_ERROR_PIPE_FAILED;
1268 if (pipe(stderr_pipe) < 0) {
1269 perror(
"pipe (stderr)");
1271 close(stdout_pipe[0]);
1272 close(stdout_pipe[1]);
1273 return PROCESS_ERROR_PIPE_FAILED;
1277 pid_t cmd_pid = fork();
1281 close(stdout_pipe[0]);
1282 close(stdout_pipe[1]);
1283 close(stderr_pipe[0]);
1284 close(stderr_pipe[1]);
1285 return PROCESS_ERROR_FORK_FAILED;
1290 close(stdout_pipe[0]);
1291 close(stderr_pipe[0]);
1294 if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0) {
1295 perror(
"dup2 (stdout)");
1300 if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
1301 perror(
"dup2 (stderr)");
1307 close(stdout_pipe[1]);
1308 close(stderr_pipe[1]);
1311 execv(cmd, (
char*
const*)args);
1319 close(stdout_pipe[1]);
1320 close(stderr_pipe[1]);
1322 int child_count = 1;
1325 pid_t stdout_tee_pid = fork();
1326 if (stdout_tee_pid < 0) {
1327 perror(
"fork tee (stdout)");
1329 close(stdout_pipe[0]);
1330 close(stderr_pipe[0]);
1331 waitpid(cmd_pid, NULL, 0);
1332 return PROCESS_ERROR_FORK_FAILED;
1335 if (stdout_tee_pid == 0) {
1340 while ((n = read(stdout_pipe[0], buffer,
sizeof(buffer))) > 0) {
1341 for (
int i = 0; output_fds[i] != -1; i++) {
1342 if (write(output_fds[i], buffer, (
size_t)n) < 0) {
1343 perror(
"write (stdout tee)");
1351 perror(
"read (stdout tee)");
1361 pid_t stderr_tee_pid = fork();
1362 if (stderr_tee_pid < 0) {
1363 perror(
"fork tee (stderr)");
1366 close(stdout_pipe[0]);
1367 close(stderr_pipe[0]);
1368 waitpid(cmd_pid, NULL, 0);
1369 waitpid(stdout_tee_pid, NULL, 0);
1370 return PROCESS_ERROR_FORK_FAILED;
1373 if (stderr_tee_pid == 0) {
1378 while ((n = read(stderr_pipe[0], buffer,
sizeof(buffer))) > 0) {
1379 for (
int i = 0; error_fds[i] != -1; i++) {
1380 if (write(error_fds[i], buffer, (
size_t)n) < 0) {
1381 perror(
"write (stderr tee)");
1389 perror(
"read (stderr tee)");
1399 close(stdout_pipe[0]);
1400 close(stderr_pipe[0]);
1404 waitpid(cmd_pid, &cmd_status, 0);
1405 set_process_result(cmd_status, result);
1408 while (child_count > 1) {
1414 if (WIFEXITED(cmd_status)) {
1415 return PROCESS_SUCCESS;
1417 return PROCESS_ERROR_EXEC_FAILED;
1432 const char* stdout_file,
const char* stderr_file,
bool append) {
1433 ExtProcessOptions options;
1434 memset(&options, 0,
sizeof(options));
1435 options.inherit_environment =
true;
1442 int flags = O_WRONLY | O_CREAT;
1443 flags |= (append ? O_APPEND : O_TRUNC);
1446 if (err != PROCESS_SUCCESS) {
1449 options.io.stdout_file = stdout_redir;
1453 int flags = O_WRONLY | O_CREAT;
1454 flags |= (append ? O_APPEND : O_TRUNC);
1457 if (err != PROCESS_SUCCESS) {
1463 options.io.stderr_file = stderr_redir;
void process_close_redirection(FileRedirection *redirection)
Close and free a file redirection.
ProcessError process_redirect_to_fd(FileRedirection **redirection, int fd, bool close_on_exec)
Create a file redirection from an existing file descriptor.
ProcessError process_run_and_capture(const char *command, const char *const argv[], ProcessOptions *options, int *exit_code)
Run a command and capture its output.
ProcessError process_create(ProcessHandle **handle, const char *command, const char *const argv[], const ProcessOptions *options)
Create a new process When custom environment is provided, command must be an absolute path or relativ...
ProcessError process_redirect_to_file(FileRedirection **redirection, const char *filepath, int flags, unsigned int mode)
Create a new file redirection for a process.
PipeFd pipe_read_fd(PipeHandle *handle)
ProcessError process_terminate(ProcessHandle *handle, bool force)
Terminate a running process.
ProcessError pipe_write(PipeHandle *pipe, const void *buffer, size_t size, size_t *bytes_written, int timeout_ms)
Write data to a pipe.
ProcessError pipe_read(PipeHandle *pipe, void *buffer, size_t size, size_t *bytes_read, int timeout_ms)
Read data from a pipe.
void process_free(ProcessHandle *handle)
Free resources associated with a process handle.
ProcessError pipe_create(PipeHandle **pipeHandle)
Create a new pipe for IPC.
ProcessError process_wait(ProcessHandle *handle, ProcessResult *result, int timeout_ms)
Wait for a process to complete.
bool pipe_read_closed(PipeHandle *handle)
ProcessError process_run_with_file_redirection(ProcessHandle **handle, const char *command, const char *const argv[], const char *stdout_file, const char *stderr_file, bool append)
Helper function to set up redirection to a file.
ProcessError process_run_with_multiwriter(ProcessResult *result, const char *cmd, const char *args[], int output_fds[], int error_fds[])
Run the command in a process that duplicates output to multiple destinations.
PipeFd pipe_write_fd(PipeHandle *handle)
void pipe_close(PipeHandle *pipe)
Close a pipe.
bool pipe_write_closed(PipeHandle *handle)
ProcessError process_create_with_redirection(ProcessHandle **handle, const char *command, const char *const argv[], const ExtProcessOptions *options)
Create a process with extended redirection options.
const char * process_error_string(ProcessError error)
Get a string description of a process error.
ProcessError pipe_set_nonblocking(PipeHandle *pipe, bool nonblocking)
Set non-blocking mode on a pipe.
struct FileRedirection FileRedirection
struct PipeHandle PipeHandle
Pipe handle type for IPC (platform-specific details hidden in implementation)
struct ProcessHandle ProcessHandle
Process handle type (platform-specific details hidden in implementation)
ProcessError
Error codes for process operations.
Options for process creation.
Result information after a process completes.