vmd.c revision 1.26
1/*	$OpenBSD: vmd.c,v 1.26 2016/02/02 17:51:11 sthen 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>
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	{ "control",	PROC_CONTROL,	vmd_dispatch_control, control },
53	{ "vmm",	PROC_VMM,	vmd_dispatch_vmm, vmm },
54};
55
56int
57vmd_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
135int
136vmd_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		vcp = &vm->vm_params;
152		if (vmr.vmr_result) {
153			errno = vmr.vmr_result;
154			log_warn("%s: failed to start vm", vcp->vcp_name);
155			vm_remove(vm);
156		} else {
157			vcp->vcp_id = vmr.vmr_id;
158			log_info("%s: started vm %d successfully, tty %s",
159			    vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname);
160		}
161		/*
162		 * If the peerid is -1, the request originated from
163		 * the parent, not the control socket.
164		 */
165		if (vm->vm_peerid == (uint32_t)-1)
166			break;
167		vmr.vmr_result = res;
168		(void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname,
169		    sizeof(vmr.vmr_ttyname));
170		if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
171		    vm->vm_peerid, -1, &vmr, sizeof(vmr)) == -1) {
172			vm_remove(vm);
173			return (-1);
174		}
175		break;
176	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
177		IMSG_SIZE_CHECK(imsg, &vmr);
178		memcpy(&vmr, imsg->data, sizeof(vmr));
179		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
180		if (vmr.vmr_result == 0) {
181			/* Remove local reference */
182			vm = vm_getbyid(vmr.vmr_id);
183			vm_remove(vm);
184		}
185		break;
186	case IMSG_VMDOP_GET_INFO_VM_DATA:
187		IMSG_SIZE_CHECK(imsg, &vir);
188		memcpy(&vir, imsg->data, sizeof(vir));
189		if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL)
190			(void)strlcpy(vir.vir_ttyname, vm->vm_ttyname,
191			    sizeof(vir.vir_ttyname));
192		if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
193		    imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) {
194			vm_remove(vm);
195			return (-1);
196		}
197		break;
198	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
199		IMSG_SIZE_CHECK(imsg, &res);
200		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
201		break;
202	default:
203		return (-1);
204	}
205
206	return (0);
207}
208
209void
210vmd_sighdlr(int sig, short event, void *arg)
211{
212	struct privsep	*ps = arg;
213	int		 die = 0, status, fail, id;
214	pid_t		 pid;
215	char		*cause;
216	const char	*title = "vm";
217
218	if (privsep_process != PROC_PARENT)
219		return;
220
221	switch (sig) {
222	case SIGHUP:
223		log_info("%s: reload requested with SIGHUP", __func__);
224
225		/*
226		 * This is safe because libevent uses async signal handlers
227		 * that run in the event loop and not in signal context.
228		 */
229		vmd_reload(1, NULL);
230		break;
231	case SIGPIPE:
232		log_info("%s: ignoring SIGPIPE", __func__);
233		break;
234	case SIGUSR1:
235		log_info("%s: ignoring SIGUSR1", __func__);
236		break;
237	case SIGTERM:
238	case SIGINT:
239		die = 1;
240		/* FALLTHROUGH */
241	case SIGCHLD:
242		do {
243			int len;
244
245			pid = waitpid(-1, &status, WNOHANG);
246			if (pid <= 0)
247				continue;
248
249			fail = 0;
250			if (WIFSIGNALED(status)) {
251				fail = 1;
252				len = asprintf(&cause, "terminated; signal %d",
253				    WTERMSIG(status));
254			} else if (WIFEXITED(status)) {
255				if (WEXITSTATUS(status) != 0) {
256					fail = 1;
257					len = asprintf(&cause,
258					    "exited abnormally");
259				} else
260					len = asprintf(&cause, "exited okay");
261			} else
262				fatalx("unexpected cause of SIGCHLD");
263
264			if (len == -1)
265				fatal("asprintf");
266
267			for (id = 0; id < PROC_MAX; id++) {
268				if (pid == ps->ps_pid[id]) {
269					die = 1;
270					title = ps->ps_title[id];
271					break;
272				}
273			}
274			if (fail)
275				log_warnx("lost child: %s %s", title, cause);
276
277			free(cause);
278		} while (pid > 0 || (pid == -1 && errno == EINTR));
279
280		if (die)
281			vmd_shutdown();
282		break;
283	default:
284		fatalx("unexpected signal");
285	}
286}
287
288__dead void
289usage(void)
290{
291	extern char *__progname;
292	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
293	    __progname);
294	exit(1);
295}
296
297int
298main(int argc, char **argv)
299{
300	struct privsep	*ps;
301	int		 ch;
302	const char	*conffile = VMD_CONF;
303
304	/* log to stderr until daemonized */
305	log_init(1, LOG_DAEMON);
306
307	if ((env = calloc(1, sizeof(*env))) == NULL)
308		fatal("calloc: env");
309
310	while ((ch = getopt(argc, argv, "D:df:vn")) != -1) {
311		switch (ch) {
312		case 'D':
313			if (cmdline_symset(optarg) < 0)
314				log_warnx("could not parse macro definition %s",
315				    optarg);
316			break;
317		case 'd':
318			env->vmd_debug = 2;
319			break;
320		case 'f':
321			conffile = optarg;
322			break;
323		case 'v':
324			env->vmd_verbose++;
325			break;
326		case 'n':
327			env->vmd_noaction = 1;
328			break;
329		default:
330			usage();
331		}
332	}
333
334	if (env->vmd_noaction && !env->vmd_debug)
335		env->vmd_debug = 1;
336
337	/* check for root privileges */
338	if (env->vmd_noaction == 0) {
339		if (geteuid())
340			fatalx("need root privileges");
341	}
342
343	ps = &env->vmd_ps;
344	ps->ps_env = env;
345
346	if (config_init(env) == -1)
347		fatal("failed to initialize configuration");
348
349	if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL)
350		fatal("unknown user %s", VMD_USER);
351
352	/* Configure the control socket */
353	ps->ps_csock.cs_name = SOCKET_NAME;
354	TAILQ_INIT(&ps->ps_rcsocks);
355
356	/* Open /dev/vmm */
357	if (env->vmd_noaction == 0) {
358		env->vmd_fd = open(VMM_NODE, O_RDWR);
359		if (env->vmd_fd == -1)
360			fatal("%s", VMM_NODE);
361	}
362
363	/* Configuration will be parsed after forking the children */
364	env->vmd_conffile = conffile;
365
366	log_init(env->vmd_debug, LOG_DAEMON);
367	log_verbose(env->vmd_verbose);
368
369	if (!env->vmd_debug && daemon(0, 0) == -1)
370		fatal("can't daemonize");
371
372	log_procinit("parent");
373
374	ps->ps_ninstances = 1;
375
376	if (!env->vmd_noaction)
377		proc_init(ps, procs, nitems(procs));
378
379	event_init();
380
381	signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps);
382	signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps);
383	signal_set(&ps->ps_evsigchld, SIGCHLD, vmd_sighdlr, ps);
384	signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps);
385	signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps);
386	signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps);
387
388	signal_add(&ps->ps_evsigint, NULL);
389	signal_add(&ps->ps_evsigterm, NULL);
390	signal_add(&ps->ps_evsigchld, NULL);
391	signal_add(&ps->ps_evsighup, NULL);
392	signal_add(&ps->ps_evsigpipe, NULL);
393	signal_add(&ps->ps_evsigusr1, NULL);
394
395	if (!env->vmd_noaction)
396		proc_listen(ps, procs, nitems(procs));
397
398	if (vmd_configure() == -1)
399		fatalx("configuration failed");
400
401	event_dispatch();
402
403	log_debug("parent exiting");
404
405	return (0);
406}
407
408int
409vmd_configure(void)
410{
411	/*
412	 * pledge in the parent process:
413	 * stdio - for malloc and basic I/O including events.
414	 * rpath - for reload to open and read the configuration files.
415	 * wpath - for opening disk images and tap devices.
416	 * tty - for openpty.
417	 * proc - run kill to terminate its children safely.
418	 * sendfd - for disks, interfaces and other fds.
419	 */
420	if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1)
421		fatal("pledge");
422
423	if (parse_config(env->vmd_conffile) == -1) {
424		proc_kill(&env->vmd_ps);
425		exit(1);
426	}
427
428	if (env->vmd_noaction) {
429		fprintf(stderr, "configuration OK\n");
430		proc_kill(&env->vmd_ps);
431		exit(0);
432	}
433
434	return (0);
435}
436
437void
438vmd_reload(int reset, const char *filename)
439{
440	/* Switch back to the default config file */
441	if (filename == NULL || *filename == '\0')
442		filename = env->vmd_conffile;
443
444	log_debug("%s: level %d config file %s", __func__, reset, filename);
445
446	if (reset)
447		config_setreset(env, CONFIG_ALL);
448
449	if (parse_config(filename) == -1) {
450		log_debug("%s: failed to load config file %s",
451		    __func__, filename);
452	}
453}
454
455void
456vmd_shutdown(void)
457{
458	proc_kill(&env->vmd_ps);
459	free(env);
460
461	log_warnx("parent terminating");
462	exit(0);
463}
464
465struct vmd_vm *
466vm_getbyvmid(uint32_t vmid)
467{
468	struct vmd_vm	*vm;
469
470	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
471		if (vm->vm_vmid == vmid)
472			return (vm);
473	}
474
475	return (NULL);
476}
477
478struct vmd_vm *
479vm_getbyid(uint32_t id)
480{
481	struct vmd_vm	*vm;
482
483	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
484		if (vm->vm_params.vcp_id == id)
485			return (vm);
486	}
487
488	return (NULL);
489}
490
491struct vmd_vm *
492vm_getbyname(const char *name)
493{
494	struct vmd_vm	*vm;
495
496	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
497		if (strcmp(vm->vm_params.vcp_name, name) == 0)
498			return (vm);
499	}
500
501	return (NULL);
502}
503
504void
505vm_remove(struct vmd_vm *vm)
506{
507	unsigned int	 i;
508
509	if (vm == NULL)
510		return;
511
512	TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
513
514	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) {
515		if (vm->vm_disks[i] != -1)
516			close(vm->vm_disks[i]);
517	}
518	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
519		if (vm->vm_ifs[i] != -1)
520			close(vm->vm_ifs[i]);
521	}
522	if (vm->vm_kernel != -1)
523		close(vm->vm_kernel);
524	if (vm->vm_tty != -1)
525		close(vm->vm_tty);
526
527	free(vm);
528}
529
530char *
531get_string(uint8_t *ptr, size_t len)
532{
533	size_t	 i;
534
535	for (i = 0; i < len; i++)
536		if (!isprint(ptr[i]))
537			break;
538
539	return strndup(ptr, i);
540}
541