aboutsummaryrefslogtreecommitdiff
path: root/src/Program.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Program.cpp')
-rw-r--r--src/Program.cpp198
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);