6#include "../include/process.h"
7#include "../include/file.h"
8#include "../include/macros.h"
31static char* find_in_path(
const char* command,
const char*
const* environment) {
32 if (!command)
return NULL;
36 if ((command[0] !=
'\0' && command[1] ==
':') ||
39 return strdup(command);
43 if (command[0] ==
'/' || command[0] ==
'.') {
44 return strdup(command);
49 const char* path_env = NULL;
50 for (
int i = 0; environment && environment[i]; i++) {
51 if (strncmp(environment[i],
"PATH=", 5) == 0) {
52 path_env = environment[i] + 5;
62 char* path_copy = strdup(path_env);
63 if (!path_copy)
return NULL;
66 char* dir = strtok_r(path_copy, PATH_SEP, &saveptr);
70 snprintf(full_path,
sizeof(full_path),
"%s%s%s", dir, DIR_SEP, command);
73 if (ACCESS(full_path, X_OK) == 0) {
75 return strdup(full_path);
80 snprintf(exe_path,
sizeof(exe_path),
"%s.exe", full_path);
82 if (ACCESS(exe_path, X_OK) == 0) {
84 return strdup(exe_path);
87 if (ACCESS(full_path, X_OK) == 0) {
89 return strdup(full_path);
93 dir = strtok_r(NULL, PATH_SEP, &saveptr);
103 PROCESS_INFORMATION process_info;
129 .working_directory = NULL,
130 .inherit_environment =
true,
138 .merge_stderr =
false,
145 DWORD error = GetLastError();
147 case ERROR_INVALID_PARAMETER:
148 return PROCESS_ERROR_INVALID_ARGUMENT;
149 case ERROR_NOT_ENOUGH_MEMORY:
150 return PROCESS_ERROR_MEMORY;
151 case ERROR_ACCESS_DENIED:
152 return PROCESS_ERROR_PERMISSION_DENIED;
153 case ERROR_BROKEN_PIPE:
154 case ERROR_PIPE_BUSY:
155 case ERROR_PIPE_NOT_CONNECTED:
156 return PROCESS_ERROR_IO;
158 return PROCESS_ERROR_UNKNOWN;
163 return PROCESS_ERROR_INVALID_ARGUMENT;
165 return PROCESS_ERROR_MEMORY;
168 return PROCESS_ERROR_PERMISSION_DENIED;
171 return PROCESS_ERROR_IO;
173 return PROCESS_ERROR_WAIT_FAILED;
175 return PROCESS_ERROR_UNKNOWN;
203 case PROCESS_SUCCESS:
205 case PROCESS_ERROR_INVALID_ARGUMENT:
206 return "Invalid argument";
207 case PROCESS_ERROR_FORK_FAILED:
208 return "Fork failed";
209 case PROCESS_ERROR_EXEC_FAILED:
210 return "Exec failed";
211 case PROCESS_ERROR_PIPE_FAILED:
212 return "Pipe creation failed";
213 case PROCESS_ERROR_MEMORY:
214 return "Memory allocation failed";
215 case PROCESS_ERROR_WAIT_FAILED:
216 return "Wait for process failed";
217 case PROCESS_ERROR_KILL_FAILED:
218 return "Failed to terminate process";
219 case PROCESS_ERROR_TERMINATE_FAILED:
220 return "Failed to terminate process";
221 case PROCESS_ERROR_PERMISSION_DENIED:
222 return "Permission denied";
223 case PROCESS_ERROR_IO:
225 case PROCESS_ERROR_TIMEOUT:
226 return "Operation timed out";
227 case PROCESS_ERROR_WOULD_BLOCK:
228 return "Operation would block (no data available)";
229 case PROCESS_ERROR_PIPE_CLOSED:
230 return "Pipe was closed";
231 case PROCESS_ERROR_UNKNOWN:
232 return "Unknown error";
234 return "Invalid error code";
241 return PROCESS_ERROR_INVALID_ARGUMENT;
246 return PROCESS_ERROR_MEMORY;
250 SECURITY_ATTRIBUTES security_attrs;
251 memset(&security_attrs, 0,
sizeof(security_attrs));
252 security_attrs.nLength =
sizeof(security_attrs);
253 security_attrs.bInheritHandle = TRUE;
255 if (!CreatePipe(&(*pipeHandle)->read_fd, &(*pipeHandle)->write_fd, &security_attrs, 0)) {
258 return PROCESS_ERROR_PIPE_FAILED;
262 if (pipe(fds) != 0) {
265 return PROCESS_ERROR_PIPE_FAILED;
268 (*pipeHandle)->read_fd = fds[0];
269 (*pipeHandle)->write_fd = fds[1];
272 return PROCESS_SUCCESS;
284 return PROCESS_ERROR_INVALID_ARGUMENT;
288 DWORD mode = nonblocking ? PIPE_NOWAIT : PIPE_WAIT;
289 if (!SetNamedPipeHandleState(pipe->read_fd, &mode, NULL, NULL)) {
290 return process_system_error();
292 if (!SetNamedPipeHandleState(pipe->write_fd, &mode, NULL, NULL)) {
293 return process_system_error();
299 flags = fcntl(pipe->read_fd, F_GETFL);
301 return process_system_error();
307 flags &= ~O_NONBLOCK;
310 if (fcntl(pipe->read_fd, F_SETFL, flags) == -1) {
311 return process_system_error();
315 flags = fcntl(pipe->write_fd, F_GETFL);
317 return process_system_error();
323 flags &= ~O_NONBLOCK;
326 if (fcntl(pipe->write_fd, F_SETFL, flags) == -1) {
327 return process_system_error();
331 return PROCESS_SUCCESS;
335 if (!pipe || !buffer || pipe->read_closed) {
336 return PROCESS_ERROR_INVALID_ARGUMENT;
345 DWORD bytes_read_win = 0;
346 OVERLAPPED overlapped;
347 memset(&overlapped, 0,
sizeof(overlapped));
348 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
350 if (!overlapped.hEvent) {
351 return process_system_error();
354 if (!ReadFile(pipe->read_fd, buffer, (DWORD)size, NULL, &overlapped)) {
355 DWORD error = GetLastError();
356 if (error != ERROR_IO_PENDING) {
357 CloseHandle(overlapped.hEvent);
358 if (error == ERROR_BROKEN_PIPE) {
359 return PROCESS_ERROR_PIPE_CLOSED;
361 return process_system_error();
365 DWORD wait_result = WaitForSingleObject(overlapped.hEvent, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
367 if (wait_result == WAIT_OBJECT_0) {
368 if (!GetOverlappedResult(pipe->read_fd, &overlapped, &bytes_read_win, FALSE)) {
369 CloseHandle(overlapped.hEvent);
370 DWORD error = GetLastError();
371 if (error == ERROR_BROKEN_PIPE) {
372 return PROCESS_ERROR_PIPE_CLOSED;
374 return process_system_error();
377 *bytes_read = bytes_read_win;
379 }
else if (wait_result == WAIT_TIMEOUT) {
380 CancelIo(pipe->read_fd);
381 CloseHandle(overlapped.hEvent);
383 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
385 CloseHandle(overlapped.hEvent);
386 return process_system_error();
389 CloseHandle(overlapped.hEvent);
392 if (timeout_ms >= 0) {
395 FD_SET(pipe->read_fd, &read_fds);
397 struct timeval timeout;
398 timeout.tv_sec = timeout_ms / 1000;
399 timeout.tv_usec = (long)((timeout_ms % 1000) * 1000);
402 int select_result = select(pipe->read_fd + 1, &read_fds, NULL, NULL, &timeout);
404 if (select_result == -1) {
406 return PROCESS_ERROR_WOULD_BLOCK;
407 return process_system_error();
408 }
else if (select_result == 0) {
411 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
416 ssize_t result = read(pipe->read_fd, buffer, size);
418 if (errno == EAGAIN || errno == EWOULDBLOCK) {
420 return PROCESS_ERROR_WOULD_BLOCK;
421 }
else if (errno == EPIPE) {
423 return PROCESS_ERROR_PIPE_CLOSED;
424 }
else if (errno == EBADF) {
426 return PROCESS_ERROR_PIPE_CLOSED;
428 return process_system_error();
433 return PROCESS_ERROR_PIPE_CLOSED;
437 *bytes_read = (size_t)result;
441 return PROCESS_SUCCESS;
445 if (!pipe || !buffer || pipe->write_closed) {
446 return PROCESS_ERROR_INVALID_ARGUMENT;
454 DWORD bytes_written_win = 0;
455 OVERLAPPED overlapped;
456 memset(&overlapped, 0,
sizeof(overlapped));
457 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
459 if (!overlapped.hEvent) {
460 return process_system_error();
463 if (!WriteFile(pipe->write_fd, buffer, (DWORD)size, NULL, &overlapped)) {
464 DWORD error = GetLastError();
465 if (error != ERROR_IO_PENDING) {
466 CloseHandle(overlapped.hEvent);
467 if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) {
468 return PROCESS_ERROR_PIPE_CLOSED;
470 return process_system_error();
474 DWORD wait_result = WaitForSingleObject(overlapped.hEvent, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
476 if (wait_result == WAIT_OBJECT_0) {
477 if (!GetOverlappedResult(pipe->write_fd, &overlapped, &bytes_written_win, FALSE)) {
478 CloseHandle(overlapped.hEvent);
479 DWORD error = GetLastError();
480 if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) {
481 return PROCESS_ERROR_PIPE_CLOSED;
483 return process_system_error();
486 *bytes_written = bytes_written_win;
488 }
else if (wait_result == WAIT_TIMEOUT) {
489 CancelIo(pipe->write_fd);
490 CloseHandle(overlapped.hEvent);
491 return PROCESS_ERROR_TIMEOUT;
493 CloseHandle(overlapped.hEvent);
494 return process_system_error();
497 CloseHandle(overlapped.hEvent);
499 if (timeout_ms >= 0) {
503 FD_SET(pipe->write_fd, &write_fds);
505 struct timeval timeout;
506 struct timeval* timeout_ptr = NULL;
507 timeout.tv_sec = timeout_ms / 1000;
508 timeout.tv_usec = ((long)timeout_ms % 1000) * 1000;
509 timeout_ptr = &timeout;
510 int select_result = select(pipe->write_fd + 1, NULL, &write_fds, NULL, timeout_ptr);
511 if (select_result == -1) {
513 return PROCESS_ERROR_WOULD_BLOCK;
514 return process_system_error();
515 }
else if (select_result == 0) {
518 return (timeout_ms == 0) ? PROCESS_ERROR_WOULD_BLOCK : PROCESS_ERROR_TIMEOUT;
523 ssize_t result = write(pipe->write_fd, buffer, size);
525 if (errno == EAGAIN || errno == EWOULDBLOCK) {
527 return PROCESS_ERROR_WOULD_BLOCK;
528 }
else if (errno == EPIPE) {
530 return PROCESS_ERROR_PIPE_CLOSED;
531 }
else if (errno == EBADF) {
533 return PROCESS_ERROR_PIPE_CLOSED;
535 return process_system_error();
539 *bytes_written = (size_t)result;
543 return PROCESS_SUCCESS;
552 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
553 CloseHandle(pipe->read_fd);
554 pipe->read_closed =
true;
555 pipe->read_fd = INVALID_NATIVE_HANDLE;
557 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
558 CloseHandle(pipe->write_fd);
559 pipe->write_closed =
true;
560 pipe->write_fd = INVALID_NATIVE_HANDLE;
563 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
564 close(pipe->read_fd);
565 pipe->read_closed =
true;
566 pipe->read_fd = INVALID_NATIVE_HANDLE;
568 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
569 close(pipe->write_fd);
570 pipe->write_closed =
true;
571 pipe->write_fd = INVALID_NATIVE_HANDLE;
579 if (!pipe)
return PROCESS_ERROR_INVALID_ARGUMENT;
580 if (pipe->read_fd != INVALID_NATIVE_HANDLE && !pipe->read_closed) {
582 CloseHandle(pipe->read_fd);
584 close(pipe->read_fd);
586 pipe->read_closed =
true;
587 pipe->read_fd = INVALID_NATIVE_HANDLE;
589 return PROCESS_SUCCESS;
593 if (!pipe)
return PROCESS_ERROR_INVALID_ARGUMENT;
594 if (pipe->write_fd != INVALID_NATIVE_HANDLE && !pipe->write_closed) {
596 CloseHandle(pipe->write_fd);
598 close(pipe->write_fd);
600 pipe->write_closed =
true;
601 pipe->write_fd = INVALID_NATIVE_HANDLE;
603 return PROCESS_SUCCESS;
626static void append_escaped_win32_arg(
char* dest,
const char* arg) {
628 if (*arg !=
'\0' && !strpbrk(arg,
" \t\n\v\"")) {
635 for (
const char* p = arg; *p !=
'\0';) {
645 for (
int k = 0; k < num_bs * 2; k++) strcat(dest,
"\\");
647 }
else if (*p ==
'"') {
649 for (
int k = 0; k < num_bs * 2 + 1; k++) strcat(dest,
"\\");
654 for (
int k = 0; k < num_bs; k++) strcat(dest,
"\\");
655 size_t len = strlen(dest);
657 dest[len + 1] =
'\0';
665static ProcessError win32_create_process(ProcessHandle** handle,
const char* command,
const char*
const argv[],
670 size_t cmdline_len = 0;
673 while (argv[arg_count] != NULL) {
674 cmdline_len += strlen(argv[arg_count]) * 2 + 4;
677 if (arg_count == 0) {
678 return PROCESS_ERROR_INVALID_ARGUMENT;
681 char* cmdline = (
char*)malloc(cmdline_len + 1);
683 return PROCESS_ERROR_MEMORY;
687 for (
int i = 0; i < arg_count; i++) {
688 if (i > 0) strcat(cmdline,
" ");
689 append_escaped_win32_arg(cmdline, argv[i]);
693 STARTUPINFOA startup_info;
694 memset(&startup_info, 0,
sizeof(startup_info));
695 startup_info.cb =
sizeof(startup_info);
696 startup_info.dwFlags = STARTF_USESTDHANDLES;
697 startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
698 startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
699 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
701 if (options->io.stdin_pipe) startup_info.hStdInput = options->io.stdin_pipe->read_fd;
702 if (options->io.stdout_pipe) startup_info.hStdOutput = options->io.stdout_pipe->write_fd;
704 if (options->io.stderr_pipe) {
705 startup_info.hStdError = options->io.stderr_pipe->write_fd;
706 }
else if (options->io.merge_stderr) {
707 startup_info.hStdError = startup_info.hStdOutput;
710 DWORD creation_flags = 0;
711 if (options->detached) {
712 creation_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
715 PROCESS_INFORMATION process_info;
716 BOOL success = CreateProcessA(command, cmdline, NULL, NULL, TRUE, creation_flags, (LPVOID)(options->environment),
717 options->working_directory, &startup_info, &process_info);
722 return process_system_error();
727 CloseHandle(process_info.hProcess);
728 CloseHandle(process_info.hThread);
729 return PROCESS_ERROR_MEMORY;
732 (*handle)->process_info = process_info;
733 (*handle)->detached = options->detached;
735 return PROCESS_SUCCESS;
738static ProcessError unix_create_process(ProcessHandle** handle,
const char* command,
const char*
const argv[],
741 int stdin_pipe[2] = {-1, -1};
742 int stdout_pipe[2] = {-1, -1};
743 int stderr_pipe[2] = {-1, -1};
746 if (options->io.stdin_pipe) {
747 stdin_pipe[0] = options->io.stdin_pipe->read_fd;
748 stdin_pipe[1] = options->io.stdin_pipe->write_fd;
751 if (options->io.stdout_pipe) {
752 stdout_pipe[0] = options->io.stdout_pipe->read_fd;
753 stdout_pipe[1] = options->io.stdout_pipe->write_fd;
756 if (options->io.stderr_pipe) {
757 stderr_pipe[0] = options->io.stderr_pipe->read_fd;
758 stderr_pipe[1] = options->io.stderr_pipe->write_fd;
765 return PROCESS_ERROR_FORK_FAILED;
772 if (options->working_directory) {
773 if (chdir(options->working_directory) != 0) {
779 if (options->io.stdin_pipe) {
780 dup2(stdin_pipe[0], STDIN_FILENO);
781 close(stdin_pipe[0]);
782 close(stdin_pipe[1]);
786 if (options->io.stdout_pipe) {
787 dup2(stdout_pipe[1], STDOUT_FILENO);
788 close(stdout_pipe[0]);
789 close(stdout_pipe[1]);
793 if (options->io.stderr_pipe) {
794 dup2(stderr_pipe[1], STDERR_FILENO);
795 close(stderr_pipe[0]);
796 close(stderr_pipe[1]);
797 }
else if (options->io.merge_stderr) {
798 dup2(STDOUT_FILENO, STDERR_FILENO);
802 if (options->detached) {
809 if (options->inherit_environment) {
810 execvp(command, (
char*
const*)argv);
813 const char*
const* env = options->environment ? options->environment : (
const char*
const[]){NULL};
814 char* cmd_path = find_in_path(command, env);
816 execve(cmd_path, (
char*
const*)argv, (
char*
const*)env);
820 execve(command, (
char*
const*)argv, (
char*
const*)env);
832 return PROCESS_ERROR_MEMORY;
835 (*handle)->pid = pid;
836 (*handle)->detached = options->detached;
838 return PROCESS_SUCCESS;
844 if (!handle || !command || !argv || !argv[0]) {
845 return PROCESS_ERROR_INVALID_ARGUMENT;
851 effective_options = *options;
853 effective_options = DEFAULT_OPTIONS;
857 return win32_create_process(handle, command, argv, &effective_options);
859 return unix_create_process(handle, command, argv, &effective_options);
874static inline void set_process_result(
int status,
ProcessResult* result) {
875 if (WIFEXITED(status)) {
876 result->exit_code = WEXITSTATUS(status);
877 result->exited_normally =
true;
878 }
else if (WIFSIGNALED(status)) {
879 result->exit_code = WTERMSIG(status);
880 result->exited_normally =
false;
881 result->term_signal = WTERMSIG(status);
883 result->exit_code = -1;
884 result->exited_normally =
false;
890void NANOSLEEP(
long seconds,
long nanoseconds) {
894 unsigned long total_milliseconds = (unsigned)(seconds * 1000 + nanoseconds / 1000000);
895 Sleep(total_milliseconds);
899 req.tv_sec = seconds;
900 req.tv_nsec = nanoseconds;
901 nanosleep(&req, NULL);
907 return PROCESS_ERROR_INVALID_ARGUMENT;
910 if (handle->detached) {
912 return PROCESS_ERROR_INVALID_ARGUMENT;
918 WaitForSingleObject(handle->process_info.hProcess, timeout_ms < 0 ? INFINITE : (DWORD)timeout_ms);
920 if (wait_result == WAIT_TIMEOUT) {
921 return PROCESS_ERROR_WAIT_FAILED;
922 }
else if (wait_result != WAIT_OBJECT_0) {
923 return process_system_error();
928 if (!GetExitCodeProcess(handle->process_info.hProcess, &exit_code)) {
929 return process_system_error();
932 result->exit_code = (int)exit_code;
933 result->exited_normally =
true;
934 result->term_signal = 0;
939 pid_t wait_result = 0;
941 if (timeout_ms < 0) {
943 wait_result = waitpid(handle->pid, &status, 0);
946 int remaining_timeout_ms = timeout_ms;
949 while ((wait_result = waitpid(handle->pid, &status, WNOHANG)) == 0 && remaining_timeout_ms > 0) {
951 ts.tv_sec = remaining_timeout_ms / 1000;
952 ts.tv_nsec = (remaining_timeout_ms % 1000) * 1000000L;
955 NANOSLEEP(ts.tv_sec, ts.tv_nsec);
958 remaining_timeout_ms -= (int)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000));
962 if (wait_result < 0) {
963 return process_system_error();
966 if (wait_result == 0) {
968 return PROCESS_ERROR_WAIT_FAILED;
972 set_process_result(status, result);
976 return PROCESS_SUCCESS;
989 return PROCESS_ERROR_INVALID_ARGUMENT;
994 if (!TerminateProcess(handle->process_info.hProcess, 1)) {
995 return PROCESS_ERROR_TERMINATE_FAILED;
1000 if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
1001 return PROCESS_ERROR_TERMINATE_FAILED;
1005 if (handle->pid <= 0) {
1006 return PROCESS_ERROR_INVALID_ARGUMENT;
1011 if (kill(handle->pid, SIGKILL) != 0) {
1012 return PROCESS_ERROR_KILL_FAILED;
1016 if (kill(handle->pid, SIGTERM) != 0) {
1017 return PROCESS_ERROR_KILL_FAILED;
1021 return PROCESS_SUCCESS;
1029 if (err != PROCESS_SUCCESS) {
1038 *exit_code = res.exit_code;
1041 if (err != PROCESS_SUCCESS) {
1045 if (res.exit_code != 0) {
1046 return PROCESS_ERROR_EXEC_FAILED;
1048 return PROCESS_SUCCESS;
1063 unsigned int mode) {
1064 if (!redirection || !filepath) {
1065 return PROCESS_ERROR_INVALID_ARGUMENT;
1069 if (!*redirection) {
1070 return PROCESS_ERROR_MEMORY;
1074 int fd = open(filepath, flags, mode);
1077 *redirection = NULL;
1078 return process_system_error();
1081 (*redirection)->fd = fd;
1082 (*redirection)->close_on_exec =
true;
1084 return PROCESS_SUCCESS;
1096 if (!redirection || fd < 0) {
1097 return PROCESS_ERROR_INVALID_ARGUMENT;
1101 if (!*redirection) {
1102 return PROCESS_ERROR_MEMORY;
1105 (*redirection)->fd = fd;
1106 (*redirection)->close_on_exec = close_on_exec;
1108 return PROCESS_SUCCESS;
1121 if (redirection->close_on_exec && redirection->fd >= 0) {
1122 close(redirection->fd);
1139 const ExtProcessOptions* options) {
1140 if (!handle || !command || !argv || !argv[0]) {
1141 return PROCESS_ERROR_INVALID_ARGUMENT;
1147 return PROCESS_ERROR_FORK_FAILED;
1154 if (options->working_directory) {
1155 if (chdir(options->working_directory) != 0) {
1161 if (options->io.stdin_pipe) {
1162 if (dup2(options->io.stdin_pipe->read_fd, STDIN_FILENO) == -1) {
1164 return PROCESS_ERROR_IO;
1168 close(options->io.stdin_pipe->read_fd);
1169 close(options->io.stdin_pipe->write_fd);
1173 if (options->io.stdout_pipe) {
1174 if (dup2(options->io.stdout_pipe->write_fd, STDOUT_FILENO) == -1) {
1176 return PROCESS_ERROR_IO;
1178 close(options->io.stdout_pipe->read_fd);
1179 close(options->io.stdout_pipe->write_fd);
1180 }
else if (options->io.stdout_file) {
1182 if (dup2(options->io.stdout_file->fd, STDOUT_FILENO) == -1) {
1184 return PROCESS_ERROR_IO;
1186 if (options->io.stdout_file->close_on_exec) {
1187 close(options->io.stdout_file->fd);
1192 if (options->io.stderr_pipe) {
1193 if (dup2(options->io.stderr_pipe->write_fd, STDERR_FILENO) == -1) {
1195 return PROCESS_ERROR_IO;
1197 close(options->io.stderr_pipe->read_fd);
1198 close(options->io.stderr_pipe->write_fd);
1199 }
else if (options->io.stderr_file) {
1201 if (dup2(options->io.stderr_file->fd, STDERR_FILENO) == -1) {
1203 return PROCESS_ERROR_IO;
1205 if (options->io.stderr_file->close_on_exec) {
1206 close(options->io.stderr_file->fd);
1208 }
else if (options->io.merge_stderr) {
1209 if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
1211 return PROCESS_ERROR_IO;
1216 if (options->detached) {
1223 if (options->environment) {
1224 if (execve(command, (
char*
const*)argv, (
char*
const*)options->environment) == -1) {
1227 }
else if (options->inherit_environment) {
1228 if (execvp(command, (
char*
const*)argv) == -1) {
1232 char* empty_env[] = {NULL};
1233 if (execve(command, (
char*
const*)argv, empty_env) == -1) {
1245 return PROCESS_ERROR_MEMORY;
1248 (*handle)->pid = pid;
1249 (*handle)->detached = options->detached;
1251 return PROCESS_SUCCESS;
1261 if (pipe(stdout_pipe) < 0) {
1262 perror(
"pipe (stdout)");
1263 return PROCESS_ERROR_PIPE_FAILED;
1267 if (pipe(stderr_pipe) < 0) {
1268 perror(
"pipe (stderr)");
1270 close(stdout_pipe[0]);
1271 close(stdout_pipe[1]);
1272 return PROCESS_ERROR_PIPE_FAILED;
1276 pid_t cmd_pid = fork();
1280 close(stdout_pipe[0]);
1281 close(stdout_pipe[1]);
1282 close(stderr_pipe[0]);
1283 close(stderr_pipe[1]);
1284 return PROCESS_ERROR_FORK_FAILED;
1289 close(stdout_pipe[0]);
1290 close(stderr_pipe[0]);
1293 if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0) {
1294 perror(
"dup2 (stdout)");
1299 if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
1300 perror(
"dup2 (stderr)");
1306 close(stdout_pipe[1]);
1307 close(stderr_pipe[1]);
1310 execv(cmd, (
char*
const*)args);
1318 close(stdout_pipe[1]);
1319 close(stderr_pipe[1]);
1321 int child_count = 1;
1324 pid_t stdout_tee_pid = fork();
1325 if (stdout_tee_pid < 0) {
1326 perror(
"fork tee (stdout)");
1328 close(stdout_pipe[0]);
1329 close(stderr_pipe[0]);
1330 waitpid(cmd_pid, NULL, 0);
1331 return PROCESS_ERROR_FORK_FAILED;
1334 if (stdout_tee_pid == 0) {
1339 while ((n = read(stdout_pipe[0], buffer,
sizeof(buffer))) > 0) {
1340 for (
int i = 0; output_fds[i] != -1; i++) {
1341 if (write(output_fds[i], buffer, (
size_t)n) < 0) {
1342 perror(
"write (stdout tee)");
1350 perror(
"read (stdout tee)");
1360 pid_t stderr_tee_pid = fork();
1361 if (stderr_tee_pid < 0) {
1362 perror(
"fork tee (stderr)");
1365 close(stdout_pipe[0]);
1366 close(stderr_pipe[0]);
1367 waitpid(cmd_pid, NULL, 0);
1368 waitpid(stdout_tee_pid, NULL, 0);
1369 return PROCESS_ERROR_FORK_FAILED;
1372 if (stderr_tee_pid == 0) {
1377 while ((n = read(stderr_pipe[0], buffer,
sizeof(buffer))) > 0) {
1378 for (
int i = 0; error_fds[i] != -1; i++) {
1379 if (write(error_fds[i], buffer, (
size_t)n) < 0) {
1380 perror(
"write (stderr tee)");
1388 perror(
"read (stderr tee)");
1398 close(stdout_pipe[0]);
1399 close(stderr_pipe[0]);
1403 waitpid(cmd_pid, &cmd_status, 0);
1404 set_process_result(cmd_status, result);
1407 while (child_count > 1) {
1413 if (WIFEXITED(cmd_status)) {
1414 return PROCESS_SUCCESS;
1416 return PROCESS_ERROR_EXEC_FAILED;
1431 const char* stdout_file,
const char* stderr_file,
bool append) {
1432 ExtProcessOptions options;
1433 memset(&options, 0,
sizeof(options));
1434 options.inherit_environment =
true;
1441 int flags = O_WRONLY | O_CREAT;
1442 flags |= (append ? O_APPEND : O_TRUNC);
1445 if (err != PROCESS_SUCCESS) {
1448 options.io.stdout_file = stdout_redir;
1452 int flags = O_WRONLY | O_CREAT;
1453 flags |= (append ? O_APPEND : O_TRUNC);
1456 if (err != PROCESS_SUCCESS) {
1462 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.