aboutsummaryrefslogtreecommitdiff
path: root/src/program.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/program.c')
-rw-r--r--src/program.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/src/program.c b/src/program.c
new file mode 100644
index 0000000..c396695
--- /dev/null
+++ b/src/program.c
@@ -0,0 +1,111 @@
+#include "program.h"
+#include "buffer.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define READ_END 0
+#define WRITE_END 1
+
+int program_buffer_write_callback(char *data, int size, void *userdata) {
+ Buffer *buffer = userdata;
+ buffer_append(buffer, data, size);
+ return 0;
+}
+
+int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata) {
+ /* 1 arguments */
+ if(args[0] == NULL)
+ return -1;
+
+ int fd[2];
+ if(pipe(fd) == -1) {
+ perror("Failed to open pipe");
+ return -2;
+ }
+
+ pid_t parent_pid = getpid();
+
+ pid_t pid = fork();
+ if(pid == -1) {
+ perror("Failed to fork");
+ return -3;
+ } 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);
+
+ dup2(fd[WRITE_END], STDOUT_FILENO);
+ close(fd[READ_END]);
+ close(fd[WRITE_END]);
+
+ execvp(args[0], (char* const*)args);
+ perror("execvp");
+ exit(127);
+ } 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) {
+ fprintf(stderr, "Failed to execute program (");
+ const char **arg = args;
+ while(*arg) {
+ if(arg != args)
+ fputc(' ', stderr);
+ fprintf(stderr, "'%s'", *arg);
+ ++arg;
+ }
+ fprintf(stderr, "), exit status %d\n", exit_status);
+ result = -exit_status;
+ goto cleanup;
+ }
+
+ cleanup:
+ close(fd[READ_END]);
+ return result;
+ }
+}