#include "program.h" #include "buffer.h" #include #include #include #include #include #include #include #include #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; } 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) { fprintf(stderr, "Failed to read from pipe, error: %s\n", strerror(errno)); return -1; } 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 argument */ 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"); close(fd[READ_END]); close(fd[WRITE_END]); 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 = 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) { if(arg != args) fputc(' ', stderr); fprintf(stderr, "'%s'", *arg); ++arg; } fprintf(stderr, ")\n"); } */ 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 = -1; 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]); *stdin_file = stdin_fd[WRITE_END]; } if(stdout_file) { close(stdout_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; }