aboutsummaryrefslogtreecommitdiff
path: root/src/program.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/program.c')
-rw-r--r--src/program.c175
1 files changed, 134 insertions, 41 deletions
diff --git a/src/program.c b/src/program.c
index c396695..6150616 100644
--- a/src/program.c
+++ b/src/program.c
@@ -18,8 +18,42 @@ int program_buffer_write_callback(char *data, int size, void *userdata) {
return 0;
}
+static int program_read_output(int process_id, int read_fd, ProgramOutputCallback output_callback, void *userdata) {
+ int status;
+ char buffer[4097];
+
+ for(;;) {
+ ssize_t bytes_read = read(read_fd, buffer, sizeof(buffer) - 1);
+ if(bytes_read == 0) {
+ break;
+ } else if(bytes_read == -1) {
+ int err = errno;
+ fprintf(stderr, "Failed to read from pipe, error: %s\n", strerror(err));
+ return -err;
+ }
+
+ buffer[bytes_read] = '\0';
+ if(output_callback && output_callback(buffer, bytes_read, userdata) != 0)
+ break;
+ }
+
+ if(waitpid(process_id, &status, 0) == -1) {
+ perror("waitpid failed");
+ return -5;
+ }
+
+ if(!WIFEXITED(status))
+ return -4;
+
+ int exit_status = WEXITSTATUS(status);
+ if(exit_status != 0)
+ return -exit_status;
+
+ return 0;
+}
+
int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata) {
- /* 1 arguments */
+ /* 1 argument */
if(args[0] == NULL)
return -1;
@@ -34,6 +68,8 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void
pid_t pid = fork();
if(pid == -1) {
perror("Failed to fork");
+ close(fd[READ_END]);
+ close(fd[WRITE_END]);
return -3;
} else if(pid == 0) { /* child */
if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
@@ -55,42 +91,8 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void
} else { /* parent */
close(fd[WRITE_END]);
- int result = 0;
- int status;
-
- char buffer[4097];
-
- if(output_callback) {
- for(;;) {
- ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1);
- if(bytes_read == 0) {
- break;
- } else if(bytes_read == -1) {
- int err = errno;
- fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err));
- result = -err;
- goto cleanup;
- }
-
- buffer[bytes_read] = '\0';
- if(output_callback(buffer, bytes_read, userdata) != 0)
- break;
- }
- }
-
- if(waitpid(pid, &status, 0) == -1) {
- perror("waitpid failed");
- result = -5;
- goto cleanup;
- }
-
- if(!WIFEXITED(status)) {
- result = -4;
- goto cleanup;
- }
-
- int exit_status = WEXITSTATUS(status);
- if(exit_status != 0) {
+ int result = program_read_output(pid, fd[READ_END], output_callback, userdata);
+ if(result != 0) {
fprintf(stderr, "Failed to execute program (");
const char **arg = args;
while(*arg) {
@@ -99,13 +101,104 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void
fprintf(stderr, "'%s'", *arg);
++arg;
}
- fprintf(stderr, "), exit status %d\n", exit_status);
- result = -exit_status;
- goto cleanup;
+ fprintf(stderr, ")\n");
}
- cleanup:
close(fd[READ_END]);
return result;
}
}
+
+int program_exec_async(const char **args, int *process_id, int *stdin_file, int *stdout_file) {
+ int result = 0;
+
+ /* 1 argument */
+ if(args[0] == NULL)
+ return -1;
+
+ int stdin_fd[2] = { -1, -1 };
+ int stdout_fd[2] = { -1, -1 };
+
+ if(stdin_file) {
+ if(pipe(stdin_fd) == -1) {
+ perror("Failed to open pipe");
+ result = -2;
+ goto cleanup;
+ }
+ }
+
+ if(stdout_file) {
+ if(pipe(stdout_fd) == -1) {
+ perror("Failed to open pipe");
+ result = -2;
+ goto cleanup;
+ }
+ }
+
+ pid_t parent_pid = getpid();
+
+ pid_t pid = fork();
+ if(pid == -1) {
+ result = -errno;
+ perror("failed to fork");
+ goto cleanup;
+ } else if(pid == 0) { /* child */
+ if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
+ perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed");
+ exit(127);
+ }
+
+ /* Test if the parent died before the above call to prctl */
+ if(getppid() != parent_pid)
+ exit(127);
+
+ if(stdin_file) {
+ dup2(stdin_fd[READ_END], STDIN_FILENO);
+ close(stdin_fd[READ_END]);
+ close(stdin_fd[WRITE_END]);
+ }
+
+ if(stdout_file) {
+ dup2(stdout_fd[WRITE_END], STDOUT_FILENO);
+ close(stdout_fd[READ_END]);
+ close(stdout_fd[WRITE_END]);
+ }
+
+ execvp(args[0], (char* const*)args);
+ perror("execvp");
+ exit(127);
+ } else { /* parent */
+ if(process_id)
+ *process_id = pid;
+
+ if(stdin_file)
+ close(stdin_fd[READ_END]);
+
+ if(stdout_file)
+ close(stdout_fd[WRITE_END]);
+
+ *stdin_file = stdin_fd[WRITE_END];
+ *stdout_file = stdout_fd[READ_END];
+
+ return 0;
+ }
+
+ cleanup:
+ if(stdin_fd[0] != -1) close(stdin_fd[0]);
+ if(stdin_fd[1] != -1) close(stdin_fd[1]);
+ if(stdout_fd[0] != -1) close(stdout_fd[0]);
+ if(stdout_fd[1] != -1) close(stdout_fd[1]);
+ return result;
+}
+
+int program_wait_until_exit(int process_id, int stdin_file, int stdout_file, ProgramOutputCallback output_callback, void *userdata) {
+ if(stdin_file != -1)
+ close(stdin_file);
+
+ int result = program_read_output(process_id, stdout_file, output_callback, userdata);
+
+ if(stdout_file != -1)
+ close(stdout_file);
+
+ return result;
+}