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