xref: /openbsd-src/usr.sbin/vmd/control.c (revision 0a9d031fce78c0ebce0995b311938b1c87b1e208)
1*0a9d031fSclaudio /*	$OpenBSD: control.c,v 1.49 2024/11/21 13:39:34 claudio Exp $	*/
2af96af6cSreyk 
3af96af6cSreyk /*
4af96af6cSreyk  * Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org>
5af96af6cSreyk  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6af96af6cSreyk  *
7af96af6cSreyk  * Permission to use, copy, modify, and distribute this software for any
8af96af6cSreyk  * purpose with or without fee is hereby granted, provided that the above
9af96af6cSreyk  * copyright notice and this permission notice appear in all copies.
10af96af6cSreyk  *
11af96af6cSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12af96af6cSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13af96af6cSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14af96af6cSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15af96af6cSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16af96af6cSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17af96af6cSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18af96af6cSreyk  */
19af96af6cSreyk 
2024fb43d0Sderaadt #include <sys/types.h>
21af96af6cSreyk #include <sys/queue.h>
22af96af6cSreyk #include <sys/stat.h>
23af96af6cSreyk #include <sys/socket.h>
24af96af6cSreyk #include <sys/un.h>
25af96af6cSreyk 
26af96af6cSreyk #include <errno.h>
27af96af6cSreyk #include <event.h>
28af96af6cSreyk #include <stdlib.h>
29af96af6cSreyk #include <string.h>
30af96af6cSreyk #include <unistd.h>
31af96af6cSreyk 
32af96af6cSreyk #include "proc.h"
33af96af6cSreyk #include "vmd.h"
34af96af6cSreyk 
35af96af6cSreyk #define	CONTROL_BACKLOG	5
36af96af6cSreyk 
37c5fa57f5Sdv struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
38af96af6cSreyk 
39e73ceaacSdv struct ctl_notify {
40e73ceaacSdv 	int			ctl_fd;
41e73ceaacSdv 	uint32_t		ctl_vmid;
42e73ceaacSdv 	TAILQ_ENTRY(ctl_notify)	entry;
43e73ceaacSdv };
44e73ceaacSdv TAILQ_HEAD(ctl_notify_q, ctl_notify) ctl_notify_q =
45e73ceaacSdv 	TAILQ_HEAD_INITIALIZER(ctl_notify_q);
46af96af6cSreyk void
47af96af6cSreyk 	 control_accept(int, short, void *);
48af96af6cSreyk struct ctl_conn
49af96af6cSreyk 	*control_connbyfd(int);
50af96af6cSreyk void	 control_close(int, struct control_sock *);
51af96af6cSreyk void	 control_dispatch_imsg(int, short, void *);
52ee5f6d5eSreyk int	 control_dispatch_vmd(int, struct privsep_proc *, struct imsg *);
53af96af6cSreyk void	 control_run(struct privsep *, struct privsep_proc *, void *);
54af96af6cSreyk 
55af96af6cSreyk static struct privsep_proc procs[] = {
56ee5f6d5eSreyk 	{ "parent",	PROC_PARENT,	control_dispatch_vmd }
57af96af6cSreyk };
58af96af6cSreyk 
59bcc679a1Sreyk void
60af96af6cSreyk control(struct privsep *ps, struct privsep_proc *p)
61af96af6cSreyk {
62bcc679a1Sreyk 	proc_run(ps, p, procs, nitems(procs), control_run, NULL);
63af96af6cSreyk }
64af96af6cSreyk 
65af96af6cSreyk void
66af96af6cSreyk control_run(struct privsep *ps, struct privsep_proc *p, void *arg)
67af96af6cSreyk {
68af96af6cSreyk 	/*
69af96af6cSreyk 	 * pledge in the control process:
70af96af6cSreyk 	 * stdio - for malloc and basic I/O including events.
71af96af6cSreyk 	 * unix - for the control socket.
72bcc679a1Sreyk 	 * recvfd - for the proc fd exchange.
73eed20f3bSpd 	 * sendfd - for send and receive.
74af96af6cSreyk 	 */
7513184223Smestre 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
76af96af6cSreyk 		fatal("pledge");
77af96af6cSreyk }
78af96af6cSreyk 
79af96af6cSreyk int
80ee5f6d5eSreyk control_dispatch_vmd(int fd, struct privsep_proc *p, struct imsg *imsg)
81af96af6cSreyk {
82af96af6cSreyk 	struct ctl_conn		*c;
83e73ceaacSdv 	struct ctl_notify	*notify = NULL, *notify_next;
84c48cfcf4Sreyk 	struct privsep		*ps = p->p_ps;
85e73ceaacSdv 	struct vmop_result	 vmr;
86e73ceaacSdv 	int			 waiting = 0;
87af96af6cSreyk 
88af96af6cSreyk 	switch (imsg->hdr.type) {
89af96af6cSreyk 	case IMSG_VMDOP_START_VM_RESPONSE:
9052e954a3Spd 	case IMSG_VMDOP_PAUSE_VM_RESPONSE:
91eed20f3bSpd 	case IMSG_VMDOP_SEND_VM_RESPONSE:
9252e954a3Spd 	case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
93af96af6cSreyk 	case IMSG_VMDOP_GET_INFO_VM_DATA:
94af96af6cSreyk 	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
95d1866b5cSreyk 	case IMSG_CTL_FAIL:
96d1866b5cSreyk 	case IMSG_CTL_OK:
97e73ceaacSdv 		/* Provide basic response back to a specific control client */
98bcc679a1Sreyk 		if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) {
990d07b4d3Sreyk 			log_warnx("%s: lost control connection: fd %d",
100bcc679a1Sreyk 			    __func__, imsg->hdr.peerid);
1010d07b4d3Sreyk 			return (0);
102bcc679a1Sreyk 		}
103af96af6cSreyk 		imsg_compose_event(&c->iev, imsg->hdr.type,
104e73ceaacSdv 		    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
105e73ceaacSdv 		break;
106e73ceaacSdv 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
107e73ceaacSdv 		IMSG_SIZE_CHECK(imsg, &vmr);
108e73ceaacSdv 		memcpy(&vmr, imsg->data, sizeof(vmr));
109e73ceaacSdv 
110e73ceaacSdv 		if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) {
111e73ceaacSdv 			log_warnx("%s: lost control connection: fd %d",
112e73ceaacSdv 			    __func__, imsg->hdr.peerid);
113e73ceaacSdv 			return (0);
114e73ceaacSdv 		}
115e73ceaacSdv 
116e73ceaacSdv 		TAILQ_FOREACH(notify, &ctl_notify_q, entry) {
117e73ceaacSdv 			if (notify->ctl_fd == (int) imsg->hdr.peerid) {
118e73ceaacSdv 				/*
119e73ceaacSdv 				 * Update if waiting by vm name. This is only
120e73ceaacSdv 				 * supported when stopping a single vm. If
121e73ceaacSdv 				 * stopping all vms, vmctl(8) sends the request
122e73ceaacSdv 				 * using the vmid.
123e73ceaacSdv 				 */
124e73ceaacSdv 				if (notify->ctl_vmid < 1)
125e73ceaacSdv 					notify->ctl_vmid = vmr.vmr_id;
126e73ceaacSdv 				waiting = 1;
127e73ceaacSdv 				break;
128e73ceaacSdv 			}
129e73ceaacSdv 		}
130e73ceaacSdv 
131e73ceaacSdv 		/* An error needs to be relayed to the client immediately */
132e73ceaacSdv 		if (!waiting || vmr.vmr_result) {
133e73ceaacSdv 			imsg_compose_event(&c->iev, imsg->hdr.type,
134e73ceaacSdv 			    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
135e73ceaacSdv 
136e73ceaacSdv 			if (notify) {
137e73ceaacSdv 				TAILQ_REMOVE(&ctl_notify_q, notify, entry);
138e73ceaacSdv 				free(notify);
139e73ceaacSdv 			}
140e73ceaacSdv 		}
141e73ceaacSdv 		break;
142e73ceaacSdv 	case IMSG_VMDOP_TERMINATE_VM_EVENT:
143e73ceaacSdv 		/* Notify any waiting clients that a VM terminated */
144e73ceaacSdv 		IMSG_SIZE_CHECK(imsg, &vmr);
145e73ceaacSdv 		memcpy(&vmr, imsg->data, sizeof(vmr));
146e73ceaacSdv 
147e73ceaacSdv 		TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) {
148e73ceaacSdv 			if (notify->ctl_vmid != vmr.vmr_id)
149e73ceaacSdv 				continue;
150e73ceaacSdv 			if ((c = control_connbyfd(notify->ctl_fd)) != NULL) {
151540e29f5Sdv 				/* Forward to the vmctl(8) client */
152540e29f5Sdv 				imsg_compose_event(&c->iev, imsg->hdr.type,
153e73ceaacSdv 				    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
154e73ceaacSdv 				TAILQ_REMOVE(&ctl_notify_q, notify, entry);
155e73ceaacSdv 				free(notify);
156e73ceaacSdv 			}
157e73ceaacSdv 		}
158af96af6cSreyk 		break;
159c48cfcf4Sreyk 	case IMSG_VMDOP_CONFIG:
160c48cfcf4Sreyk 		config_getconfig(ps->ps_env, imsg);
1616cfffd57Sreyk 		proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
162c48cfcf4Sreyk 		break;
163c48cfcf4Sreyk 	case IMSG_CTL_RESET:
164c48cfcf4Sreyk 		config_getreset(ps->ps_env, imsg);
165c48cfcf4Sreyk 		break;
166af96af6cSreyk 	default:
167af96af6cSreyk 		return (-1);
168af96af6cSreyk 	}
169af96af6cSreyk 
170af96af6cSreyk 	return (0);
171af96af6cSreyk }
172af96af6cSreyk 
173af96af6cSreyk int
174af96af6cSreyk control_init(struct privsep *ps, struct control_sock *cs)
175af96af6cSreyk {
176af96af6cSreyk 	struct sockaddr_un	 sun;
177af96af6cSreyk 	int			 fd;
178af96af6cSreyk 	mode_t			 old_umask, mode;
179af96af6cSreyk 
180af96af6cSreyk 	if (cs->cs_name == NULL)
181af96af6cSreyk 		return (0);
182af96af6cSreyk 
183af96af6cSreyk 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
184af96af6cSreyk 		log_warn("%s: socket", __func__);
185af96af6cSreyk 		return (-1);
186af96af6cSreyk 	}
187af96af6cSreyk 
188af96af6cSreyk 	sun.sun_family = AF_UNIX;
189af96af6cSreyk 	if (strlcpy(sun.sun_path, cs->cs_name,
190af96af6cSreyk 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
191af96af6cSreyk 		log_warn("%s: %s name too long", __func__, cs->cs_name);
192af96af6cSreyk 		close(fd);
193af96af6cSreyk 		return (-1);
194af96af6cSreyk 	}
195af96af6cSreyk 
196af96af6cSreyk 	if (unlink(cs->cs_name) == -1)
197af96af6cSreyk 		if (errno != ENOENT) {
198af96af6cSreyk 			log_warn("%s: unlink %s", __func__, cs->cs_name);
199af96af6cSreyk 			close(fd);
200af96af6cSreyk 			return (-1);
201af96af6cSreyk 		}
202af96af6cSreyk 
203af96af6cSreyk 	if (cs->cs_restricted) {
204af96af6cSreyk 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
205af96af6cSreyk 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
206af96af6cSreyk 	} else {
207af96af6cSreyk 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
208af96af6cSreyk 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
209af96af6cSreyk 	}
210af96af6cSreyk 
211af96af6cSreyk 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
212af96af6cSreyk 		log_warn("%s: bind: %s", __func__, cs->cs_name);
213af96af6cSreyk 		close(fd);
214af96af6cSreyk 		(void)umask(old_umask);
215af96af6cSreyk 		return (-1);
216af96af6cSreyk 	}
217af96af6cSreyk 	(void)umask(old_umask);
218af96af6cSreyk 
219af96af6cSreyk 	if (chmod(cs->cs_name, mode) == -1) {
220af96af6cSreyk 		log_warn("%s: chmod", __func__);
221af96af6cSreyk 		close(fd);
222af96af6cSreyk 		(void)unlink(cs->cs_name);
223af96af6cSreyk 		return (-1);
224af96af6cSreyk 	}
225af96af6cSreyk 
226af96af6cSreyk 	cs->cs_fd = fd;
227af96af6cSreyk 	cs->cs_env = ps;
228af96af6cSreyk 
2296cfffd57Sreyk 	proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
2306cfffd57Sreyk 
2316cfffd57Sreyk 	return (0);
2326cfffd57Sreyk }
2336cfffd57Sreyk 
2346cfffd57Sreyk int
2356cfffd57Sreyk control_reset(struct control_sock *cs)
2366cfffd57Sreyk {
2376cfffd57Sreyk 	/* Updating owner of the control socket */
2386cfffd57Sreyk 	if (chown(cs->cs_name, cs->cs_uid, cs->cs_gid) == -1)
2396cfffd57Sreyk 		return (-1);
2406cfffd57Sreyk 
241af96af6cSreyk 	return (0);
242af96af6cSreyk }
243af96af6cSreyk 
244af96af6cSreyk int
245af96af6cSreyk control_listen(struct control_sock *cs)
246af96af6cSreyk {
247af96af6cSreyk 	if (cs->cs_name == NULL)
248af96af6cSreyk 		return (0);
249af96af6cSreyk 
250af96af6cSreyk 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
251af96af6cSreyk 		log_warn("%s: listen", __func__);
252af96af6cSreyk 		return (-1);
253af96af6cSreyk 	}
254af96af6cSreyk 
255af96af6cSreyk 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
256af96af6cSreyk 	    control_accept, cs);
257af96af6cSreyk 	event_add(&cs->cs_ev, NULL);
258af96af6cSreyk 	evtimer_set(&cs->cs_evt, control_accept, cs);
259af96af6cSreyk 
260af96af6cSreyk 	return (0);
261af96af6cSreyk }
262af96af6cSreyk 
263af96af6cSreyk void
264af96af6cSreyk control_accept(int listenfd, short event, void *arg)
265af96af6cSreyk {
266af96af6cSreyk 	struct control_sock	*cs = arg;
267af96af6cSreyk 	int			 connfd;
268af96af6cSreyk 	socklen_t		 len;
269af96af6cSreyk 	struct sockaddr_un	 sun;
270af96af6cSreyk 	struct ctl_conn		*c;
271af96af6cSreyk 
272af96af6cSreyk 	event_add(&cs->cs_ev, NULL);
273af96af6cSreyk 	if ((event & EV_TIMEOUT))
274af96af6cSreyk 		return;
275af96af6cSreyk 
276af96af6cSreyk 	len = sizeof(sun);
277af96af6cSreyk 	if ((connfd = accept4(listenfd,
278af96af6cSreyk 	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
279af96af6cSreyk 		/*
280af96af6cSreyk 		 * Pause accept if we are out of file descriptors, or
281af96af6cSreyk 		 * libevent will haunt us here too.
282af96af6cSreyk 		 */
283af96af6cSreyk 		if (errno == ENFILE || errno == EMFILE) {
284af96af6cSreyk 			struct timeval evtpause = { 1, 0 };
285af96af6cSreyk 
286af96af6cSreyk 			event_del(&cs->cs_ev);
287af96af6cSreyk 			evtimer_add(&cs->cs_evt, &evtpause);
288af96af6cSreyk 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
289af96af6cSreyk 		    errno != ECONNABORTED)
290af96af6cSreyk 			log_warn("%s: accept", __func__);
291af96af6cSreyk 		return;
292af96af6cSreyk 	}
293af96af6cSreyk 
294af96af6cSreyk 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
295af96af6cSreyk 		log_warn("%s", __func__);
296af96af6cSreyk 		close(connfd);
297af96af6cSreyk 		return;
298af96af6cSreyk 	}
299af96af6cSreyk 
300091d9ffeSreyk 	if (getsockopt(connfd, SOL_SOCKET, SO_PEERCRED,
301091d9ffeSreyk 	    &c->peercred, &len) != 0) {
302091d9ffeSreyk 		log_warn("%s: failed to get peer credentials", __func__);
303091d9ffeSreyk 		close(connfd);
304091d9ffeSreyk 		free(c);
305091d9ffeSreyk 		return;
306091d9ffeSreyk 	}
307091d9ffeSreyk 
308*0a9d031fSclaudio 	if (imsgbuf_init(&c->iev.ibuf, connfd) == -1) {
309*0a9d031fSclaudio 		log_warn("%s: failed to init imsgbuf", __func__);
310*0a9d031fSclaudio 		close(connfd);
311*0a9d031fSclaudio 		free(c);
312*0a9d031fSclaudio 		return;
313*0a9d031fSclaudio 	}
314*0a9d031fSclaudio 	imsgbuf_allow_fdpass(&c->iev.ibuf);
315af96af6cSreyk 	c->iev.handler = control_dispatch_imsg;
316af96af6cSreyk 	c->iev.events = EV_READ;
317af96af6cSreyk 	c->iev.data = cs;
318af96af6cSreyk 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
319af96af6cSreyk 	    c->iev.handler, c->iev.data);
320af96af6cSreyk 	event_add(&c->iev.ev, NULL);
321af96af6cSreyk 
322af96af6cSreyk 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
323af96af6cSreyk }
324af96af6cSreyk 
325af96af6cSreyk struct ctl_conn *
326af96af6cSreyk control_connbyfd(int fd)
327af96af6cSreyk {
328af96af6cSreyk 	struct ctl_conn	*c;
329af96af6cSreyk 
3304ff7cad5Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
3314ff7cad5Skrw 		if (c->iev.ibuf.fd == fd)
3324ff7cad5Skrw 			break;
3334ff7cad5Skrw 	}
334af96af6cSreyk 
335af96af6cSreyk 	return (c);
336af96af6cSreyk }
337af96af6cSreyk 
338af96af6cSreyk void
339af96af6cSreyk control_close(int fd, struct control_sock *cs)
340af96af6cSreyk {
341af96af6cSreyk 	struct ctl_conn		*c;
342e73ceaacSdv 	struct ctl_notify	*notify, *notify_next;
343af96af6cSreyk 
344af96af6cSreyk 	if ((c = control_connbyfd(fd)) == NULL) {
345af96af6cSreyk 		log_warn("%s: fd %d: not found", __func__, fd);
346af96af6cSreyk 		return;
347af96af6cSreyk 	}
348af96af6cSreyk 
3499cbf9e90Sclaudio 	imsgbuf_clear(&c->iev.ibuf);
350af96af6cSreyk 	TAILQ_REMOVE(&ctl_conns, c, entry);
351af96af6cSreyk 
352e73ceaacSdv 	TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) {
353e73ceaacSdv 		if (notify->ctl_fd == fd) {
354e73ceaacSdv 			TAILQ_REMOVE(&ctl_notify_q, notify, entry);
355e73ceaacSdv 			free(notify);
356e73ceaacSdv 			break;
357e73ceaacSdv 		}
358e73ceaacSdv 	}
359e73ceaacSdv 
360af96af6cSreyk 	event_del(&c->iev.ev);
361af96af6cSreyk 	close(c->iev.ibuf.fd);
362af96af6cSreyk 
363af96af6cSreyk 	/* Some file descriptors are available again. */
364af96af6cSreyk 	if (evtimer_pending(&cs->cs_evt, NULL)) {
365af96af6cSreyk 		evtimer_del(&cs->cs_evt);
366af96af6cSreyk 		event_add(&cs->cs_ev, NULL);
367af96af6cSreyk 	}
368af96af6cSreyk 
369af96af6cSreyk 	free(c);
370af96af6cSreyk }
371af96af6cSreyk 
372af96af6cSreyk void
373af96af6cSreyk control_dispatch_imsg(int fd, short event, void *arg)
374af96af6cSreyk {
375af96af6cSreyk 	struct control_sock		*cs = arg;
376af96af6cSreyk 	struct privsep			*ps = cs->cs_env;
377af96af6cSreyk 	struct ctl_conn			*c;
378af96af6cSreyk 	struct imsg			 imsg;
379bb55a3a2Sreyk 	struct vmop_create_params	 vmc;
380bb55a3a2Sreyk 	struct vmop_id			 vid;
381e73ceaacSdv 	struct ctl_notify		*notify;
382e73ceaacSdv 	int				 n, v, wait = 0, ret = 0;
383af96af6cSreyk 
384af96af6cSreyk 	if ((c = control_connbyfd(fd)) == NULL) {
385af96af6cSreyk 		log_warn("%s: fd %d: not found", __func__, fd);
386af96af6cSreyk 		return;
387af96af6cSreyk 	}
388af96af6cSreyk 
389af96af6cSreyk 	if (event & EV_READ) {
390d12ef5f3Sclaudio 		if (imsgbuf_read(&c->iev.ibuf) != 1) {
391af96af6cSreyk 			control_close(fd, cs);
392af96af6cSreyk 			return;
393af96af6cSreyk 		}
394af96af6cSreyk 	}
395af96af6cSreyk 	if (event & EV_WRITE) {
396dd7efffeSclaudio 		if (imsgbuf_write(&c->iev.ibuf) == -1) {
397af96af6cSreyk 			control_close(fd, cs);
398af96af6cSreyk 			return;
399af96af6cSreyk 		}
400af96af6cSreyk 	}
401af96af6cSreyk 
402af96af6cSreyk 	for (;;) {
403af96af6cSreyk 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
404af96af6cSreyk 			control_close(fd, cs);
405af96af6cSreyk 			return;
406af96af6cSreyk 		}
407af96af6cSreyk 
408af96af6cSreyk 		if (n == 0)
409af96af6cSreyk 			break;
410af96af6cSreyk 
411091d9ffeSreyk 		switch (imsg.hdr.type) {
412008065a5Sreyk 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
41395176482Sclaudio 		case IMSG_VMDOP_WAIT_VM_REQUEST:
414e5d5b350Sreyk 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
415e5d5b350Sreyk 		case IMSG_VMDOP_START_VM_REQUEST:
416bc813cecSpd 		case IMSG_VMDOP_PAUSE_VM:
417bc813cecSpd 		case IMSG_VMDOP_UNPAUSE_VM:
418008065a5Sreyk 			break;
419008065a5Sreyk 		default:
420091d9ffeSreyk 			if (c->peercred.uid != 0) {
421091d9ffeSreyk 				log_warnx("denied request %d from uid %d",
422091d9ffeSreyk 				    imsg.hdr.type, c->peercred.uid);
423091d9ffeSreyk 				ret = EPERM;
424091d9ffeSreyk 				goto fail;
425091d9ffeSreyk 			}
426091d9ffeSreyk 			break;
427091d9ffeSreyk 		}
428091d9ffeSreyk 
429af96af6cSreyk 		switch (imsg.hdr.type) {
430af96af6cSreyk 		case IMSG_CTL_VERBOSE:
431bb55a3a2Sreyk 			if (IMSG_DATA_SIZE(&imsg) < sizeof(v))
432bb55a3a2Sreyk 				goto fail;
433af96af6cSreyk 			memcpy(&v, imsg.data, sizeof(v));
434871fc12cSreyk 			log_setverbose(v);
435af96af6cSreyk 
436b55cbc1dSreyk 			/* FALLTHROUGH */
437eed20f3bSpd 		case IMSG_VMDOP_RECEIVE_VM_REQUEST:
438eed20f3bSpd 		case IMSG_VMDOP_SEND_VM_REQUEST:
439b55cbc1dSreyk 		case IMSG_VMDOP_LOAD:
440b55cbc1dSreyk 		case IMSG_VMDOP_RELOAD:
441b55cbc1dSreyk 		case IMSG_CTL_RESET:
442d1866b5cSreyk 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
44353027660Sclaudio 			    imsg.hdr.type, fd, imsg_get_fd(&imsg),
444d1866b5cSreyk 			    imsg.data, IMSG_DATA_SIZE(&imsg)) == -1)
445d1866b5cSreyk 				goto fail;
446af96af6cSreyk 			break;
447af96af6cSreyk 		case IMSG_VMDOP_START_VM_REQUEST:
448bb55a3a2Sreyk 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vmc))
449bb55a3a2Sreyk 				goto fail;
450e5d5b350Sreyk 			memcpy(&vmc, imsg.data, sizeof(vmc));
451476d73d1Sreyk 			vmc.vmc_owner.uid = c->peercred.uid;
452476d73d1Sreyk 			vmc.vmc_owner.gid = -1;
453e5d5b350Sreyk 
454b848b186Sdv 			/* imsg.fd may contain kernel image fd. */
455f0bbd60cSreyk 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
45653027660Sclaudio 			    imsg.hdr.type, fd, imsg_get_fd(&imsg), &vmc,
457b848b186Sdv 			    sizeof(vmc)) == -1) {
458af96af6cSreyk 				control_close(fd, cs);
459af96af6cSreyk 				return;
460af96af6cSreyk 			}
461af96af6cSreyk 			break;
46295176482Sclaudio 		case IMSG_VMDOP_WAIT_VM_REQUEST:
463e73ceaacSdv 			wait = 1;
464e73ceaacSdv 			/* FALLTHROUGH */
465bb55a3a2Sreyk 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
466bb55a3a2Sreyk 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
467bb55a3a2Sreyk 				goto fail;
468e5d5b350Sreyk 			memcpy(&vid, imsg.data, sizeof(vid));
469e5d5b350Sreyk 			vid.vid_uid = c->peercred.uid;
470e5d5b350Sreyk 
471e73ceaacSdv 			if (wait || vid.vid_flags & VMOP_WAIT) {
472e73ceaacSdv 				vid.vid_flags |= VMOP_WAIT;
473e73ceaacSdv 				notify = calloc(1, sizeof(struct ctl_notify));
474e73ceaacSdv 				if (notify == NULL)
475e73ceaacSdv 					fatal("%s: calloc", __func__);
476e73ceaacSdv 				notify->ctl_vmid = vid.vid_id;
477e73ceaacSdv 				notify->ctl_fd = fd;
478e73ceaacSdv 				TAILQ_INSERT_TAIL(&ctl_notify_q, notify, entry);
479e73ceaacSdv 				log_debug("%s: registered wait for peer %d",
480e73ceaacSdv 				    __func__, fd);
481e73ceaacSdv 			}
482e73ceaacSdv 
483bb55a3a2Sreyk 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
484e5d5b350Sreyk 			    imsg.hdr.type, fd, -1, &vid, sizeof(vid)) == -1) {
485e0994759Smlarkin 				log_debug("%s: proc_compose_imsg failed",
486e0994759Smlarkin 				    __func__);
487bb55a3a2Sreyk 				control_close(fd, cs);
488bb55a3a2Sreyk 				return;
489bb55a3a2Sreyk 			}
490bb55a3a2Sreyk 			break;
491bb55a3a2Sreyk 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
492bb55a3a2Sreyk 			if (IMSG_DATA_SIZE(&imsg) != 0)
493bb55a3a2Sreyk 				goto fail;
494bb55a3a2Sreyk 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
495bb55a3a2Sreyk 			    imsg.hdr.type, fd, -1, NULL, 0) == -1) {
496bb55a3a2Sreyk 				control_close(fd, cs);
497bb55a3a2Sreyk 				return;
498bb55a3a2Sreyk 			}
499bb55a3a2Sreyk 			break;
500bc813cecSpd 		case IMSG_VMDOP_PAUSE_VM:
501bc813cecSpd 		case IMSG_VMDOP_UNPAUSE_VM:
502bc813cecSpd 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
503bc813cecSpd 				goto fail;
504bc813cecSpd 			memcpy(&vid, imsg.data, sizeof(vid));
505bc813cecSpd 			vid.vid_uid = c->peercred.uid;
506bc813cecSpd 			log_debug("%s id: %d, name: %s, uid: %d",
507bc813cecSpd 			    __func__, vid.vid_id, vid.vid_name,
508bc813cecSpd 			    vid.vid_uid);
509bc813cecSpd 
510bc813cecSpd 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
51153027660Sclaudio 			    imsg.hdr.type, fd, imsg_get_fd(&imsg),
512bc813cecSpd 			    &vid, sizeof(vid)) == -1)
513bc813cecSpd 				goto fail;
514bc813cecSpd 			break;
515af96af6cSreyk 		default:
516af96af6cSreyk 			log_debug("%s: error handling imsg %d",
517af96af6cSreyk 			    __func__, imsg.hdr.type);
518af96af6cSreyk 			control_close(fd, cs);
519af96af6cSreyk 			break;
520af96af6cSreyk 		}
521af96af6cSreyk 		imsg_free(&imsg);
522af96af6cSreyk 	}
523af96af6cSreyk 
524af96af6cSreyk 	imsg_event_add(&c->iev);
525091d9ffeSreyk 	return;
526091d9ffeSreyk 
527091d9ffeSreyk  fail:
528bb55a3a2Sreyk 	if (ret == 0)
529bb55a3a2Sreyk 		ret = EINVAL;
530091d9ffeSreyk 	imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
531091d9ffeSreyk 	    0, 0, -1, &ret, sizeof(ret));
532dd7efffeSclaudio 	imsgbuf_flush(&c->iev.ibuf);
533091d9ffeSreyk 	control_close(fd, cs);
534af96af6cSreyk }
535