diff --git a/qmeventd/qmeventd.c b/qmeventd/qmeventd.c index 8d32827a..3256d428 100644 --- a/qmeventd/qmeventd.c +++ b/qmeventd/qmeventd.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,15 +37,19 @@ #include #include #include +#include #include "qmeventd.h" +#define DEFAULT_KILL_TIMEOUT 60 + static int verbose = 0; +static int kill_timeout = DEFAULT_KILL_TIMEOUT; static int epoll_fd = 0; static const char *progname; GHashTable *vm_clients; // key=vmid (freed on remove), value=*Client (free manually) GSList *forced_cleanups; -volatile sig_atomic_t alarm_triggered = 0; +static int needs_cleanup = 0; /* * Helper functions @@ -56,6 +61,7 @@ usage() fprintf(stderr, "Usage: %s [-f] [-v] PATH\n", progname); fprintf(stderr, " -f run in foreground (default: false)\n"); fprintf(stderr, " -v verbose (default: false)\n"); + fprintf(stderr, " -t kill timeout (default: %ds)\n", DEFAULT_KILL_TIMEOUT); fprintf(stderr, " PATH use PATH for socket\n"); } @@ -436,6 +442,11 @@ cleanup_client(struct Client *client) break; } + if (client->pidfd > 0) { + (void)close(client->pidfd); + } + VERBOSE_PRINT("removing %s from forced cleanups\n", client->qemu.vmid); + forced_cleanups = g_slist_remove(forced_cleanups, client); free(client); } @@ -469,16 +480,13 @@ terminate_client(struct Client *client) int err = kill(client->pid, SIGTERM); log_neg(err, "kill"); - struct CleanupData *data_ptr = malloc(sizeof(struct CleanupData)); - struct CleanupData data = { - .pid = client->pid, - .pidfd = pidfd - }; - *data_ptr = data; - forced_cleanups = g_slist_prepend(forced_cleanups, (void *)data_ptr); + time_t timeout = time(NULL) + kill_timeout; - // resets any other alarms, but will fire eventually and cleanup all - alarm(5); + client->pidfd = pidfd; + client->timeout = timeout; + + forced_cleanups = g_slist_prepend(forced_cleanups, (void *)client); + needs_cleanup = 1; } void @@ -551,58 +559,51 @@ handle_client(struct Client *client) json_tokener_free(tok); } - -/* - * SIGALRM and cleanup handling - * - * terminate_client will set an alarm for 5 seconds and add its client's PID to - * the forced_cleanups list - when the timer expires, we iterate the list and - * attempt to issue SIGKILL to all processes which haven't yet stopped. - */ - static void -alarm_handler(__attribute__((unused)) int signum) +sigkill(void *ptr, void *time_ptr) { - alarm_triggered = 1; -} - -static void -sigkill(void *ptr, __attribute__((unused)) void *unused) -{ - struct CleanupData data = *((struct CleanupData *)ptr); + struct Client *data = ptr; int err; - if (data.pidfd > 0) { - err = pidfd_send_signal(data.pidfd, SIGKILL, NULL, 0); - (void)close(data.pidfd); + if (data->timeout != 0 && data->timeout > *(time_t *)time_ptr) { + return; + } + + if (data->pidfd > 0) { + err = pidfd_send_signal(data->pidfd, SIGKILL, NULL, 0); + (void)close(data->pidfd); + data->pidfd = -1; } else { - err = kill(data.pid, SIGKILL); + err = kill(data->pid, SIGKILL); } if (err < 0) { if (errno != ESRCH) { fprintf(stderr, "SIGKILL cleanup of pid '%d' failed - %s\n", - data.pid, strerror(errno)); + data->pid, strerror(errno)); } } else { fprintf(stderr, "cleanup failed, terminating pid '%d' with SIGKILL\n", - data.pid); + data->pid); } + + data->timeout = 0; + + // remove ourselves from the list + forced_cleanups = g_slist_remove(forced_cleanups, ptr); } static void handle_forced_cleanup() { - if (alarm_triggered) { + if (g_slist_length(forced_cleanups) > 0) { VERBOSE_PRINT("clearing forced cleanup backlog\n"); - alarm_triggered = 0; - g_slist_foreach(forced_cleanups, sigkill, NULL); - g_slist_free_full(forced_cleanups, free); - forced_cleanups = NULL; + time_t cur_time = time(NULL); + g_slist_foreach(forced_cleanups, sigkill, &cur_time); } + needs_cleanup = g_slist_length(forced_cleanups) > 0; } - int main(int argc, char *argv[]) { @@ -611,7 +612,7 @@ main(int argc, char *argv[]) char *socket_path = NULL; progname = argv[0]; - while ((opt = getopt(argc, argv, "hfv")) != -1) { + while ((opt = getopt(argc, argv, "hfvt:")) != -1) { switch (opt) { case 'f': daemonize = 0; @@ -619,6 +620,15 @@ main(int argc, char *argv[]) case 'v': verbose = 1; break; + case 't': + errno = 0; + char *endptr = NULL; + kill_timeout = strtoul(optarg, &endptr, 10); + if (errno != 0 || *endptr != '\0' || kill_timeout == 0) { + usage(); + exit(EXIT_FAILURE); + } + break; case 'h': usage(); exit(EXIT_SUCCESS); @@ -635,7 +645,6 @@ main(int argc, char *argv[]) } signal(SIGCHLD, SIG_IGN); - signal(SIGALRM, alarm_handler); socket_path = argv[optind]; @@ -669,11 +678,7 @@ main(int argc, char *argv[]) int nevents; for(;;) { - nevents = epoll_wait(epoll_fd, events, 1, -1); - if (nevents < 0 && errno == EINTR) { - handle_forced_cleanup(); - continue; - } + nevents = epoll_wait(epoll_fd, events, 1, needs_cleanup ? 10*1000 : -1); bail_neg(nevents, "epoll_wait"); for (int n = 0; n < nevents; n++) { @@ -688,7 +693,6 @@ main(int argc, char *argv[]) handle_client((struct Client *)events[n].data.ptr); } } - handle_forced_cleanup(); } } diff --git a/qmeventd/qmeventd.h b/qmeventd/qmeventd.h index 2cf1947f..9afc9351 100644 --- a/qmeventd/qmeventd.h +++ b/qmeventd/qmeventd.h @@ -7,6 +7,7 @@ */ #include +#include #ifndef __NR_pidfd_open #define __NR_pidfd_open 434 @@ -63,6 +64,8 @@ struct Client { int fd; pid_t pid; + int pidfd; + time_t timeout; ClientType type; ClientState state; @@ -83,11 +86,6 @@ struct Client { } vzdump; }; -struct CleanupData { - pid_t pid; - int pidfd; -}; - void handle_qmp_handshake(struct Client *client); void handle_qmp_event(struct Client *client, struct json_object *obj); void handle_qmp_return(struct Client *client, struct json_object *data, bool error);