1*6676295fSclaudio /* $OpenBSD: control.c,v 1.21 2024/11/21 13:38:45 claudio Exp $ */ 2b7b6a941Sreyk 3b7b6a941Sreyk /* 4b7b6a941Sreyk * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5b7b6a941Sreyk * 6b7b6a941Sreyk * Permission to use, copy, modify, and distribute this software for any 7b7b6a941Sreyk * purpose with or without fee is hereby granted, provided that the above 8b7b6a941Sreyk * copyright notice and this permission notice appear in all copies. 9b7b6a941Sreyk * 10b7b6a941Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11b7b6a941Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12b7b6a941Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13b7b6a941Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14b7b6a941Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15b7b6a941Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16b7b6a941Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17b7b6a941Sreyk */ 18b7b6a941Sreyk 19b7b6a941Sreyk #include <sys/queue.h> 20b7b6a941Sreyk #include <sys/stat.h> 21b7b6a941Sreyk #include <sys/socket.h> 2286f952e4Sreyk #include <sys/time.h> 23b7b6a941Sreyk #include <sys/un.h> 24b7b6a941Sreyk 25b7b6a941Sreyk #include <errno.h> 26b7b6a941Sreyk #include <event.h> 27b7b6a941Sreyk #include <fcntl.h> 28b7b6a941Sreyk #include <stdlib.h> 29b7b6a941Sreyk #include <string.h> 30b7b6a941Sreyk #include <unistd.h> 3186f952e4Sreyk #include <imsg.h> 32b7b6a941Sreyk 33b7b6a941Sreyk #include "httpd.h" 34b7b6a941Sreyk 35b7b6a941Sreyk #define CONTROL_BACKLOG 5 36b7b6a941Sreyk 37c5fa57f5Sdv struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); 38b7b6a941Sreyk 39b7b6a941Sreyk void control_accept(int, short, void *); 40b7b6a941Sreyk void control_close(int, struct control_sock *); 41b7b6a941Sreyk 42b7b6a941Sreyk int 43b7b6a941Sreyk control_init(struct privsep *ps, struct control_sock *cs) 44b7b6a941Sreyk { 45b7b6a941Sreyk struct httpd *env = ps->ps_env; 46b7b6a941Sreyk struct sockaddr_un sun; 47b7b6a941Sreyk int fd; 48b7b6a941Sreyk mode_t old_umask, mode; 49b7b6a941Sreyk 50b7b6a941Sreyk if (cs->cs_name == NULL) 51b7b6a941Sreyk return (0); 52b7b6a941Sreyk 53838637bcSreyk if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) { 54b7b6a941Sreyk log_warn("%s: socket", __func__); 55b7b6a941Sreyk return (-1); 56b7b6a941Sreyk } 57b7b6a941Sreyk 58b7b6a941Sreyk sun.sun_family = AF_UNIX; 59b7b6a941Sreyk if (strlcpy(sun.sun_path, cs->cs_name, 60b7b6a941Sreyk sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { 61b7b6a941Sreyk log_warn("%s: %s name too long", __func__, cs->cs_name); 62b7b6a941Sreyk close(fd); 63b7b6a941Sreyk return (-1); 64b7b6a941Sreyk } 65b7b6a941Sreyk 66b7b6a941Sreyk if (unlink(cs->cs_name) == -1) 67b7b6a941Sreyk if (errno != ENOENT) { 68b7b6a941Sreyk log_warn("%s: unlink %s", __func__, cs->cs_name); 69b7b6a941Sreyk close(fd); 70b7b6a941Sreyk return (-1); 71b7b6a941Sreyk } 72b7b6a941Sreyk 73b7b6a941Sreyk if (cs->cs_restricted) { 74b7b6a941Sreyk old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 75b7b6a941Sreyk mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 76b7b6a941Sreyk } else { 77b7b6a941Sreyk old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 78b7b6a941Sreyk mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; 79b7b6a941Sreyk } 80b7b6a941Sreyk 81b7b6a941Sreyk if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 82b7b6a941Sreyk log_warn("%s: bind: %s", __func__, cs->cs_name); 83b7b6a941Sreyk close(fd); 84b7b6a941Sreyk (void)umask(old_umask); 85b7b6a941Sreyk return (-1); 86b7b6a941Sreyk } 87b7b6a941Sreyk (void)umask(old_umask); 88b7b6a941Sreyk 89b7b6a941Sreyk if (chmod(cs->cs_name, mode) == -1) { 90b7b6a941Sreyk log_warn("%s: chmod", __func__); 91b7b6a941Sreyk close(fd); 92b7b6a941Sreyk (void)unlink(cs->cs_name); 93b7b6a941Sreyk return (-1); 94b7b6a941Sreyk } 95b7b6a941Sreyk 96b7b6a941Sreyk cs->cs_fd = fd; 97b7b6a941Sreyk cs->cs_env = env; 98b7b6a941Sreyk 99b7b6a941Sreyk return (0); 100b7b6a941Sreyk } 101b7b6a941Sreyk 102b7b6a941Sreyk int 103b7b6a941Sreyk control_listen(struct control_sock *cs) 104b7b6a941Sreyk { 105b7b6a941Sreyk if (cs->cs_name == NULL) 106b7b6a941Sreyk return (0); 107b7b6a941Sreyk 108b7b6a941Sreyk if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) { 109b7b6a941Sreyk log_warn("%s: listen", __func__); 110b7b6a941Sreyk return (-1); 111b7b6a941Sreyk } 112b7b6a941Sreyk 113b7b6a941Sreyk event_set(&cs->cs_ev, cs->cs_fd, EV_READ, 114b7b6a941Sreyk control_accept, cs); 115b7b6a941Sreyk event_add(&cs->cs_ev, NULL); 116b7b6a941Sreyk evtimer_set(&cs->cs_evt, control_accept, cs); 117b7b6a941Sreyk 118b7b6a941Sreyk return (0); 119b7b6a941Sreyk } 120b7b6a941Sreyk 121b7b6a941Sreyk void 122b7b6a941Sreyk control_cleanup(struct control_sock *cs) 123b7b6a941Sreyk { 124b7b6a941Sreyk if (cs->cs_name == NULL) 125b7b6a941Sreyk return; 126b7b6a941Sreyk event_del(&cs->cs_ev); 127b7b6a941Sreyk event_del(&cs->cs_evt); 128b7b6a941Sreyk } 129b7b6a941Sreyk 130b7b6a941Sreyk void 131b7b6a941Sreyk control_accept(int listenfd, short event, void *arg) 132b7b6a941Sreyk { 133b7b6a941Sreyk int connfd; 134b7b6a941Sreyk socklen_t len; 135b7b6a941Sreyk struct sockaddr_un sun; 136b7b6a941Sreyk struct ctl_conn *c; 137b7b6a941Sreyk struct control_sock *cs = arg; 138b7b6a941Sreyk 139b7b6a941Sreyk event_add(&cs->cs_ev, NULL); 140b7b6a941Sreyk if ((event & EV_TIMEOUT)) 141b7b6a941Sreyk return; 142b7b6a941Sreyk 143b7b6a941Sreyk len = sizeof(sun); 144838637bcSreyk if ((connfd = accept4(listenfd, 145838637bcSreyk (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) { 146b7b6a941Sreyk /* 147b7b6a941Sreyk * Pause accept if we are out of file descriptors, or 148b7b6a941Sreyk * libevent will haunt us here too. 149b7b6a941Sreyk */ 150b7b6a941Sreyk if (errno == ENFILE || errno == EMFILE) { 151b7b6a941Sreyk struct timeval evtpause = { 1, 0 }; 152b7b6a941Sreyk 153b7b6a941Sreyk event_del(&cs->cs_ev); 154b7b6a941Sreyk evtimer_add(&cs->cs_evt, &evtpause); 155b7b6a941Sreyk } else if (errno != EWOULDBLOCK && errno != EINTR && 156b7b6a941Sreyk errno != ECONNABORTED) 157b7b6a941Sreyk log_warn("%s: accept", __func__); 158b7b6a941Sreyk return; 159b7b6a941Sreyk } 160b7b6a941Sreyk 161b7b6a941Sreyk if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { 162b7b6a941Sreyk log_warn("%s: calloc", __func__); 163*6676295fSclaudio close(connfd); 164b7b6a941Sreyk return; 165b7b6a941Sreyk } 166b7b6a941Sreyk 167*6676295fSclaudio if (imsgbuf_init(&c->iev.ibuf, connfd) == -1) { 168*6676295fSclaudio log_warn("%s: imsgbuf_init", __func__); 169*6676295fSclaudio close(connfd); 170*6676295fSclaudio free(c); 171*6676295fSclaudio return; 172*6676295fSclaudio } 173b7b6a941Sreyk c->iev.handler = control_dispatch_imsg; 174b7b6a941Sreyk c->iev.events = EV_READ; 175b7b6a941Sreyk c->iev.data = cs; /* proc.c cheats (reuses the handler) */ 176b7b6a941Sreyk event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, 177b7b6a941Sreyk c->iev.handler, cs); 178b7b6a941Sreyk event_add(&c->iev.ev, NULL); 179b7b6a941Sreyk 180b7b6a941Sreyk TAILQ_INSERT_TAIL(&ctl_conns, c, entry); 181b7b6a941Sreyk } 182b7b6a941Sreyk 183b7b6a941Sreyk struct ctl_conn * 184b7b6a941Sreyk control_connbyfd(int fd) 185b7b6a941Sreyk { 186b7b6a941Sreyk struct ctl_conn *c; 187b7b6a941Sreyk 1884ff7cad5Skrw TAILQ_FOREACH(c, &ctl_conns, entry) { 1894ff7cad5Skrw if (c->iev.ibuf.fd == fd) 1904ff7cad5Skrw break; 1914ff7cad5Skrw } 192b7b6a941Sreyk 193b7b6a941Sreyk return (c); 194b7b6a941Sreyk } 195b7b6a941Sreyk 196b7b6a941Sreyk void 197b7b6a941Sreyk control_close(int fd, struct control_sock *cs) 198b7b6a941Sreyk { 199b7b6a941Sreyk struct ctl_conn *c; 200b7b6a941Sreyk 201b7b6a941Sreyk if ((c = control_connbyfd(fd)) == NULL) { 202b7b6a941Sreyk log_warn("%s: fd %d not found", __func__, fd); 203b7b6a941Sreyk return; 204b7b6a941Sreyk } 205b7b6a941Sreyk 2069cbf9e90Sclaudio imsgbuf_clear(&c->iev.ibuf); 207b7b6a941Sreyk TAILQ_REMOVE(&ctl_conns, c, entry); 208b7b6a941Sreyk 209b7b6a941Sreyk event_del(&c->iev.ev); 210b7b6a941Sreyk close(c->iev.ibuf.fd); 211b7b6a941Sreyk 212b7b6a941Sreyk /* Some file descriptors are available again. */ 213b7b6a941Sreyk if (evtimer_pending(&cs->cs_evt, NULL)) { 214b7b6a941Sreyk evtimer_del(&cs->cs_evt); 215b7b6a941Sreyk event_add(&cs->cs_ev, NULL); 216b7b6a941Sreyk } 217b7b6a941Sreyk 218b7b6a941Sreyk free(c); 219b7b6a941Sreyk } 220b7b6a941Sreyk 221b7b6a941Sreyk void 222b7b6a941Sreyk control_dispatch_imsg(int fd, short event, void *arg) 223b7b6a941Sreyk { 224b7b6a941Sreyk struct control_sock *cs = arg; 225b7b6a941Sreyk struct ctl_conn *c; 226b7b6a941Sreyk struct imsg imsg; 227b7b6a941Sreyk int n; 228b7b6a941Sreyk int verbose; 229b7b6a941Sreyk struct httpd *env = cs->cs_env; 230b7b6a941Sreyk 231b7b6a941Sreyk if ((c = control_connbyfd(fd)) == NULL) { 232b7b6a941Sreyk log_warn("%s: fd %d not found", __func__, fd); 233b7b6a941Sreyk return; 234b7b6a941Sreyk } 235b7b6a941Sreyk 236b7b6a941Sreyk if (event & EV_READ) { 237668e5ba9Sclaudio if (imsgbuf_read(&c->iev.ibuf) != 1) { 238b7b6a941Sreyk control_close(fd, cs); 239b7b6a941Sreyk return; 240b7b6a941Sreyk } 241b7b6a941Sreyk } 242b7b6a941Sreyk 243b7b6a941Sreyk if (event & EV_WRITE) { 244dd7efffeSclaudio if (imsgbuf_write(&c->iev.ibuf) == -1) { 245b7b6a941Sreyk control_close(fd, cs); 246b7b6a941Sreyk return; 247b7b6a941Sreyk } 248b7b6a941Sreyk } 249b7b6a941Sreyk 250b7b6a941Sreyk for (;;) { 251b7b6a941Sreyk if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { 252b7b6a941Sreyk control_close(fd, cs); 253b7b6a941Sreyk return; 254b7b6a941Sreyk } 255b7b6a941Sreyk 256b7b6a941Sreyk if (n == 0) 257b7b6a941Sreyk break; 258b7b6a941Sreyk 259b7b6a941Sreyk if (c->waiting) { 260b7b6a941Sreyk log_debug("%s: unexpected imsg %d", 261b7b6a941Sreyk __func__, imsg.hdr.type); 262b7b6a941Sreyk imsg_free(&imsg); 263b7b6a941Sreyk control_close(fd, cs); 264b7b6a941Sreyk return; 265b7b6a941Sreyk } 266b7b6a941Sreyk 267b7b6a941Sreyk switch (imsg.hdr.type) { 268b7b6a941Sreyk case IMSG_CTL_SHUTDOWN: 269b7b6a941Sreyk case IMSG_CTL_RELOAD: 270844c3615Sreyk case IMSG_CTL_REOPEN: 271b7b6a941Sreyk proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1); 272b7b6a941Sreyk break; 273b7b6a941Sreyk case IMSG_CTL_NOTIFY: 274b7b6a941Sreyk if (c->flags & CTL_CONN_NOTIFY) { 275b7b6a941Sreyk log_debug("%s: " 276b7b6a941Sreyk "client requested notify more than once", 277b7b6a941Sreyk __func__); 278b7b6a941Sreyk imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 2798ea811cdSreyk 0, env->sc_ps->ps_instance + 1, -1, 2808ea811cdSreyk NULL, 0); 281b7b6a941Sreyk break; 282b7b6a941Sreyk } 283b7b6a941Sreyk c->flags |= CTL_CONN_NOTIFY; 284b7b6a941Sreyk break; 285b7b6a941Sreyk case IMSG_CTL_VERBOSE: 286b7b6a941Sreyk IMSG_SIZE_CHECK(&imsg, &verbose); 287b7b6a941Sreyk 288b7b6a941Sreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 289b7b6a941Sreyk 290b7b6a941Sreyk proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1); 291b7b6a941Sreyk proc_forward_imsg(env->sc_ps, &imsg, PROC_SERVER, -1); 292b7b6a941Sreyk 293b7b6a941Sreyk memcpy(imsg.data, &verbose, sizeof(verbose)); 294b607ef5aSrzalamena control_imsg_forward(env->sc_ps, &imsg); 295871fc12cSreyk log_setverbose(verbose); 296b7b6a941Sreyk break; 297b7b6a941Sreyk default: 298b7b6a941Sreyk log_debug("%s: error handling imsg %d", 299b7b6a941Sreyk __func__, imsg.hdr.type); 300b7b6a941Sreyk break; 301b7b6a941Sreyk } 302b7b6a941Sreyk imsg_free(&imsg); 303b7b6a941Sreyk } 304b7b6a941Sreyk 305b7b6a941Sreyk imsg_event_add(&c->iev); 306b7b6a941Sreyk } 307b7b6a941Sreyk 308b7b6a941Sreyk void 309b607ef5aSrzalamena control_imsg_forward(struct privsep *ps, struct imsg *imsg) 310b7b6a941Sreyk { 311b7b6a941Sreyk struct ctl_conn *c; 312b7b6a941Sreyk 313b7b6a941Sreyk TAILQ_FOREACH(c, &ctl_conns, entry) 314b7b6a941Sreyk if (c->flags & CTL_CONN_NOTIFY) 315b7b6a941Sreyk imsg_compose_event(&c->iev, imsg->hdr.type, 316b607ef5aSrzalamena 0, ps->ps_instance + 1, -1, imsg->data, 317b7b6a941Sreyk imsg->hdr.len - IMSG_HEADER_SIZE); 318b7b6a941Sreyk } 319