2016-02-28 15:53:55 -08:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/socket.h>
|
2016-03-07 15:19:00 -08:00
|
|
|
#include <sys/stat.h>
|
2016-02-28 15:53:55 -08:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2016-02-29 17:12:06 -08:00
|
|
|
#include "flow.h"
|
2016-03-05 22:54:26 -08:00
|
|
|
#include "log.h"
|
2016-02-28 15:53:55 -08:00
|
|
|
#include "peer.h"
|
|
|
|
|
#include "uuid.h"
|
|
|
|
|
#include "wakeup.h"
|
|
|
|
|
|
|
|
|
|
#include "exec.h"
|
|
|
|
|
|
|
|
|
|
struct exec {
|
|
|
|
|
struct peer peer;
|
2016-03-06 18:50:15 -08:00
|
|
|
struct peer log_peer;
|
2016-02-28 15:53:55 -08:00
|
|
|
uint8_t id[UUID_LEN];
|
|
|
|
|
char *command;
|
2016-02-29 17:12:06 -08:00
|
|
|
struct flow *flow;
|
2016-02-28 15:53:55 -08:00
|
|
|
void *passthrough;
|
|
|
|
|
pid_t child;
|
|
|
|
|
struct list_head exec_list;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct list_head exec_head = LIST_HEAD_INIT(exec_head);
|
|
|
|
|
|
|
|
|
|
static void exec_spawn_wrapper(struct peer *);
|
|
|
|
|
|
2016-03-06 18:50:15 -08:00
|
|
|
static void exec_harvest(struct exec *exec) {
|
2016-03-06 18:57:37 -08:00
|
|
|
if (exec->child > 0) {
|
|
|
|
|
int status;
|
2016-03-06 18:50:15 -08:00
|
|
|
assert(waitpid(exec->child, &status, 0) == exec->child);
|
|
|
|
|
exec->child = -1;
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
log_write('E', exec->id, "Client exited with status %d", WEXITSTATUS(status));
|
|
|
|
|
} else {
|
|
|
|
|
assert(WIFSIGNALED(status));
|
|
|
|
|
log_write('E', exec->id, "Client exited with signal %d", WTERMSIG(status));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (exec->log_peer.fd >= 0) {
|
|
|
|
|
assert(!close(exec->log_peer.fd));
|
|
|
|
|
exec->log_peer.fd = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-28 15:53:55 -08:00
|
|
|
static void exec_del(struct exec *exec) {
|
2016-03-02 19:20:25 -08:00
|
|
|
flow_ref_dec(exec->flow);
|
|
|
|
|
|
2016-02-28 15:53:55 -08:00
|
|
|
if (exec->child > 0) {
|
2016-03-05 22:54:26 -08:00
|
|
|
log_write('E', exec->id, "Sending SIGTERM to child process %d", exec->child);
|
2016-02-28 15:53:55 -08:00
|
|
|
// Racy with the process terminating, so don't assert on it
|
|
|
|
|
kill(exec->child, SIGTERM);
|
|
|
|
|
}
|
2016-03-06 18:50:15 -08:00
|
|
|
exec_harvest(exec);
|
2016-03-02 18:52:02 -08:00
|
|
|
list_del(&exec->exec_list);
|
2016-02-28 15:53:55 -08:00
|
|
|
free(exec->command);
|
|
|
|
|
free(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void exec_close_handler(struct peer *peer) {
|
|
|
|
|
struct exec *exec = (struct exec *) peer;
|
2016-03-06 18:50:15 -08:00
|
|
|
exec_harvest(exec);
|
2016-02-28 15:53:55 -08:00
|
|
|
uint32_t delay = wakeup_get_retry_delay_ms(1);
|
2016-03-05 22:54:26 -08:00
|
|
|
log_write('E', exec->id, "Will retry in %ds", delay / 1000);
|
2016-02-28 15:53:55 -08:00
|
|
|
exec->peer.event_handler = exec_spawn_wrapper;
|
|
|
|
|
wakeup_add((struct peer *) exec, delay);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-06 18:50:15 -08:00
|
|
|
static void exec_log_handler(struct peer *peer) {
|
|
|
|
|
// Do you believe in magic?
|
|
|
|
|
struct exec *exec = container_of(peer, struct exec, log_peer);
|
|
|
|
|
|
|
|
|
|
char linebuf[4096];
|
2016-03-06 18:57:37 -08:00
|
|
|
ssize_t ret = read(exec->log_peer.fd, linebuf, 4096);
|
|
|
|
|
if (ret <= 0) {
|
2016-03-06 18:50:15 -08:00
|
|
|
log_write('E', exec->id, "Log input stream closed");
|
|
|
|
|
assert(!close(exec->log_peer.fd));
|
|
|
|
|
exec->log_peer.fd = -1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-03-06 18:57:37 -08:00
|
|
|
size_t len = (size_t) ret;
|
|
|
|
|
char *iter = linebuf, *eol;
|
|
|
|
|
while ((eol = memchr(iter, '\n', len))) {
|
|
|
|
|
assert(eol >= iter);
|
|
|
|
|
size_t linelen = (size_t) (eol - iter);
|
2016-03-06 22:08:04 -08:00
|
|
|
log_write('E', exec->id, "(child output) %.*s", (int) linelen, iter);
|
2016-03-06 18:57:37 -08:00
|
|
|
iter += (linelen + 1);
|
|
|
|
|
len -= (linelen + 1);
|
|
|
|
|
}
|
|
|
|
|
if (len) {
|
2016-03-06 22:08:04 -08:00
|
|
|
log_write('E', exec->id, "(child output) %.*s", (int) len, iter);
|
2016-03-06 18:50:15 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void exec_parent(struct exec *exec, pid_t child, int data_fd, int log_fd) {
|
2016-02-28 15:53:55 -08:00
|
|
|
exec->child = child;
|
2016-03-05 22:54:26 -08:00
|
|
|
log_write('E', exec->id, "Child started as process %d", exec->child);
|
2016-02-28 15:53:55 -08:00
|
|
|
|
2016-03-06 18:50:15 -08:00
|
|
|
exec->log_peer.fd = log_fd;
|
|
|
|
|
exec->log_peer.event_handler = exec_log_handler;
|
|
|
|
|
peer_epoll_add(&exec->log_peer, EPOLLIN);
|
|
|
|
|
|
2016-03-02 22:25:56 -08:00
|
|
|
exec->peer.event_handler = exec_close_handler;
|
2016-03-06 18:50:15 -08:00
|
|
|
if (!flow_new_send_hello(data_fd, exec->flow, exec->passthrough, (struct peer *) exec)) {
|
2016-02-28 15:53:55 -08:00
|
|
|
exec_close_handler((struct peer *) exec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-06 18:50:15 -08:00
|
|
|
static void __attribute__ ((noreturn)) exec_child(const struct exec *exec, int data_fd, int log_fd) {
|
2016-02-28 15:53:55 -08:00
|
|
|
assert(setsid() != -1);
|
|
|
|
|
// We leave stderr open from child to parent
|
|
|
|
|
// Other than that, fds should have CLOEXEC set
|
2016-03-06 18:50:15 -08:00
|
|
|
if (data_fd != STDIN_FILENO) {
|
|
|
|
|
assert(dup2(data_fd, STDIN_FILENO) == STDIN_FILENO);
|
|
|
|
|
}
|
|
|
|
|
if (data_fd != STDOUT_FILENO) {
|
|
|
|
|
assert(dup2(data_fd, STDOUT_FILENO) == STDOUT_FILENO);
|
2016-02-28 15:53:55 -08:00
|
|
|
}
|
2016-03-06 18:50:15 -08:00
|
|
|
if (data_fd != STDIN_FILENO && data_fd != STDOUT_FILENO) {
|
|
|
|
|
assert(!close(data_fd));
|
2016-02-28 15:53:55 -08:00
|
|
|
}
|
2016-03-06 18:50:15 -08:00
|
|
|
if (log_fd != STDERR_FILENO) {
|
|
|
|
|
assert(dup2(log_fd, STDERR_FILENO) == STDERR_FILENO);
|
|
|
|
|
assert(!close(log_fd));
|
2016-02-28 15:53:55 -08:00
|
|
|
}
|
|
|
|
|
assert(!execl("/bin/sh", "sh", "-c", exec->command, NULL));
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void exec_spawn(struct exec *exec) {
|
2016-03-06 18:50:15 -08:00
|
|
|
log_write('E', exec->id, "Executing: %s", exec->command);
|
|
|
|
|
int data_fds[2], log_fds[2];
|
|
|
|
|
// Leave these sockets blocking; we move in lock step with subprograms
|
|
|
|
|
assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, data_fds));
|
|
|
|
|
assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, log_fds));
|
2016-02-28 15:53:55 -08:00
|
|
|
|
|
|
|
|
int res = fork();
|
|
|
|
|
assert(res >= 0);
|
|
|
|
|
if (res) {
|
2016-03-06 18:50:15 -08:00
|
|
|
assert(!close(data_fds[1]));
|
|
|
|
|
assert(!close(log_fds[1]));
|
|
|
|
|
assert(!shutdown(log_fds[0], SHUT_WR));
|
|
|
|
|
exec_parent(exec, res, data_fds[0], log_fds[0]);
|
2016-02-28 15:53:55 -08:00
|
|
|
} else {
|
2016-03-06 18:50:15 -08:00
|
|
|
assert(!close(data_fds[0]));
|
|
|
|
|
assert(!close(log_fds[0]));
|
|
|
|
|
exec_child(exec, data_fds[1], log_fds[1]);
|
2016-02-28 15:53:55 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void exec_spawn_wrapper(struct peer *peer) {
|
|
|
|
|
struct exec *exec = (struct exec *) peer;
|
|
|
|
|
exec_spawn(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void exec_cleanup() {
|
|
|
|
|
struct exec *iter, *next;
|
|
|
|
|
list_for_each_entry_safe(iter, next, &exec_head, exec_list) {
|
|
|
|
|
exec_del(iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-07 11:26:25 -08:00
|
|
|
void exec_new(const char *command, struct flow *flow, void *passthrough) {
|
2016-03-02 19:20:25 -08:00
|
|
|
flow_ref_inc(flow);
|
2016-02-28 15:53:55 -08:00
|
|
|
|
|
|
|
|
struct exec *exec = malloc(sizeof(*exec));
|
2016-03-02 14:23:58 -08:00
|
|
|
assert(exec);
|
2016-02-28 15:53:55 -08:00
|
|
|
exec->peer.fd = -1;
|
|
|
|
|
uuid_gen(exec->id);
|
|
|
|
|
exec->command = strdup(command);
|
2016-03-02 14:23:58 -08:00
|
|
|
assert(exec->command);
|
2016-02-29 17:12:06 -08:00
|
|
|
exec->flow = flow;
|
2016-02-28 15:53:55 -08:00
|
|
|
exec->passthrough = passthrough;
|
|
|
|
|
|
|
|
|
|
list_add(&exec->exec_list, &exec_head);
|
|
|
|
|
|
|
|
|
|
exec_spawn(exec);
|
|
|
|
|
}
|