diff options
Diffstat (limited to 'src/pingu_adm.c')
-rw-r--r-- | src/pingu_adm.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/src/pingu_adm.c b/src/pingu_adm.c new file mode 100644 index 0000000..9062e6e --- /dev/null +++ b/src/pingu_adm.c @@ -0,0 +1,157 @@ + +#include <sys/socket.h> +#include <sys/un.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <ev.h> + +#include "list.h" +#include "log.h" +#include "pingu_adm.h" +#include "pingu_iface.h" +#include "pingu_host.h" + +static struct ev_io accept_io; + +struct adm_conn { + struct ev_io io; + struct ev_timer timeout; + int num_read; + char cmd[512]; +}; + +static void pingu_adm_free_conn(struct ev_loop *loop, struct adm_conn *rm) +{ + int fd = rm->io.fd; + + ev_io_stop(loop, &rm->io); + ev_timer_stop(loop, &rm->timeout); + close(fd); + free(rm); + log_debug("Admin connection closed"); +} + +static struct { + const char *command; + void (*handler)(int fd, char *args); +} adm_handler[] = { + { "host-status", pingu_host_dump_status }, + { "gateway-status", pingu_iface_dump_status }, + { "pings", pingu_iface_dump_pings }, + { "routes", pingu_iface_dump_routes }, + { NULL, NULL } +}; + +static void pingu_adm_recv_cb (struct ev_loop *loop, struct ev_io *w, + int revents) +{ + struct adm_conn *conn = container_of(w, struct adm_conn, io); + int len, i; + char *args; + + len = recv(conn->io.fd, conn->cmd, sizeof(conn->cmd) - conn->num_read, + MSG_DONTWAIT); + if (len < 0 && errno == EAGAIN) + return; + if (len <= 0) + goto err; + + conn->num_read += len; + if (conn->num_read >= sizeof(conn->cmd)) + goto err; + if (conn->cmd[conn->num_read - 1] != '\n') + goto err; + + conn->num_read--; + conn->cmd[conn->num_read] = '\0'; + + args = strchr(conn->cmd, ' '); + if (args != NULL) + *args++ = '\0'; + + for (i = 0; adm_handler[i].command != NULL; i++) { + if (strncmp(conn->cmd, adm_handler[i].command, len) != 0) + continue; + log_debug("Admin command: %s (args='%s')", conn->cmd, args ? args : "NULL"); + adm_handler[i].handler(conn->io.fd, args); + conn->cmd[0] = '\0'; + conn->num_read = 0; + break; + } + + if (adm_handler[i].command == NULL) + log_error("%s: unknown admim command", conn->cmd); + +err: + pingu_adm_free_conn(loop, conn); +} + +static void pingu_adm_timeout_cb (struct ev_loop *loop, struct ev_timer *t, + int revents) +{ + log_debug("Admin connection timed out"); + pingu_adm_free_conn(loop, container_of(t, struct adm_conn, timeout)); +} + +static void pingu_adm_accept_cb(struct ev_loop *loop, struct ev_io *w, + int revents) +{ + struct adm_conn *conn; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + int fd; + + fd = accept(w->fd, (struct sockaddr *) &from, &fromlen); + if (fd < 0) { + log_perror("accept"); + return; + } + log_debug("New admin connection"); + fcntl(fd, F_SETFD, FD_CLOEXEC); + conn = calloc(1, sizeof(struct adm_conn)); + + ev_io_init(&conn->io, pingu_adm_recv_cb, fd, EV_READ); + ev_io_start(loop, &conn->io); + ev_timer_init(&conn->timeout, pingu_adm_timeout_cb, 10.0, 0.); + ev_timer_start(loop, &conn->timeout); +} + + +int pingu_adm_init(struct ev_loop *loop, const char *socket_path) +{ + struct sockaddr_un sun; + int fd; + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path)); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + log_perror("socket"); + return -1; + } + + fcntl(fd, F_SETFD, FD_CLOEXEC); + unlink(socket_path); + if (bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0) + goto perr_close; + + if (listen(fd, 5) < 0) + goto perr_close; + + ev_io_init(&accept_io, pingu_adm_accept_cb, fd, EV_READ); + ev_io_start(loop, &accept_io); + return 0; + +perr_close: + log_perror(socket_path); + close(fd); + return -1; + +} |