xref: /openbsd-src/usr.sbin/relayd/control.c (revision 6676295ff107abd6850f48ae428e19963f4b1dc5)
1*6676295fSclaudio /*	$OpenBSD: control.c,v 1.64 2024/11/21 13:38:45 claudio Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
4feb9ff76Sreyk  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19feb9ff76Sreyk #include <sys/queue.h>
20feb9ff76Sreyk #include <sys/stat.h>
21feb9ff76Sreyk #include <sys/socket.h>
22f04ff968Sreyk #include <sys/time.h>
23feb9ff76Sreyk #include <sys/un.h>
240ca734d7Sreyk 
25feb9ff76Sreyk #include <errno.h>
26feb9ff76Sreyk #include <event.h>
27feb9ff76Sreyk #include <fcntl.h>
28feb9ff76Sreyk #include <stdlib.h>
29feb9ff76Sreyk #include <string.h>
30feb9ff76Sreyk #include <unistd.h>
31f04ff968Sreyk #include <imsg.h>
32e8fb3979Spyr 
33748ceb64Sreyk #include "relayd.h"
34feb9ff76Sreyk 
35feb9ff76Sreyk #define	CONTROL_BACKLOG	5
36feb9ff76Sreyk 
37c5fa57f5Sdv struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
38feb9ff76Sreyk 
390325c666Sreyk void		 control_accept(int, short, void *);
406b61fee8Sderaadt void		 control_close(int, struct control_sock *);
41feb9ff76Sreyk 
42feb9ff76Sreyk int
430325c666Sreyk control_init(struct privsep *ps, struct control_sock *cs)
44feb9ff76Sreyk {
450325c666Sreyk 	struct relayd		*env = ps->ps_env;
46feb9ff76Sreyk 	struct sockaddr_un	 sun;
47feb9ff76Sreyk 	int			 fd;
480325c666Sreyk 	mode_t			 old_umask, mode;
490325c666Sreyk 
500325c666Sreyk 	if (cs->cs_name == NULL)
510325c666Sreyk 		return (0);
52feb9ff76Sreyk 
53b045ffeeSreyk 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
5485a8c65fSreyk 		log_warn("%s: socket", __func__);
55feb9ff76Sreyk 		return (-1);
56feb9ff76Sreyk 	}
57feb9ff76Sreyk 
58feb9ff76Sreyk 	sun.sun_family = AF_UNIX;
590325c666Sreyk 	if (strlcpy(sun.sun_path, cs->cs_name,
60a82f8bfbSreyk 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
610325c666Sreyk 		log_warn("%s: %s name too long", __func__, cs->cs_name);
62a82f8bfbSreyk 		close(fd);
63a82f8bfbSreyk 		return (-1);
64a82f8bfbSreyk 	}
65feb9ff76Sreyk 
660325c666Sreyk 	if (unlink(cs->cs_name) == -1)
67feb9ff76Sreyk 		if (errno != ENOENT) {
680325c666Sreyk 			log_warn("%s: unlink %s", __func__, cs->cs_name);
69feb9ff76Sreyk 			close(fd);
70feb9ff76Sreyk 			return (-1);
71feb9ff76Sreyk 		}
72feb9ff76Sreyk 
730325c666Sreyk 	if (cs->cs_restricted) {
740325c666Sreyk 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
750325c666Sreyk 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
760325c666Sreyk 	} else {
77feb9ff76Sreyk 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
780325c666Sreyk 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
790325c666Sreyk 	}
800325c666Sreyk 
81feb9ff76Sreyk 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
820325c666Sreyk 		log_warn("%s: bind: %s", __func__, cs->cs_name);
83feb9ff76Sreyk 		close(fd);
84a82f8bfbSreyk 		(void)umask(old_umask);
85feb9ff76Sreyk 		return (-1);
86feb9ff76Sreyk 	}
87a82f8bfbSreyk 	(void)umask(old_umask);
88feb9ff76Sreyk 
890325c666Sreyk 	if (chmod(cs->cs_name, mode) == -1) {
9085a8c65fSreyk 		log_warn("%s: chmod", __func__);
91feb9ff76Sreyk 		close(fd);
920325c666Sreyk 		(void)unlink(cs->cs_name);
93feb9ff76Sreyk 		return (-1);
94feb9ff76Sreyk 	}
95feb9ff76Sreyk 
960325c666Sreyk 	cs->cs_fd = fd;
970325c666Sreyk 	cs->cs_env = env;
98feb9ff76Sreyk 
99feb9ff76Sreyk 	return (0);
100feb9ff76Sreyk }
101feb9ff76Sreyk 
102feb9ff76Sreyk int
1030325c666Sreyk control_listen(struct control_sock *cs)
104feb9ff76Sreyk {
1050325c666Sreyk 	if (cs->cs_name == NULL)
1060325c666Sreyk 		return (0);
107feb9ff76Sreyk 
1080325c666Sreyk 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
10985a8c65fSreyk 		log_warn("%s: listen", __func__);
110feb9ff76Sreyk 		return (-1);
111feb9ff76Sreyk 	}
112feb9ff76Sreyk 
1136b61fee8Sderaadt 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
1146b61fee8Sderaadt 	    control_accept, cs);
1150325c666Sreyk 	event_add(&cs->cs_ev, NULL);
1166b61fee8Sderaadt 	evtimer_set(&cs->cs_evt, control_accept, cs);
117feb9ff76Sreyk 
118feb9ff76Sreyk 	return (0);
119feb9ff76Sreyk }
120feb9ff76Sreyk 
121feb9ff76Sreyk void
1220325c666Sreyk control_cleanup(struct control_sock *cs)
123feb9ff76Sreyk {
1240325c666Sreyk 	if (cs->cs_name == NULL)
1250325c666Sreyk 		return;
1266b61fee8Sderaadt 	event_del(&cs->cs_ev);
1276b61fee8Sderaadt 	event_del(&cs->cs_evt);
128feb9ff76Sreyk }
129feb9ff76Sreyk 
130feb9ff76Sreyk void
131feb9ff76Sreyk control_accept(int listenfd, short event, void *arg)
132feb9ff76Sreyk {
133feb9ff76Sreyk 	int			 connfd;
134feb9ff76Sreyk 	socklen_t		 len;
135feb9ff76Sreyk 	struct sockaddr_un	 sun;
136feb9ff76Sreyk 	struct ctl_conn		*c;
1376b61fee8Sderaadt 	struct control_sock	*cs = arg;
1386b61fee8Sderaadt 
1396b61fee8Sderaadt 	event_add(&cs->cs_ev, NULL);
1406b61fee8Sderaadt 	if ((event & EV_TIMEOUT))
1416b61fee8Sderaadt 		return;
142feb9ff76Sreyk 
143feb9ff76Sreyk 	len = sizeof(sun);
144b045ffeeSreyk 	if ((connfd = accept4(listenfd,
145b045ffeeSreyk 	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
1466b61fee8Sderaadt 		/*
1476b61fee8Sderaadt 		 * Pause accept if we are out of file descriptors, or
1486b61fee8Sderaadt 		 * libevent will haunt us here too.
1496b61fee8Sderaadt 		 */
1506b61fee8Sderaadt 		if (errno == ENFILE || errno == EMFILE) {
1516b61fee8Sderaadt 			struct timeval evtpause = { 1, 0 };
1526b61fee8Sderaadt 
1536b61fee8Sderaadt 			event_del(&cs->cs_ev);
1546b61fee8Sderaadt 			evtimer_add(&cs->cs_evt, &evtpause);
15562e3c252Sderaadt 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
15662e3c252Sderaadt 		    errno != ECONNABORTED)
15785a8c65fSreyk 			log_warn("%s: accept", __func__);
158feb9ff76Sreyk 		return;
159feb9ff76Sreyk 	}
160feb9ff76Sreyk 
161e8494736Sreyk 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
16285a8c65fSreyk 		log_warn("%s: calloc", __func__);
163*6676295fSclaudio 		close(connfd);
164feb9ff76Sreyk 		return;
165feb9ff76Sreyk 	}
166feb9ff76Sreyk 
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 	}
173*6676295fSclaudio 
174f07d0e3bSpyr 	c->iev.handler = control_dispatch_imsg;
175f07d0e3bSpyr 	c->iev.events = EV_READ;
1763cc2d6a3Sderaadt 	c->iev.data = cs;	/* proc.c cheats (reuses the handler) */
177f07d0e3bSpyr 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
1786b61fee8Sderaadt 	    c->iev.handler, cs);
179f07d0e3bSpyr 	event_add(&c->iev.ev, NULL);
180feb9ff76Sreyk 
181feb9ff76Sreyk 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
182feb9ff76Sreyk }
183feb9ff76Sreyk 
184feb9ff76Sreyk struct ctl_conn *
185feb9ff76Sreyk control_connbyfd(int fd)
186feb9ff76Sreyk {
187feb9ff76Sreyk 	struct ctl_conn	*c;
188feb9ff76Sreyk 
1894ff7cad5Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
1904ff7cad5Skrw 		if (c->iev.ibuf.fd == fd)
1914ff7cad5Skrw 			break;
1924ff7cad5Skrw 	}
193feb9ff76Sreyk 
194feb9ff76Sreyk 	return (c);
195feb9ff76Sreyk }
196feb9ff76Sreyk 
197feb9ff76Sreyk void
1986b61fee8Sderaadt control_close(int fd, struct control_sock *cs)
199feb9ff76Sreyk {
200feb9ff76Sreyk 	struct ctl_conn	*c;
201feb9ff76Sreyk 
202bca0e58eSclaudio 	if ((c = control_connbyfd(fd)) == NULL) {
20385a8c65fSreyk 		log_warn("%s: fd %d not found", __func__, fd);
204bca0e58eSclaudio 		return;
205bca0e58eSclaudio 	}
206feb9ff76Sreyk 
2079cbf9e90Sclaudio 	imsgbuf_clear(&c->iev.ibuf);
208feb9ff76Sreyk 	TAILQ_REMOVE(&ctl_conns, c, entry);
209feb9ff76Sreyk 
210f07d0e3bSpyr 	event_del(&c->iev.ev);
211f07d0e3bSpyr 	close(c->iev.ibuf.fd);
2126b61fee8Sderaadt 
2136b61fee8Sderaadt 	/* Some file descriptors are available again. */
2146b61fee8Sderaadt 	if (evtimer_pending(&cs->cs_evt, NULL)) {
2156b61fee8Sderaadt 		evtimer_del(&cs->cs_evt);
2166b61fee8Sderaadt 		event_add(&cs->cs_ev, NULL);
2176b61fee8Sderaadt 	}
2186b61fee8Sderaadt 
219feb9ff76Sreyk 	free(c);
220feb9ff76Sreyk }
221feb9ff76Sreyk 
222feb9ff76Sreyk void
223feb9ff76Sreyk control_dispatch_imsg(int fd, short event, void *arg)
224feb9ff76Sreyk {
2253cc2d6a3Sderaadt 	struct control_sock	*cs = arg;
226feb9ff76Sreyk 	struct ctl_conn		*c;
227feb9ff76Sreyk 	struct imsg		 imsg;
228ef1f2334Sreyk 	struct ctl_id		 id;
229feb9ff76Sreyk 	int			 n;
230f579a0f7Sjsg 	int			 verbose;
2316b61fee8Sderaadt 	struct relayd		*env = cs->cs_env;
232c28c61ccSreyk 	struct privsep		*ps = env->sc_ps;
233feb9ff76Sreyk 
234feb9ff76Sreyk 	if ((c = control_connbyfd(fd)) == NULL) {
23585a8c65fSreyk 		log_warn("%s: fd %d not found", __func__, fd);
236feb9ff76Sreyk 		return;
237feb9ff76Sreyk 	}
238feb9ff76Sreyk 
2392668107aSreyk 	if (event & EV_READ) {
240668e5ba9Sclaudio 		if (imsgbuf_read(&c->iev.ibuf) != 1) {
2416b61fee8Sderaadt 			control_close(fd, cs);
242feb9ff76Sreyk 			return;
243feb9ff76Sreyk 		}
2442668107aSreyk 	}
2452668107aSreyk 
2462668107aSreyk 	if (event & EV_WRITE) {
247dd7efffeSclaudio 		if (imsgbuf_write(&c->iev.ibuf) == -1) {
2486b61fee8Sderaadt 			control_close(fd, cs);
249feb9ff76Sreyk 			return;
250feb9ff76Sreyk 		}
251feb9ff76Sreyk 	}
252feb9ff76Sreyk 
253feb9ff76Sreyk 	for (;;) {
254f07d0e3bSpyr 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
2556b61fee8Sderaadt 			control_close(fd, cs);
256feb9ff76Sreyk 			return;
257feb9ff76Sreyk 		}
258feb9ff76Sreyk 
259feb9ff76Sreyk 		if (n == 0)
260feb9ff76Sreyk 			break;
261feb9ff76Sreyk 
26286b74329Sreyk 		if (c->waiting) {
26386b74329Sreyk 			log_debug("%s: unexpected imsg %d",
26486b74329Sreyk 			    __func__, imsg.hdr.type);
26586b74329Sreyk 			imsg_free(&imsg);
2666b61fee8Sderaadt 			control_close(fd, cs);
26786b74329Sreyk 			return;
26886b74329Sreyk 		}
26986b74329Sreyk 
270feb9ff76Sreyk 		switch (imsg.hdr.type) {
271feb9ff76Sreyk 		case IMSG_CTL_SHOW_SUM:
272feb9ff76Sreyk 			show(c);
273feb9ff76Sreyk 			break;
2749d421a7aSreyk 		case IMSG_CTL_SESSION:
2759d421a7aSreyk 			show_sessions(c);
2769d421a7aSreyk 			break;
2779591a9f7Spyr 		case IMSG_CTL_RDR_DISABLE:
278feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
279feb9ff76Sreyk 				fatalx("invalid imsg header len");
280feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
2819591a9f7Spyr 			if (disable_rdr(c, &id))
282f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
283c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
2841569a65fSpyr 			else {
2851569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
286c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
287f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
288c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
2891569a65fSpyr 			}
290feb9ff76Sreyk 			break;
2919591a9f7Spyr 		case IMSG_CTL_RDR_ENABLE:
292feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
293feb9ff76Sreyk 				fatalx("invalid imsg header len");
294feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
2959591a9f7Spyr 			if (enable_rdr(c, &id))
296f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
297c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
2981569a65fSpyr 			else {
2991569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
300c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
301f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
302c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3031569a65fSpyr 			}
304feb9ff76Sreyk 			break;
305feb9ff76Sreyk 		case IMSG_CTL_TABLE_DISABLE:
306feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
307feb9ff76Sreyk 				fatalx("invalid imsg header len");
308feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
309ef1f2334Sreyk 			if (disable_table(c, &id))
310f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
311c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3121569a65fSpyr 			else {
3131569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
314c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
315f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
316c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3171569a65fSpyr 			}
318feb9ff76Sreyk 			break;
319feb9ff76Sreyk 		case IMSG_CTL_TABLE_ENABLE:
320feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
321feb9ff76Sreyk 				fatalx("invalid imsg header len");
322feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
323ef1f2334Sreyk 			if (enable_table(c, &id))
324f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
325c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3261569a65fSpyr 			else {
3271569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
328c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
329f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
330c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3311569a65fSpyr 			}
332feb9ff76Sreyk 			break;
333feb9ff76Sreyk 		case IMSG_CTL_HOST_DISABLE:
334feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
335feb9ff76Sreyk 				fatalx("invalid imsg header len");
336feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
337c723f8edSreyk 			if (disable_host(c, &id, NULL))
338f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
339c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3401569a65fSpyr 			else {
3411569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
342c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
343f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
344c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3451569a65fSpyr 			}
346feb9ff76Sreyk 			break;
347feb9ff76Sreyk 		case IMSG_CTL_HOST_ENABLE:
348feb9ff76Sreyk 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
349feb9ff76Sreyk 				fatalx("invalid imsg header len");
350feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
351c723f8edSreyk 			if (enable_host(c, &id, NULL))
352f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
353c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3541569a65fSpyr 			else {
3551569a65fSpyr 				memcpy(imsg.data, &id, sizeof(id));
356c28c61ccSreyk 				control_imsg_forward(ps, &imsg);
357f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
358c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3591569a65fSpyr 			}
360feb9ff76Sreyk 			break;
361feb9ff76Sreyk 		case IMSG_CTL_SHUTDOWN:
362a2195becSreyk 		case IMSG_CTL_RELOAD:
363a2195becSreyk 			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
364feb9ff76Sreyk 			break;
365cd65ce7bSpyr 		case IMSG_CTL_POLL:
3662166201eSreyk 			proc_compose(env->sc_ps, PROC_HCE,
3672166201eSreyk 			    IMSG_CTL_POLL, NULL, 0);
368f07d0e3bSpyr 			imsg_compose_event(&c->iev, IMSG_CTL_OK,
369c28c61ccSreyk 			    0, ps->ps_instance + 1, -1, NULL, 0);
370cd65ce7bSpyr 			break;
3711569a65fSpyr 		case IMSG_CTL_NOTIFY:
3721569a65fSpyr 			if (c->flags & CTL_CONN_NOTIFY) {
37385a8c65fSreyk 				log_debug("%s: "
37485a8c65fSreyk 				    "client requested notify more than once",
37585a8c65fSreyk 				    __func__);
376f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
377c28c61ccSreyk 				    0, ps->ps_instance + 1, -1, NULL, 0);
3781569a65fSpyr 				break;
3791569a65fSpyr 			}
3801569a65fSpyr 			c->flags |= CTL_CONN_NOTIFY;
3811569a65fSpyr 			break;
3820325c666Sreyk 		case IMSG_CTL_VERBOSE:
3830325c666Sreyk 			IMSG_SIZE_CHECK(&imsg, &verbose);
384f579a0f7Sjsg 
385f579a0f7Sjsg 			memcpy(&verbose, imsg.data, sizeof(verbose));
386f579a0f7Sjsg 
387a2195becSreyk 			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
388a2195becSreyk 			proc_forward_imsg(env->sc_ps, &imsg, PROC_HCE, -1);
3890325c666Sreyk 			proc_forward_imsg(env->sc_ps, &imsg, PROC_RELAY, -1);
3900325c666Sreyk 
391f579a0f7Sjsg 			memcpy(imsg.data, &verbose, sizeof(verbose));
392c28c61ccSreyk 			control_imsg_forward(ps, &imsg);
393871fc12cSreyk 			log_setverbose(verbose);
394f579a0f7Sjsg 			break;
395feb9ff76Sreyk 		default:
39685a8c65fSreyk 			log_debug("%s: error handling imsg %d",
39785a8c65fSreyk 			    __func__, imsg.hdr.type);
398feb9ff76Sreyk 			break;
399feb9ff76Sreyk 		}
400feb9ff76Sreyk 		imsg_free(&imsg);
401feb9ff76Sreyk 	}
402feb9ff76Sreyk 
403f07d0e3bSpyr 	imsg_event_add(&c->iev);
404feb9ff76Sreyk }
405feb9ff76Sreyk 
406feb9ff76Sreyk void
407c28c61ccSreyk control_imsg_forward(struct privsep *ps, struct imsg *imsg)
4081569a65fSpyr {
4091569a65fSpyr 	struct ctl_conn *c;
4101569a65fSpyr 
4111569a65fSpyr 	TAILQ_FOREACH(c, &ctl_conns, entry)
4121569a65fSpyr 		if (c->flags & CTL_CONN_NOTIFY)
413f07d0e3bSpyr 			imsg_compose_event(&c->iev, imsg->hdr.type,
414c28c61ccSreyk 			    0, ps->ps_instance + 1, -1, imsg->data,
415069bf5e4Spyr 			    imsg->hdr.len - IMSG_HEADER_SIZE);
4161569a65fSpyr }
417