xref: /openbsd-src/usr.sbin/vmd/vmd.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: vmd.c,v 1.29 2016/08/17 05:07:13 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 Reyk Floeter <reyk@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/param.h>	/* nitems */
20 #include <sys/queue.h>
21 #include <sys/wait.h>
22 #include <sys/cdefs.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <event.h>
29 #include <fcntl.h>
30 #include <pwd.h>
31 #include <signal.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 #include <ctype.h>
35 
36 #include "proc.h"
37 #include "vmd.h"
38 
39 __dead void usage(void);
40 
41 int	 main(int, char **);
42 int	 vmd_configure(void);
43 void	 vmd_sighdlr(int sig, short event, void *arg);
44 void	 vmd_shutdown(void);
45 int	 vmd_control_run(void);
46 int	 vmd_dispatch_control(int, struct privsep_proc *, struct imsg *);
47 int	 vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *);
48 
49 struct vmd	*env;
50 
51 static struct privsep_proc procs[] = {
52 	{ "control",	PROC_CONTROL,	vmd_dispatch_control, control },
53 	{ "vmm",	PROC_VMM,	vmd_dispatch_vmm, vmm },
54 };
55 
56 int
57 vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
58 {
59 	struct privsep			*ps = p->p_ps;
60 	int				 res = 0, cmd = 0, v = 0;
61 	struct vm_create_params		 vcp;
62 	struct vmop_id			 vid;
63 	struct vm_terminate_params	 vtp;
64 	struct vmop_result		 vmr;
65 	struct vmd_vm			*vm = NULL;
66 	char				*str = NULL;
67 	uint32_t			 id = 0;
68 
69 	switch (imsg->hdr.type) {
70 	case IMSG_VMDOP_START_VM_REQUEST:
71 		IMSG_SIZE_CHECK(imsg, &vcp);
72 		memcpy(&vcp, imsg->data, sizeof(vcp));
73 		res = config_getvm(ps, &vcp, -1, imsg->hdr.peerid);
74 		if (res == -1) {
75 			res = errno;
76 			cmd = IMSG_VMDOP_START_VM_RESPONSE;
77 		}
78 		break;
79 	case IMSG_VMDOP_TERMINATE_VM_REQUEST:
80 		IMSG_SIZE_CHECK(imsg, &vid);
81 		memcpy(&vid, imsg->data, sizeof(vid));
82 		if ((id = vid.vid_id) == 0) {
83 			/* Lookup vm (id) by name */
84 			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
85 				res = ENOENT;
86 				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
87 				break;
88 			}
89 			id = vm->vm_params.vcp_id;
90 		}
91 		memset(&vtp, 0, sizeof(vtp));
92 		vtp.vtp_vm_id = id;
93 		if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
94 		    imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1)
95 			return (-1);
96 		break;
97 	case IMSG_VMDOP_GET_INFO_VM_REQUEST:
98 		proc_forward_imsg(ps, imsg, PROC_VMM, -1);
99 		break;
100 	case IMSG_VMDOP_RELOAD:
101 		v = 1;
102 	case IMSG_VMDOP_LOAD:
103 		if (IMSG_DATA_SIZE(imsg) > 0)
104 			str = get_string((uint8_t *)imsg->data,
105 			    IMSG_DATA_SIZE(imsg));
106 		vmd_reload(v, str);
107 		free(str);
108 		break;
109 	default:
110 		return (-1);
111 	}
112 
113 	switch (cmd) {
114 	case 0:
115 		break;
116 	case IMSG_VMDOP_START_VM_RESPONSE:
117 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
118 		memset(&vmr, 0, sizeof(vmr));
119 		vmr.vmr_result = res;
120 		vmr.vmr_id = id;
121 		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
122 		    imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1)
123 			return (-1);
124 		break;
125 	default:
126 		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
127 		    imsg->hdr.peerid, -1, &res, sizeof(res)) == -1)
128 			return (-1);
129 		break;
130 	}
131 
132 	return (0);
133 }
134 
135 int
136 vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
137 {
138 	struct vmop_result	 vmr;
139 	struct privsep		*ps = p->p_ps;
140 	int			 res = 0;
141 	struct vmd_vm		*vm;
142 	struct vm_create_params	*vcp;
143 	struct vmop_info_result	 vir;
144 
145 	switch (imsg->hdr.type) {
146 	case IMSG_VMDOP_START_VM_RESPONSE:
147 		IMSG_SIZE_CHECK(imsg, &vmr);
148 		memcpy(&vmr, imsg->data, sizeof(vmr));
149 		if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL)
150 			fatalx("%s: invalid vm response", __func__);
151 		vm->vm_pid = vmr.vmr_pid;
152 		vcp = &vm->vm_params;
153 		vcp->vcp_id = vmr.vmr_id;
154 
155 		/*
156 		 * If the peerid is not -1, forward the response back to the
157 		 * the control socket.  If it is -1, the request originated
158 		 * from the parent, not the control socket.
159 		 */
160 		if (vm->vm_peerid != (uint32_t)-1) {
161 			vmr.vmr_result = res;
162 			(void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname,
163 			    sizeof(vmr.vmr_ttyname));
164 			if (proc_compose_imsg(ps, PROC_CONTROL, -1,
165 			    imsg->hdr.type, vm->vm_peerid, -1,
166 			    &vmr, sizeof(vmr)) == -1) {
167 				errno = vmr.vmr_result;
168 				log_warn("%s: failed to foward vm result",
169 				    vcp->vcp_name);
170 				vm_remove(vm);
171 				return (-1);
172 			}
173 		}
174 
175 		if (vmr.vmr_result) {
176 			errno = vmr.vmr_result;
177 			log_warn("%s: failed to start vm", vcp->vcp_name);
178 			vm_remove(vm);
179 		} else {
180 			log_info("%s: started vm %d successfully, tty %s",
181 			    vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname);
182 		}
183 		break;
184 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
185 	case IMSG_VMDOP_TERMINATE_VM_EVENT:
186 		IMSG_SIZE_CHECK(imsg, &vmr);
187 		memcpy(&vmr, imsg->data, sizeof(vmr));
188 		if (imsg->hdr.type == IMSG_VMDOP_TERMINATE_VM_RESPONSE)
189 			proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
190 		if (vmr.vmr_result == 0) {
191 			/* Remove local reference */
192 			vm = vm_getbyid(vmr.vmr_id);
193 			vm_remove(vm);
194 		}
195 		break;
196 	case IMSG_VMDOP_GET_INFO_VM_DATA:
197 		IMSG_SIZE_CHECK(imsg, &vir);
198 		memcpy(&vir, imsg->data, sizeof(vir));
199 		if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL)
200 			(void)strlcpy(vir.vir_ttyname, vm->vm_ttyname,
201 			    sizeof(vir.vir_ttyname));
202 		if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
203 		    imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) {
204 			vm_remove(vm);
205 			return (-1);
206 		}
207 		break;
208 	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
209 		IMSG_SIZE_CHECK(imsg, &res);
210 		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
211 		break;
212 	default:
213 		return (-1);
214 	}
215 
216 	return (0);
217 }
218 
219 void
220 vmd_sighdlr(int sig, short event, void *arg)
221 {
222 	struct privsep	*ps = arg;
223 	int		 die = 0, status, fail, id;
224 	pid_t		 pid;
225 	char		*cause;
226 	const char	*title = "vm";
227 
228 	if (privsep_process != PROC_PARENT)
229 		return;
230 
231 	switch (sig) {
232 	case SIGHUP:
233 		log_info("%s: reload requested with SIGHUP", __func__);
234 
235 		/*
236 		 * This is safe because libevent uses async signal handlers
237 		 * that run in the event loop and not in signal context.
238 		 */
239 		vmd_reload(1, NULL);
240 		break;
241 	case SIGPIPE:
242 		log_info("%s: ignoring SIGPIPE", __func__);
243 		break;
244 	case SIGUSR1:
245 		log_info("%s: ignoring SIGUSR1", __func__);
246 		break;
247 	case SIGTERM:
248 	case SIGINT:
249 		die = 1;
250 		/* FALLTHROUGH */
251 	case SIGCHLD:
252 		do {
253 			int len;
254 
255 			pid = waitpid(-1, &status, WNOHANG);
256 			if (pid <= 0)
257 				continue;
258 
259 			fail = 0;
260 			if (WIFSIGNALED(status)) {
261 				fail = 1;
262 				len = asprintf(&cause, "terminated; signal %d",
263 				    WTERMSIG(status));
264 			} else if (WIFEXITED(status)) {
265 				if (WEXITSTATUS(status) != 0) {
266 					fail = 1;
267 					len = asprintf(&cause,
268 					    "exited abnormally");
269 				} else
270 					len = asprintf(&cause, "exited okay");
271 			} else
272 				fatalx("unexpected cause of SIGCHLD");
273 
274 			if (len == -1)
275 				fatal("asprintf");
276 
277 			for (id = 0; id < PROC_MAX; id++) {
278 				if (pid == ps->ps_pid[id]) {
279 					die = 1;
280 					title = ps->ps_title[id];
281 					break;
282 				}
283 			}
284 			if (fail)
285 				log_warnx("lost child: %s %s", title, cause);
286 
287 			free(cause);
288 		} while (pid > 0 || (pid == -1 && errno == EINTR));
289 
290 		if (die)
291 			vmd_shutdown();
292 		break;
293 	default:
294 		fatalx("unexpected signal");
295 	}
296 }
297 
298 __dead void
299 usage(void)
300 {
301 	extern char *__progname;
302 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
303 	    __progname);
304 	exit(1);
305 }
306 
307 int
308 main(int argc, char **argv)
309 {
310 	struct privsep	*ps;
311 	int		 ch;
312 	const char	*conffile = VMD_CONF;
313 
314 	/* log to stderr until daemonized */
315 	log_init(1, LOG_DAEMON);
316 
317 	if ((env = calloc(1, sizeof(*env))) == NULL)
318 		fatal("calloc: env");
319 
320 	while ((ch = getopt(argc, argv, "D:df:vn")) != -1) {
321 		switch (ch) {
322 		case 'D':
323 			if (cmdline_symset(optarg) < 0)
324 				log_warnx("could not parse macro definition %s",
325 				    optarg);
326 			break;
327 		case 'd':
328 			env->vmd_debug = 2;
329 			break;
330 		case 'f':
331 			conffile = optarg;
332 			break;
333 		case 'v':
334 			env->vmd_verbose++;
335 			break;
336 		case 'n':
337 			env->vmd_noaction = 1;
338 			break;
339 		default:
340 			usage();
341 		}
342 	}
343 
344 	if (env->vmd_noaction && !env->vmd_debug)
345 		env->vmd_debug = 1;
346 
347 	/* check for root privileges */
348 	if (env->vmd_noaction == 0) {
349 		if (geteuid())
350 			fatalx("need root privileges");
351 	}
352 
353 	ps = &env->vmd_ps;
354 	ps->ps_env = env;
355 
356 	if (config_init(env) == -1)
357 		fatal("failed to initialize configuration");
358 
359 	if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL)
360 		fatal("unknown user %s", VMD_USER);
361 
362 	/* Configure the control socket */
363 	ps->ps_csock.cs_name = SOCKET_NAME;
364 	TAILQ_INIT(&ps->ps_rcsocks);
365 
366 	/* Open /dev/vmm */
367 	if (env->vmd_noaction == 0) {
368 		env->vmd_fd = open(VMM_NODE, O_RDWR);
369 		if (env->vmd_fd == -1)
370 			fatal("%s", VMM_NODE);
371 	}
372 
373 	/* Configuration will be parsed after forking the children */
374 	env->vmd_conffile = conffile;
375 
376 	log_init(env->vmd_debug, LOG_DAEMON);
377 	log_verbose(env->vmd_verbose);
378 
379 	if (!env->vmd_debug && daemon(0, 0) == -1)
380 		fatal("can't daemonize");
381 
382 	log_procinit("parent");
383 
384 	ps->ps_ninstances = 1;
385 
386 	if (!env->vmd_noaction)
387 		proc_init(ps, procs, nitems(procs));
388 
389 	event_init();
390 
391 	signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps);
392 	signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps);
393 	signal_set(&ps->ps_evsigchld, SIGCHLD, vmd_sighdlr, ps);
394 	signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps);
395 	signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps);
396 	signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps);
397 
398 	signal_add(&ps->ps_evsigint, NULL);
399 	signal_add(&ps->ps_evsigterm, NULL);
400 	signal_add(&ps->ps_evsigchld, NULL);
401 	signal_add(&ps->ps_evsighup, NULL);
402 	signal_add(&ps->ps_evsigpipe, NULL);
403 	signal_add(&ps->ps_evsigusr1, NULL);
404 
405 	if (!env->vmd_noaction)
406 		proc_listen(ps, procs, nitems(procs));
407 
408 	if (vmd_configure() == -1)
409 		fatalx("configuration failed");
410 
411 	event_dispatch();
412 
413 	log_debug("parent exiting");
414 
415 	return (0);
416 }
417 
418 int
419 vmd_configure(void)
420 {
421 	/*
422 	 * pledge in the parent process:
423 	 * stdio - for malloc and basic I/O including events.
424 	 * rpath - for reload to open and read the configuration files.
425 	 * wpath - for opening disk images and tap devices.
426 	 * tty - for openpty.
427 	 * proc - run kill to terminate its children safely.
428 	 * sendfd - for disks, interfaces and other fds.
429 	 */
430 	if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1)
431 		fatal("pledge");
432 
433 	if (parse_config(env->vmd_conffile) == -1) {
434 		proc_kill(&env->vmd_ps);
435 		exit(1);
436 	}
437 
438 	if (env->vmd_noaction) {
439 		fprintf(stderr, "configuration OK\n");
440 		proc_kill(&env->vmd_ps);
441 		exit(0);
442 	}
443 
444 	return (0);
445 }
446 
447 void
448 vmd_reload(int reset, const char *filename)
449 {
450 	/* Switch back to the default config file */
451 	if (filename == NULL || *filename == '\0')
452 		filename = env->vmd_conffile;
453 
454 	log_debug("%s: level %d config file %s", __func__, reset, filename);
455 
456 	if (reset)
457 		config_setreset(env, CONFIG_ALL);
458 
459 	if (parse_config(filename) == -1) {
460 		log_debug("%s: failed to load config file %s",
461 		    __func__, filename);
462 	}
463 }
464 
465 void
466 vmd_shutdown(void)
467 {
468 	proc_kill(&env->vmd_ps);
469 	free(env);
470 
471 	log_warnx("parent terminating");
472 	exit(0);
473 }
474 
475 struct vmd_vm *
476 vm_getbyvmid(uint32_t vmid)
477 {
478 	struct vmd_vm	*vm;
479 
480 	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
481 		if (vm->vm_vmid == vmid)
482 			return (vm);
483 	}
484 
485 	return (NULL);
486 }
487 
488 struct vmd_vm *
489 vm_getbyid(uint32_t id)
490 {
491 	struct vmd_vm	*vm;
492 
493 	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
494 		if (vm->vm_params.vcp_id == id)
495 			return (vm);
496 	}
497 
498 	return (NULL);
499 }
500 
501 struct vmd_vm *
502 vm_getbyname(const char *name)
503 {
504 	struct vmd_vm	*vm;
505 
506 	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
507 		if (strcmp(vm->vm_params.vcp_name, name) == 0)
508 			return (vm);
509 	}
510 
511 	return (NULL);
512 }
513 
514 struct vmd_vm *
515 vm_getbypid(pid_t pid)
516 {
517 	struct vmd_vm	*vm;
518 
519 	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
520 		if (vm->vm_pid == pid)
521 			return (vm);
522 	}
523 
524 	return (NULL);
525 }
526 
527 void
528 vm_remove(struct vmd_vm *vm)
529 {
530 	unsigned int	 i;
531 
532 	if (vm == NULL)
533 		return;
534 
535 	TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
536 
537 	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) {
538 		if (vm->vm_disks[i] != -1)
539 			close(vm->vm_disks[i]);
540 	}
541 	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
542 		if (vm->vm_ifs[i] != -1)
543 			close(vm->vm_ifs[i]);
544 	}
545 	if (vm->vm_kernel != -1)
546 		close(vm->vm_kernel);
547 	if (vm->vm_tty != -1)
548 		close(vm->vm_tty);
549 
550 	free(vm);
551 }
552 
553 char *
554 get_string(uint8_t *ptr, size_t len)
555 {
556 	size_t	 i;
557 
558 	for (i = 0; i < len; i++)
559 		if (!isprint(ptr[i]))
560 			break;
561 
562 	return strndup(ptr, i);
563 }
564