summaryrefslogtreecommitdiff
path: root/usr.sbin/relayd/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/relayd/control.c')
-rw-r--r--usr.sbin/relayd/control.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/usr.sbin/relayd/control.c b/usr.sbin/relayd/control.c
new file mode 100644
index 00000000000..2f994e458f4
--- /dev/null
+++ b/usr.sbin/relayd/control.c
@@ -0,0 +1,340 @@
+/* $OpenBSD: control.c,v 1.1 2006/12/16 11:45:07 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <net/if.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "hostated.h"
+
+#define CONTROL_BACKLOG 5
+
+struct ctl_connlist ctl_conns;
+
+int control_imsg_relay(struct imsg *imsg);
+
+struct ctl_conn *control_connbyfd(int);
+struct ctl_conn *control_connbypid(pid_t);
+void control_close(int);
+
+int
+control_init(void)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("control_init: socket");
+ return (-1);
+ }
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, HOSTATED_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(HOSTATED_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", HOSTATED_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("control_init: bind: %s", HOSTATED_SOCKET);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(HOSTATED_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(HOSTATED_SOCKET);
+ return (-1);
+ }
+
+ session_socket_blockmode(fd, BM_NONBLOCK);
+ control_state.fd = fd;
+
+ return (0);
+}
+
+int
+control_listen(void)
+{
+
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+ log_warn("control_listen: listen");
+ return (-1);
+ }
+
+ event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
+ control_accept, NULL);
+ event_add(&control_state.ev, NULL);
+
+ return (0);
+}
+
+void
+control_cleanup(void)
+{
+
+ unlink(HOSTATED_SOCKET);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *arg)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno != EWOULDBLOCK && errno != EINTR)
+ log_warn("control_accept");
+ return;
+ }
+
+ session_socket_blockmode(connfd, BM_NONBLOCK);
+
+ if ((c = malloc(sizeof(struct ctl_conn))) == NULL) {
+ log_warn("control_accept");
+ return;
+ }
+
+ imsg_init(&c->ibuf, connfd, control_dispatch_imsg);
+ c->ibuf.events = EV_READ;
+ event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events,
+ c->ibuf.handler, &c->ibuf);
+ event_add(&c->ibuf.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL)
+ log_warn("control_close: fd %d: not found", fd);
+
+ msgbuf_clear(&c->ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->ibuf.ev);
+ close(c->ibuf.fd);
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *arg)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ objid_t id;
+ int n;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("control_dispatch_imsg: fd %d: not found", fd);
+ return;
+ }
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(&c->ibuf)) <= 0) {
+ control_close(fd);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&c->ibuf.w) < 0) {
+ control_close(fd);
+ return;
+ }
+ imsg_event_add(&c->ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_SHOW_SUM:
+ show(c);
+ break;
+ case IMSG_CTL_SERVICE_DISABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (disable_service(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_SERVICE_ENABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (enable_service(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_TABLE_DISABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (disable_table(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_TABLE_ENABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (enable_table(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_HOST_DISABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (disable_host(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_HOST_ENABLE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
+ fatalx("invalid imsg header len");
+ memcpy(&id, imsg.data, sizeof(id));
+ if (enable_host(c, id))
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0,
+ NULL, 0);
+ else
+ imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0,
+ NULL, 0);
+ break;
+ case IMSG_CTL_SHUTDOWN:
+ case IMSG_CTL_RELOAD:
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, NULL, 0);
+ break;
+ default:
+ log_debug("control_dispatch_imsg: "
+ "error handling imsg %d", imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->ibuf);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+ return (0);
+
+ return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid,
+ imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
+
+void
+session_socket_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fnctl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fnctl F_SETFL");
+}