diff options
Diffstat (limited to 'src/Program.cpp')
-rw-r--r-- | src/Program.cpp | 198 |
1 files changed, 188 insertions, 10 deletions
diff --git a/src/Program.cpp b/src/Program.cpp index c66611d..ee189f7 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -13,8 +13,14 @@ #define READ_END 0 #define WRITE_END 1 +struct ReadWriteProgram { + pid_t pid = -1; + int read_fd = -1; + int write_fd = -1; +}; + struct ThreadProgram { - ReadProgram read_program; + ReadWriteProgram program; bool killed; }; @@ -26,8 +32,9 @@ public: CurrentThreadProgram() { std::lock_guard<std::mutex> lock(thread_current_program_mutex); ThreadProgram thread_program; - thread_program.read_program.pid = -1; - thread_program.read_program.read_fd = -1; + thread_program.program.pid = -1; + thread_program.program.read_fd = -1; + thread_program.program.write_fd = -1; thread_program.killed = false; thread_current_program[std::this_thread::get_id()] = std::move(thread_program); } @@ -37,30 +44,52 @@ public: thread_current_program.erase(std::this_thread::get_id()); } + // TODO: Make sure the thread specific program has been stopped before this is called. exec_program_pipe needs to be modified for that void set(ReadProgram read_program) { std::lock_guard<std::mutex> lock(thread_current_program_mutex); auto it = thread_current_program.find(std::this_thread::get_id()); + if(it != thread_current_program.end()) { + it->second.program.pid = read_program.pid; + it->second.program.read_fd = read_program.read_fd; + it->second.program.write_fd = -1; + } + } + + // TODO: Make sure the thread specific program has been stopped before this is called. exec_program_pipe needs to be modified for that + void set(ReadWriteProgram program) { + std::lock_guard<std::mutex> lock(thread_current_program_mutex); + auto it = thread_current_program.find(std::this_thread::get_id()); if(it != thread_current_program.end()) - it->second.read_program = std::move(read_program); + it->second.program = std::move(program); } void clear() { std::lock_guard<std::mutex> lock(thread_current_program_mutex); auto it = thread_current_program.find(std::this_thread::get_id()); if(it != thread_current_program.end()) { - it->second.read_program.pid = -1; - it->second.read_program.read_fd = -1; + it->second.program.pid = -1; + it->second.program.read_fd = -1; + it->second.program.write_fd = -1; } } + // TODO: This same mutex should be used in the exec_... functions when they do kill() etc to make sure we dont accidentally kill another program here if another process gets the killed process id! void kill_in_thread(const std::thread::id &thread_id) { std::lock_guard<std::mutex> lock(thread_current_program_mutex); auto it = thread_current_program.find(thread_id); if(it != thread_current_program.end()) { - if(it->second.read_program.read_fd != -1) - close(it->second.read_program.read_fd); - if(it->second.read_program.pid != -1) - kill(it->second.read_program.pid, SIGTERM); + if(it->second.program.read_fd != -1) { + close(it->second.program.read_fd); + it->second.program.read_fd = -1; + } + if(it->second.program.write_fd != -1) { + close(it->second.program.write_fd); + it->second.program.write_fd = -1; + } + if(it->second.program.pid != -1) { + kill(it->second.program.pid, SIGTERM); + it->second.program.pid = -1; + } it->second.killed = true; } } @@ -116,6 +145,155 @@ int exec_program_pipe(const char **args, ReadProgram *read_program) { } } +static int exec_program_pipe2(const char **args, ReadWriteProgram *program) { + program->pid = -1; + program->read_fd = -1; + program->write_fd = -1; + + /* 1 arguments */ + if(args[0] == NULL) + return -1; + + if(current_thread_program.is_killed()) + return -1; + + int read_fd[2]; + if(pipe(read_fd) == -1) { + perror("Failed to open pipe"); + return -2; + } + + int write_fd[2]; + if(pipe(write_fd) == -1) { + close(read_fd[0]); + close(read_fd[1]); + perror("Failed to open pipe"); + return -2; + } + + pid_t pid = vfork(); + if(pid == -1) { + perror("Failed to vfork"); + close(read_fd[READ_END]); + close(read_fd[WRITE_END]); + close(write_fd[READ_END]); + close(write_fd[WRITE_END]); + return -3; + } else if(pid == 0) { /* child */ + dup2(read_fd[WRITE_END], STDOUT_FILENO); + close(read_fd[READ_END]); + close(read_fd[WRITE_END]); + + dup2(write_fd[READ_END], STDIN_FILENO); + close(write_fd[READ_END]); + close(write_fd[WRITE_END]); + + execvp(args[0], (char* const*)args); + perror("execvp"); + _exit(127); + } else { /* parent */ + close(read_fd[WRITE_END]); + close(write_fd[READ_END]); + program->pid = pid; + program->read_fd = read_fd[READ_END]; + program->write_fd = write_fd[WRITE_END]; + current_thread_program.set(*program); + return 0; + } +} + +int exec_program_write_stdin(const char **args, const char *str, size_t size, ProgramOutputCallback output_callback, void *userdata, int buffer_size) { + ReadWriteProgram program; + int res = exec_program_pipe2(args, &program); + if(res != 0) + return res; + + int result = 0; + int status; + int exit_status; + + assert(buffer_size >= 1 && buffer_size <= 65536); + char *buffer = (char*)alloca(buffer_size + 1); + + const ssize_t write_buffer_size = 8192; + size_t write_offset = 0; + while(write_offset < size) { + ssize_t write_size = (ssize_t)size - (ssize_t)write_offset; + if(write_size > write_buffer_size) + write_size = write_buffer_size; + + ssize_t bytes_written = write(program.write_fd, str + write_offset, write_size); + if(bytes_written == -1) { + int err = errno; + fprintf(stderr, "Failed to write to pipe to program %s, error: %s\n", args[0], strerror(err)); + result = -err; + break; + } + + if(bytes_written < write_size) + write_size = bytes_written; + + write_offset += write_size; + } + + close(program.write_fd); + + if(result == 0) { + for(;;) { + ssize_t bytes_read = read(program.read_fd, buffer, buffer_size); + 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; + break; + } + + buffer[bytes_read] = '\0'; + if(output_callback) { + result = output_callback(buffer, bytes_read, userdata); + if(result != 0) + break; + } + } + } + + // TODO: Set program.pid to -1 and with currenthreadprogram mutex. Same in other places + if(result != 0) + kill(program.pid, SIGTERM); + + if(waitpid(program.pid, &status, 0) == -1) { + perror("waitpid failed"); + result = -5; + goto cleanup; + } + + if(!WIFEXITED(status)) { + result = -4; + goto cleanup; + } + + 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; + } + + cleanup: + program_clear_current_thread(); + close(program.read_fd); + return result; +} + int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int buffer_size) { ReadProgram read_program; int res = exec_program_pipe(args, &read_program); |