From 928f2525c29929de0c2ab520f48c82b5cb882aa7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 30 Oct 2020 16:47:40 +0100 Subject: Matrix: re-add /logout, cancel task immediately Cancel video download when pressing escape, other fixes.. --- src/Program.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/Program.cpp (limited to 'src/Program.cpp') diff --git a/src/Program.cpp b/src/Program.cpp new file mode 100644 index 0000000..136a494 --- /dev/null +++ b/src/Program.cpp @@ -0,0 +1,246 @@ +#include "../include/Program.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define READ_END 0 +#define WRITE_END 1 + +class CurrentThreadProgram { +public: + void set(ReadProgram read_program) { + std::lock_guard lock(mutex); + thread_current_program[std::this_thread::get_id()] = read_program; + } + + void clear() { + std::lock_guard lock(mutex); + thread_current_program.erase(std::this_thread::get_id()); + } + + void kill_in_thread(const std::thread::id &thread_id) { + std::lock_guard lock(mutex); + auto it = thread_current_program.find(thread_id); + if(it != thread_current_program.end()) { + close(it->second.read_fd); + kill(it->second.pid, SIGTERM); + } + } +private: + std::unordered_map thread_current_program; + std::mutex mutex; +}; + +static CurrentThreadProgram current_thread_program; + +int exec_program_pipe(const char **args, ReadProgram *read_program) { + /* 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]); + read_program->pid = pid; + read_program->read_fd = fd[READ_END]; + current_thread_program.set(*read_program); + return 0; + } +} + +int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata) { + ReadProgram read_program; + int res = exec_program_pipe(args, &read_program); + if(res != 0) + return res; + + int result = 0; + int status; + int exit_status; + + char buffer[4097]; + + for(;;) { + ssize_t bytes_read = read(read_program.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 to program %s, error: %s\n", args[0], strerror(err)); + result = -err; + break; + } + + //check if running. Also do the same in download_to_json + + buffer[bytes_read] = '\0'; + if(output_callback) { + result = output_callback(buffer, bytes_read, userdata); + if(result != 0) + break; + } + } + program_clear_current_thread(); + + if(result != 0) + kill(read_program.pid, SIGTERM); + + if(waitpid(read_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: + close(read_program.read_fd); + 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; +} + +void program_clear_current_thread() { + current_thread_program.clear(); +} + +void program_kill_in_thread(const std::thread::id &thread_id) { + current_thread_program.kill_in_thread(thread_id); +} -- cgit v1.2.3