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