#include "../include/Program.h" #include #include #include #include #include #include #include #include #define READ_END 0 #define WRITE_END 1 int exec_program(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"); 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 = 0; int status; char buffer[4097]; 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 && 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; } } int wait_program(pid_t process_id) { int status; if(waitpid(process_id, &status, 0) == -1) { perror("waitpid failed"); return -errno; } if(!WIFEXITED(status)) return -4; return WEXITSTATUS(status); } int wait_program_non_blocking(pid_t process_id, int *status) { int s; int wait_result = waitpid(process_id, &s, WNOHANG); if(wait_result == -1) { perror("waitpid failed"); *status = -errno; return 0; } else if(wait_result == 0) { /* the child process is still running */ *status = 0; return 0; } if(!WIFEXITED(s)) { *status = -4; return 0; } *status = WEXITSTATUS(s); return 1; } int exec_program_async(const char **args, pid_t *result_process_id) { /* 1 arguments */ if(args[0] == NULL) return -1; pid_t parent_pid = getpid(); pid_t pid = fork(); if(pid == -1) { int err = errno; perror("Failed to fork"); return -err; } else if(pid == 0) { /* child */ if(result_process_id) { 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); execvp(args[0], (char* const*)args); perror("execvp"); _exit(127); } else { setsid(); signal(SIGHUP, SIG_IGN); // TODO: Still creates zombie??? find a fix! // Daemonize child to make the parent the init process which will reap the zombie child pid_t second_child = fork(); if(second_child == 0) { // child execvp(args[0], (char* const*)args); perror("execvp"); _exit(127); } else if(second_child != -1) { _exit(0); } } } else { /* parent */ if(result_process_id) *result_process_id = pid; } return 0; } #if 0 int program_pipe_write(ProgramPipe *self, const char *data, size_t size) { ssize_t bytes_written = write(self->write_fd, data, size); if(bytes_written == -1) { int err = errno; perror("Failed to write to pipe to program"); return -err; } return 0; } int program_pipe_read(ProgramPipe *self, ProgramOutputCallback output_callback, void *userdata) { char buffer[2048]; for(;;) { ssize_t bytes_read = read(self->read_fd, buffer, sizeof(buffer) - 1); if(bytes_read == 0) { break; } else if(bytes_read == -1) { int err = errno; perror("Failed to read from pipe to program"); return -err; } buffer[bytes_read] = '\0'; if(output_callback && output_callback(buffer, bytes_read, userdata) != 0) break; } return 0; } void program_pipe_close(ProgramPipe *self) { close(self->read_fd); close(self->write_fd); self->read_fd = -1; self->write_fd = -1; } #endif