vmd.c revision 1.77
1/*	$OpenBSD: vmd.c,v 1.77 2018/01/03 05:39:56 ccardenas 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#include <sys/stat.h>
24#include <sys/tty.h>
25#include <sys/ioctl.h>
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <termios.h>
31#include <errno.h>
32#include <event.h>
33#include <fcntl.h>
34#include <pwd.h>
35#include <signal.h>
36#include <syslog.h>
37#include <unistd.h>
38#include <ctype.h>
39#include <pwd.h>
40#include <grp.h>
41
42#include <machine/specialreg.h>
43#include <machine/vmmvar.h>
44
45#include "proc.h"
46#include "atomicio.h"
47#include "vmd.h"
48
49__dead void usage(void);
50
51int	 main(int, char **);
52int	 vmd_configure(void);
53void	 vmd_sighdlr(int sig, short event, void *arg);
54void	 vmd_shutdown(void);
55int	 vmd_control_run(void);
56int	 vmd_dispatch_control(int, struct privsep_proc *, struct imsg *);
57int	 vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *);
58int	 check_vmh(struct vm_dump_header *);
59
60struct vmd	*env;
61
62static struct privsep_proc procs[] = {
63	/* Keep "priv" on top as procs[0] */
64	{ "priv",	PROC_PRIV,	NULL, priv },
65	{ "control",	PROC_CONTROL,	vmd_dispatch_control, control },
66	{ "vmm",	PROC_VMM,	vmd_dispatch_vmm, vmm, vmm_shutdown },
67};
68
69/* For the privileged process */
70static struct privsep_proc *proc_priv = &procs[0];
71static struct passwd proc_privpw;
72
73int
74vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
75{
76	struct privsep			*ps = p->p_ps;
77	int				 res = 0, ret = 0, cmd = 0, verbose;
78	unsigned int			 v = 0;
79	struct vmop_create_params	 vmc;
80	struct vmop_id			 vid;
81	struct vm_terminate_params	 vtp;
82	struct vmop_result		 vmr;
83	struct vm_dump_header		 vmh;
84	struct vmd_vm			*vm = NULL;
85	char				*str = NULL;
86	uint32_t			 id = 0;
87
88	switch (imsg->hdr.type) {
89	case IMSG_VMDOP_START_VM_REQUEST:
90		IMSG_SIZE_CHECK(imsg, &vmc);
91		memcpy(&vmc, imsg->data, sizeof(vmc));
92		ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid);
93		if (vmc.vmc_flags == 0) {
94			/* start an existing VM with pre-configured options */
95			if (!(ret == -1 && errno == EALREADY &&
96			    vm->vm_running == 0)) {
97				res = errno;
98				cmd = IMSG_VMDOP_START_VM_RESPONSE;
99			}
100		} else if (ret != 0) {
101			res = errno;
102			cmd = IMSG_VMDOP_START_VM_RESPONSE;
103		}
104		if (res == 0 &&
105		    config_setvm(ps, vm, imsg->hdr.peerid, vm->vm_params.vmc_uid) == -1) {
106			res = errno;
107			cmd = IMSG_VMDOP_START_VM_RESPONSE;
108		}
109		break;
110	case IMSG_VMDOP_TERMINATE_VM_REQUEST:
111		IMSG_SIZE_CHECK(imsg, &vid);
112		memcpy(&vid, imsg->data, sizeof(vid));
113		if ((id = vid.vid_id) == 0) {
114			/* Lookup vm (id) by name */
115			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
116				res = ENOENT;
117				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
118				break;
119			} else if (vm->vm_shutdown) {
120				res = EALREADY;
121				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
122				break;
123			} else if (vm->vm_running == 0) {
124				res = EINVAL;
125				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
126				break;
127			}
128			id = vm->vm_vmid;
129		} else if ((vm = vm_getbyvmid(id)) == NULL) {
130			res = ENOENT;
131			cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
132			break;
133		}
134		if (vm_checkperm(vm, vid.vid_uid) != 0) {
135			res = EPERM;
136			cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
137			break;
138		}
139		memset(&vtp, 0, sizeof(vtp));
140		vtp.vtp_vm_id = id;
141		if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
142		    imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1)
143			return (-1);
144		break;
145	case IMSG_VMDOP_GET_INFO_VM_REQUEST:
146		proc_forward_imsg(ps, imsg, PROC_VMM, -1);
147		break;
148	case IMSG_VMDOP_LOAD:
149		IMSG_SIZE_CHECK(imsg, str); /* at least one byte for path */
150		str = get_string((uint8_t *)imsg->data,
151		    IMSG_DATA_SIZE(imsg));
152	case IMSG_VMDOP_RELOAD:
153		if (vmd_reload(0, str) == -1)
154			cmd = IMSG_CTL_FAIL;
155		else
156			cmd = IMSG_CTL_OK;
157		free(str);
158		break;
159	case IMSG_CTL_RESET:
160		IMSG_SIZE_CHECK(imsg, &v);
161		memcpy(&v, imsg->data, sizeof(v));
162		if (vmd_reload(v, NULL) == -1)
163			cmd = IMSG_CTL_FAIL;
164		else
165			cmd = IMSG_CTL_OK;
166		break;
167	case IMSG_CTL_VERBOSE:
168		IMSG_SIZE_CHECK(imsg, &verbose);
169		memcpy(&verbose, imsg->data, sizeof(verbose));
170		log_setverbose(verbose);
171
172		proc_forward_imsg(ps, imsg, PROC_VMM, -1);
173		proc_forward_imsg(ps, imsg, PROC_PRIV, -1);
174		cmd = IMSG_CTL_OK;
175		break;
176	case IMSG_VMDOP_PAUSE_VM:
177	case IMSG_VMDOP_UNPAUSE_VM:
178		IMSG_SIZE_CHECK(imsg, &vid);
179		memcpy(&vid, imsg->data, sizeof(vid));
180		if (vid.vid_id == 0) {
181			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
182				res = ENOENT;
183				cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE;
184				break;
185			} else {
186				vid.vid_id = vm->vm_vmid;
187			}
188		}
189		proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
190		    imsg->hdr.peerid, -1, &vid, sizeof(vid));
191		break;
192	case IMSG_VMDOP_SEND_VM_REQUEST:
193		IMSG_SIZE_CHECK(imsg, &vid);
194		memcpy(&vid, imsg->data, sizeof(vid));
195		id = vid.vid_id;
196		if (vid.vid_id == 0) {
197			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
198				res = ENOENT;
199				cmd = IMSG_VMDOP_SEND_VM_RESPONSE;
200				close(imsg->fd);
201				break;
202			} else {
203				vid.vid_id = vm->vm_vmid;
204			}
205		} else if ((vm = vm_getbyvmid(vid.vid_id)) == NULL) {
206			res = ENOENT;
207			cmd = IMSG_VMDOP_SEND_VM_RESPONSE;
208			close(imsg->fd);
209			break;
210		} else {
211		}
212		vmr.vmr_id = vid.vid_id;
213		log_debug("%s: sending fd to vmm", __func__);
214		proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
215		    imsg->hdr.peerid, imsg->fd, &vid, sizeof(vid));
216		break;
217	case IMSG_VMDOP_RECEIVE_VM_REQUEST:
218		IMSG_SIZE_CHECK(imsg, &vid);
219		memcpy(&vid, imsg->data, sizeof(vid));
220		if (imsg->fd == -1) {
221			log_warnx("%s: invalid fd", __func__);
222			return (-1);
223		}
224		if (atomicio(read, imsg->fd, &vmh, sizeof(vmh)) !=
225		    sizeof(vmh)) {
226			log_warnx("%s: error reading vmh from recevied vm",
227			    __func__);
228			res = EIO;
229			close(imsg->fd);
230			cmd = IMSG_VMDOP_START_VM_RESPONSE;
231			break;
232		}
233		if(check_vmh(&vmh)) {
234			res = ENOENT;
235			close(imsg->fd);
236			cmd = IMSG_VMDOP_START_VM_RESPONSE;
237			break;
238		}
239		if (atomicio(read, imsg->fd, &vmc, sizeof(vmc)) !=
240		    sizeof(vmc)) {
241			log_warnx("%s: error reading vmc from recevied vm",
242			    __func__);
243			res = EIO;
244			close(imsg->fd);
245			cmd = IMSG_VMDOP_START_VM_RESPONSE;
246			break;
247		}
248		strlcpy(vmc.vmc_params.vcp_name, vid.vid_name,
249		    sizeof(vmc.vmc_params.vcp_name));
250		vmc.vmc_params.vcp_id = 0;
251
252		ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid);
253		if (ret != 0) {
254			res = errno;
255			cmd = IMSG_VMDOP_START_VM_RESPONSE;
256			close(imsg->fd);
257		} else {
258			vm->vm_received = 1;
259			config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid);
260			log_debug("%s: sending fd to vmm", __func__);
261			proc_compose_imsg(ps, PROC_VMM, -1,
262			    IMSG_VMDOP_RECEIVE_VM_END, vm->vm_vmid, imsg->fd,
263			    NULL, 0);
264		}
265		break;
266	default:
267		return (-1);
268	}
269
270	switch (cmd) {
271	case 0:
272		break;
273	case IMSG_VMDOP_START_VM_RESPONSE:
274	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
275		memset(&vmr, 0, sizeof(vmr));
276		vmr.vmr_result = res;
277		vmr.vmr_id = id;
278		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
279		    imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1)
280			return (-1);
281		break;
282	default:
283		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
284		    imsg->hdr.peerid, -1, &res, sizeof(res)) == -1)
285			return (-1);
286		break;
287	}
288
289	return (0);
290}
291
292int
293vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
294{
295	struct vmop_result	 vmr;
296	struct privsep		*ps = p->p_ps;
297	int			 res = 0;
298	struct vmd_vm		*vm;
299	struct vm_create_params	*vcp;
300	struct vmop_info_result	 vir;
301
302	switch (imsg->hdr.type) {
303	case IMSG_VMDOP_PAUSE_VM_RESPONSE:
304		IMSG_SIZE_CHECK(imsg, &vmr);
305		memcpy(&vmr, imsg->data, sizeof(vmr));
306		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
307			break;
308		proc_compose_imsg(ps, PROC_CONTROL, -1,
309		    imsg->hdr.type, imsg->hdr.peerid, -1,
310		    imsg->data, sizeof(imsg->data));
311		log_info("%s: paused vm %d successfully",
312		    vm->vm_params.vmc_params.vcp_name,
313		    vm->vm_vmid);
314		break;
315	case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
316		IMSG_SIZE_CHECK(imsg, &vmr);
317		memcpy(&vmr, imsg->data, sizeof(vmr));
318		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
319			break;
320		proc_compose_imsg(ps, PROC_CONTROL, -1,
321		    imsg->hdr.type, imsg->hdr.peerid, -1,
322		    imsg->data, sizeof(imsg->data));
323		log_info("%s: unpaused vm %d successfully.",
324		    vm->vm_params.vmc_params.vcp_name,
325		    vm->vm_vmid);
326		break;
327	case IMSG_VMDOP_START_VM_RESPONSE:
328		IMSG_SIZE_CHECK(imsg, &vmr);
329		memcpy(&vmr, imsg->data, sizeof(vmr));
330		if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL)
331			break;
332		vm->vm_pid = vmr.vmr_pid;
333		vcp = &vm->vm_params.vmc_params;
334		vcp->vcp_id = vmr.vmr_id;
335
336		/*
337		 * If the peerid is not -1, forward the response back to the
338		 * the control socket.  If it is -1, the request originated
339		 * from the parent, not the control socket.
340		 */
341		if (vm->vm_peerid != (uint32_t)-1) {
342			(void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname,
343			    sizeof(vmr.vmr_ttyname));
344			if (proc_compose_imsg(ps, PROC_CONTROL, -1,
345			    imsg->hdr.type, vm->vm_peerid, -1,
346			    &vmr, sizeof(vmr)) == -1) {
347				errno = vmr.vmr_result;
348				log_warn("%s: failed to foward vm result",
349				    vcp->vcp_name);
350				vm_remove(vm);
351				return (-1);
352			}
353		}
354
355		if (vmr.vmr_result) {
356			errno = vmr.vmr_result;
357			log_warn("%s: failed to start vm", vcp->vcp_name);
358			vm_remove(vm);
359			break;
360		}
361
362		/* Now configure all the interfaces */
363		if (vm_priv_ifconfig(ps, vm) == -1) {
364			log_warn("%s: failed to configure vm", vcp->vcp_name);
365			vm_remove(vm);
366			break;
367		}
368
369		log_info("%s: started vm %d successfully, tty %s",
370		    vcp->vcp_name, vm->vm_vmid, vm->vm_ttyname);
371		break;
372	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
373		IMSG_SIZE_CHECK(imsg, &vmr);
374		memcpy(&vmr, imsg->data, sizeof(vmr));
375		log_debug("%s: forwarding TERMINATE VM for vm id %d",
376		    __func__, vmr.vmr_id);
377		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
378		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
379			break;
380		if (vmr.vmr_result == 0) {
381			/* Mark VM as shutting down */
382			vm->vm_shutdown = 1;
383		}
384		break;
385	case IMSG_VMDOP_SEND_VM_RESPONSE:
386		IMSG_SIZE_CHECK(imsg, &vmr);
387		memcpy(&vmr, imsg->data, sizeof(vmr));
388		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
389			break;
390		if (!vmr.vmr_result)
391			log_info("%s: sent vm %d successfully.",
392			    vm->vm_params.vmc_params.vcp_name,
393			    vm->vm_vmid);
394	case IMSG_VMDOP_TERMINATE_VM_EVENT:
395		IMSG_SIZE_CHECK(imsg, &vmr);
396		memcpy(&vmr, imsg->data, sizeof(vmr));
397		log_debug("%s: handling TERMINATE_EVENT for vm id %d ret %d",
398		    __func__, vmr.vmr_id, vmr.vmr_result);
399		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) {
400			log_debug("%s: vm %d is no longer available",
401			    __func__, vmr.vmr_id);
402			break;
403		}
404		if (vmr.vmr_result != EAGAIN) {
405			if (vm->vm_from_config) {
406				log_debug("%s: about to stop vm id %d",
407				    __func__, vm->vm_vmid);
408				vm_stop(vm, 0);
409			} else {
410				log_debug("%s: about to remove vm %d",
411				    __func__, vm->vm_vmid);
412				vm_remove(vm);
413			}
414		} else {
415			/* Stop VM instance but keep the tty open */
416			log_debug("%s: about to stop vm id %d with tty open",
417			    __func__, vm->vm_vmid);
418			vm_stop(vm, 1);
419			config_setvm(ps, vm, (uint32_t)-1, vm->vm_uid);
420		}
421		break;
422	case IMSG_VMDOP_GET_INFO_VM_DATA:
423		IMSG_SIZE_CHECK(imsg, &vir);
424		memcpy(&vir, imsg->data, sizeof(vir));
425		if ((vm = vm_getbyvmid(vir.vir_info.vir_id)) != NULL) {
426			memset(vir.vir_ttyname, 0, sizeof(vir.vir_ttyname));
427			if (vm->vm_ttyname != NULL)
428				strlcpy(vir.vir_ttyname, vm->vm_ttyname,
429				    sizeof(vir.vir_ttyname));
430			if (vm->vm_shutdown) {
431				/* XXX there might be a nicer way */
432				(void)strlcat(vir.vir_info.vir_name,
433				    " - stopping",
434				    sizeof(vir.vir_info.vir_name));
435			}
436			/* get the user id who started the vm */
437			vir.vir_uid = vm->vm_uid;
438			vir.vir_gid = vm->vm_params.vmc_gid;
439		}
440		if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
441		    imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) {
442			log_debug("%s: GET_INFO_VM failed for vm %d, removing",
443			    __func__, vm->vm_vmid);
444			vm_remove(vm);
445			return (-1);
446		}
447		break;
448	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
449		/*
450		 * PROC_VMM has responded with the *running* VMs, now we
451		 * append the others. These use the special value 0 for their
452		 * kernel id to indicate that they are not running.
453		 */
454		TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
455			if (!vm->vm_running) {
456				memset(&vir, 0, sizeof(vir));
457				vir.vir_info.vir_id = vm->vm_vmid;
458				strlcpy(vir.vir_info.vir_name,
459				    vm->vm_params.vmc_params.vcp_name,
460				    VMM_MAX_NAME_LEN);
461				vir.vir_info.vir_memory_size =
462				    vm->vm_params.vmc_params.vcp_memranges[0].vmr_size;
463				vir.vir_info.vir_ncpus =
464				    vm->vm_params.vmc_params.vcp_ncpus;
465				/* get the configured user id for this vm */
466				vir.vir_uid = vm->vm_params.vmc_uid;
467				vir.vir_gid = vm->vm_params.vmc_gid;
468				if (proc_compose_imsg(ps, PROC_CONTROL, -1,
469				    IMSG_VMDOP_GET_INFO_VM_DATA,
470				    imsg->hdr.peerid, -1, &vir,
471				    sizeof(vir)) == -1) {
472					log_debug("%s: GET_INFO_VM_END failed",
473					    __func__);
474					vm_remove(vm);
475					return (-1);
476				}
477			}
478		}
479		IMSG_SIZE_CHECK(imsg, &res);
480		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
481		break;
482	default:
483		return (-1);
484	}
485
486	return (0);
487}
488
489int
490check_vmh(struct vm_dump_header *vmh) {
491	int i;
492	unsigned int code, leaf;
493	unsigned int a, b, c, d;
494
495
496	if (vmh->vmh_version != VM_DUMP_VERSION) {
497		log_warnx("%s: incompatible dump version", __func__);
498		return (-1);
499	}
500
501	for (i = 0; i < VM_DUMP_HEADER_CPUID_COUNT; i++) {
502		code = vmh->vmh_cpuids[i].code;
503		leaf = vmh->vmh_cpuids[i].leaf;
504		if (leaf != 0x00) {
505			log_debug("%s: invalid leaf 0x%x for code 0x%x",
506			    __func__, leaf, code);
507			return (-1);
508		}
509
510		switch(code) {
511		case 0x00:
512		CPUID_LEAF(code, leaf, a, b, c, d);
513		if (vmh->vmh_cpuids[i].a > a) {
514			log_debug("%s: incompatible cpuid level", __func__);
515			return (-1);
516		}
517		if (!(vmh->vmh_cpuids[i].b == b &&
518		    vmh->vmh_cpuids[i].c == c &&
519		    vmh->vmh_cpuids[i].d == d)) {
520			log_debug("%s: incompatible cpu brand", __func__);
521			return (-1);
522		}
523		break;
524
525		case 0x01:
526		CPUID_LEAF(code, leaf, a, b, c, d);
527		if ((vmh->vmh_cpuids[i].c & c & VMM_CPUIDECX_MASK) !=
528		    (vmh->vmh_cpuids[i].c & VMM_CPUIDECX_MASK)) {
529			log_debug("%s: incompatible cpu features "
530			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
531			    code, leaf);
532			return (-1);
533		}
534		if ((vmh->vmh_cpuids[i].d & d & VMM_CPUIDEDX_MASK) !=
535		    (vmh->vmh_cpuids[i].d & VMM_CPUIDEDX_MASK)) {
536			log_debug("%s: incompatible cpu features "
537			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
538			    code, leaf);
539			return (-1);
540		}
541		break;
542
543		case 0x07:
544		CPUID_LEAF(code, leaf, a, b, c, d);
545		if ((vmh->vmh_cpuids[i].b & b & VMM_SEFF0EBX_MASK) !=
546		    (vmh->vmh_cpuids[i].b & VMM_SEFF0EBX_MASK)) {
547			log_debug("%s: incompatible cpu features "
548			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
549			    code, leaf);
550			return (-1);
551		}
552		if ((vmh->vmh_cpuids[i].c & c & VMM_SEFF0ECX_MASK) !=
553		    (vmh->vmh_cpuids[i].c & VMM_SEFF0ECX_MASK)) {
554			log_debug("%s: incompatible cpu features "
555			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
556			    code, leaf);
557			return (-1);
558		}
559		break;
560
561		case 0x0d:
562		CPUID_LEAF(code, leaf, a, b, c, d);
563		if (vmh->vmh_cpuids[i].b > b) {
564			log_debug("%s: incompatible cpu: insufficient "
565			    "max save area for enabled XCR0 features",
566			    __func__);
567			return (-1);
568		}
569		if (vmh->vmh_cpuids[i].c > c) {
570			log_debug("%s: incompatible cpu: insufficient "
571			    "max save area for supported XCR0 features",
572			    __func__);
573			return (-1);
574		}
575		break;
576
577		case 0x80000001:
578		CPUID_LEAF(code, leaf, a, b, c, d);
579		if ((vmh->vmh_cpuids[i].a & a) != vmh->vmh_cpuids[i].a) {
580			log_debug("%s: incompatible cpu features "
581			    "code: 0x%x leaf: 0x%x  reg: a", __func__,
582			    code, leaf);
583			return (-1);
584		}
585		if ((vmh->vmh_cpuids[i].c & c) != vmh->vmh_cpuids[i].c) {
586			log_debug("%s: incompatible cpu features "
587			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
588			    code, leaf);
589			return (-1);
590		}
591		if ((vmh->vmh_cpuids[i].d & d) != vmh->vmh_cpuids[i].d) {
592			log_debug("%s: incompatible cpu features "
593			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
594			    code, leaf);
595			return (-1);
596		}
597		break;
598
599		default:
600		log_debug("%s: unknown code 0x%x", __func__, code);
601		return (-1);
602		}
603	}
604
605	return (0);
606}
607
608void
609vmd_sighdlr(int sig, short event, void *arg)
610{
611	if (privsep_process != PROC_PARENT)
612		return;
613	log_debug("%s: handling signal", __func__);
614
615	switch (sig) {
616	case SIGHUP:
617		log_info("%s: reload requested with SIGHUP", __func__);
618
619		/*
620		 * This is safe because libevent uses async signal handlers
621		 * that run in the event loop and not in signal context.
622		 */
623		(void)vmd_reload(0, NULL);
624		break;
625	case SIGPIPE:
626		log_info("%s: ignoring SIGPIPE", __func__);
627		break;
628	case SIGUSR1:
629		log_info("%s: ignoring SIGUSR1", __func__);
630		break;
631	case SIGTERM:
632	case SIGINT:
633		vmd_shutdown();
634		break;
635	default:
636		fatalx("unexpected signal");
637	}
638}
639
640__dead void
641usage(void)
642{
643	extern char *__progname;
644	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
645	    __progname);
646	exit(1);
647}
648
649int
650main(int argc, char **argv)
651{
652	struct privsep		*ps;
653	int			 ch;
654	const char		*conffile = VMD_CONF;
655	enum privsep_procid	 proc_id = PROC_PARENT;
656	int			 proc_instance = 0;
657	const char		*errp, *title = NULL;
658	int			 argc0 = argc;
659
660	/* log to stderr until daemonized */
661	log_init(1, LOG_DAEMON);
662
663	if ((env = calloc(1, sizeof(*env))) == NULL)
664		fatal("calloc: env");
665
666	while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) {
667		switch (ch) {
668		case 'D':
669			if (cmdline_symset(optarg) < 0)
670				log_warnx("could not parse macro definition %s",
671				    optarg);
672			break;
673		case 'd':
674			env->vmd_debug = 2;
675			break;
676		case 'f':
677			conffile = optarg;
678			break;
679		case 'v':
680			env->vmd_verbose++;
681			break;
682		case 'n':
683			env->vmd_noaction = 1;
684			break;
685		case 'P':
686			title = optarg;
687			proc_id = proc_getid(procs, nitems(procs), title);
688			if (proc_id == PROC_MAX)
689				fatalx("invalid process name");
690			break;
691		case 'I':
692			proc_instance = strtonum(optarg, 0,
693			    PROC_MAX_INSTANCES, &errp);
694			if (errp)
695				fatalx("invalid process instance");
696			break;
697		default:
698			usage();
699		}
700	}
701
702	argc -= optind;
703	if (argc > 0)
704		usage();
705
706	if (env->vmd_noaction && !env->vmd_debug)
707		env->vmd_debug = 1;
708
709	/* check for root privileges */
710	if (env->vmd_noaction == 0) {
711		if (geteuid())
712			fatalx("need root privileges");
713	}
714
715	ps = &env->vmd_ps;
716	ps->ps_env = env;
717	env->vmd_fd = -1;
718
719	if (config_init(env) == -1)
720		fatal("failed to initialize configuration");
721
722	if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL)
723		fatal("unknown user %s", VMD_USER);
724
725	/* First proc runs as root without pledge but in default chroot */
726	proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */
727	proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */
728
729	/* Open /dev/vmm */
730	if (env->vmd_noaction == 0) {
731		env->vmd_fd = open(VMM_NODE, O_RDWR);
732		if (env->vmd_fd == -1)
733			fatal("%s", VMM_NODE);
734	}
735
736	/* Configure the control socket */
737	ps->ps_csock.cs_name = SOCKET_NAME;
738	TAILQ_INIT(&ps->ps_rcsocks);
739
740	/* Configuration will be parsed after forking the children */
741	env->vmd_conffile = conffile;
742
743	log_init(env->vmd_debug, LOG_DAEMON);
744	log_setverbose(env->vmd_verbose);
745
746	if (env->vmd_noaction)
747		ps->ps_noaction = 1;
748	ps->ps_instance = proc_instance;
749	if (title != NULL)
750		ps->ps_title[proc_id] = title;
751
752	/* only the parent returns */
753	proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);
754
755	log_procinit("parent");
756	if (!env->vmd_debug && daemon(0, 0) == -1)
757		fatal("can't daemonize");
758
759	if (ps->ps_noaction == 0)
760		log_info("startup");
761
762	event_init();
763
764	signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps);
765	signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps);
766	signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps);
767	signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps);
768	signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps);
769
770	signal_add(&ps->ps_evsigint, NULL);
771	signal_add(&ps->ps_evsigterm, NULL);
772	signal_add(&ps->ps_evsighup, NULL);
773	signal_add(&ps->ps_evsigpipe, NULL);
774	signal_add(&ps->ps_evsigusr1, NULL);
775
776	if (!env->vmd_noaction)
777		proc_connect(ps);
778
779	if (vmd_configure() == -1)
780		fatalx("configuration failed");
781
782	event_dispatch();
783
784	log_debug("parent exiting");
785
786	return (0);
787}
788
789int
790vmd_configure(void)
791{
792	struct vmd_vm		*vm;
793	struct vmd_switch	*vsw;
794
795	if ((env->vmd_ptmfd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC)) == -1)
796		fatal("open %s", PATH_PTMDEV);
797
798	/*
799	 * pledge in the parent process:
800	 * stdio - for malloc and basic I/O including events.
801	 * rpath - for reload to open and read the configuration files.
802	 * wpath - for opening disk images and tap devices.
803	 * tty - for openpty.
804	 * proc - run kill to terminate its children safely.
805	 * sendfd - for disks, interfaces and other fds.
806	 * recvfd - for send and receive.
807	 * getpw - lookup user or group id by name.
808	 * chown, fattr - change tty ownership
809	 */
810	if (pledge("stdio rpath wpath proc tty recvfd sendfd getpw"
811	    " chown fattr", NULL) == -1)
812		fatal("pledge");
813
814	if (parse_config(env->vmd_conffile) == -1) {
815		proc_kill(&env->vmd_ps);
816		exit(1);
817	}
818
819	if (env->vmd_noaction) {
820		fprintf(stderr, "configuration OK\n");
821		proc_kill(&env->vmd_ps);
822		exit(0);
823	}
824
825	TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
826		if (vsw->sw_running)
827			continue;
828		if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) {
829			log_warn("%s: failed to create switch %s",
830			    __func__, vsw->sw_name);
831			switch_remove(vsw);
832			return (-1);
833		}
834	}
835
836	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
837		if (vm->vm_disabled) {
838			log_debug("%s: not creating vm %s (disabled)",
839			    __func__,
840			    vm->vm_params.vmc_params.vcp_name);
841			continue;
842		}
843		if (config_setvm(&env->vmd_ps, vm, -1, vm->vm_params.vmc_uid) == -1)
844			return (-1);
845	}
846
847	/* Send shared global configuration to all children */
848	if (config_setconfig(env) == -1)
849		return (-1);
850
851	return (0);
852}
853
854int
855vmd_reload(unsigned int reset, const char *filename)
856{
857	struct vmd_vm		*vm, *next_vm;
858	struct vmd_switch	*vsw;
859	int			 reload = 0;
860
861	/* Switch back to the default config file */
862	if (filename == NULL || *filename == '\0') {
863		filename = env->vmd_conffile;
864		reload = 1;
865	}
866
867	log_debug("%s: level %d config file %s", __func__, reset, filename);
868
869	if (reset) {
870		/* Purge the configuration */
871		config_purge(env, reset);
872		config_setreset(env, reset);
873	} else {
874		/*
875		 * Load or reload the configuration.
876		 *
877		 * Reloading removes all non-running VMs before processing the
878		 * config file, whereas loading only adds to the existing list
879		 * of VMs.
880		 */
881
882		if (reload) {
883			TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, next_vm) {
884				if (vm->vm_running == 0) {
885					log_debug("%s: calling vm_remove",
886					    __func__);
887					vm_remove(vm);
888				}
889			}
890
891			/* Update shared global configuration in all children */
892			if (config_setconfig(env) == -1)
893				return (-1);
894		}
895
896		if (parse_config(filename) == -1) {
897			log_debug("%s: failed to load config file %s",
898			    __func__, filename);
899			return (-1);
900		}
901
902		TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
903			if (vsw->sw_running)
904				continue;
905			if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) {
906				log_warn("%s: failed to create switch %s",
907				    __func__, vsw->sw_name);
908				switch_remove(vsw);
909				return (-1);
910			}
911		}
912
913		TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
914			if (vm->vm_running == 0) {
915				if (vm->vm_disabled) {
916					log_debug("%s: not creating vm %s"
917					    " (disabled)", __func__,
918					    vm->vm_params.vmc_params.vcp_name);
919					continue;
920				}
921				if (config_setvm(&env->vmd_ps, vm, -1, vm->vm_params.vmc_uid) == -1)
922					return (-1);
923			} else {
924				log_debug("%s: not creating vm \"%s\": "
925				    "(running)", __func__,
926				    vm->vm_params.vmc_params.vcp_name);
927			}
928		}
929	}
930
931	return (0);
932}
933
934void
935vmd_shutdown(void)
936{
937	struct vmd_vm *vm, *vm_next;
938
939	log_debug("%s: performing shutdown", __func__);
940	TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) {
941		vm_remove(vm);
942	}
943
944	proc_kill(&env->vmd_ps);
945	free(env);
946
947	log_warnx("parent terminating");
948	exit(0);
949}
950
951struct vmd_vm *
952vm_getbyvmid(uint32_t vmid)
953{
954	struct vmd_vm	*vm;
955
956	if (vmid == 0)
957		return (NULL);
958	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
959		if (vm->vm_vmid == vmid)
960			return (vm);
961	}
962
963	return (NULL);
964}
965
966struct vmd_vm *
967vm_getbyid(uint32_t id)
968{
969	struct vmd_vm	*vm;
970
971	if (id == 0)
972		return (NULL);
973	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
974		if (vm->vm_params.vmc_params.vcp_id == id)
975			return (vm);
976	}
977
978	return (NULL);
979}
980
981uint32_t
982vm_id2vmid(uint32_t id, struct vmd_vm *vm)
983{
984	if (vm == NULL && (vm = vm_getbyid(id)) == NULL)
985		return (0);
986	dprintf("%s: vmm id %u is vmid %u", __func__,
987	    id, vm->vm_vmid);
988	return (vm->vm_vmid);
989}
990
991uint32_t
992vm_vmid2id(uint32_t vmid, struct vmd_vm *vm)
993{
994	if (vm == NULL && (vm = vm_getbyvmid(vmid)) == NULL)
995		return (0);
996	dprintf("%s: vmid %u is vmm id %u", __func__,
997	    vmid, vm->vm_params.vmc_params.vcp_id);
998	return (vm->vm_params.vmc_params.vcp_id);
999}
1000
1001struct vmd_vm *
1002vm_getbyname(const char *name)
1003{
1004	struct vmd_vm	*vm;
1005
1006	if (name == NULL)
1007		return (NULL);
1008	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
1009		if (strcmp(vm->vm_params.vmc_params.vcp_name, name) == 0)
1010			return (vm);
1011	}
1012
1013	return (NULL);
1014}
1015
1016struct vmd_vm *
1017vm_getbypid(pid_t pid)
1018{
1019	struct vmd_vm	*vm;
1020
1021	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
1022		if (vm->vm_pid == pid)
1023			return (vm);
1024	}
1025
1026	return (NULL);
1027}
1028
1029void
1030vm_stop(struct vmd_vm *vm, int keeptty)
1031{
1032	unsigned int	 i;
1033
1034	if (vm == NULL)
1035		return;
1036
1037	log_debug("%s: stopping vm %d", __func__, vm->vm_vmid);
1038	vm->vm_running = 0;
1039	vm->vm_shutdown = 0;
1040
1041	if (vm->vm_iev.ibuf.fd != -1) {
1042		event_del(&vm->vm_iev.ev);
1043		close(vm->vm_iev.ibuf.fd);
1044	}
1045	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) {
1046		if (vm->vm_disks[i] != -1) {
1047			close(vm->vm_disks[i]);
1048			vm->vm_disks[i] = -1;
1049		}
1050	}
1051	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
1052		if (vm->vm_ifs[i].vif_fd != -1) {
1053			close(vm->vm_ifs[i].vif_fd);
1054			vm->vm_ifs[i].vif_fd = -1;
1055		}
1056		free(vm->vm_ifs[i].vif_name);
1057		free(vm->vm_ifs[i].vif_switch);
1058		free(vm->vm_ifs[i].vif_group);
1059		vm->vm_ifs[i].vif_name = NULL;
1060		vm->vm_ifs[i].vif_switch = NULL;
1061		vm->vm_ifs[i].vif_group = NULL;
1062	}
1063	if (vm->vm_kernel != -1) {
1064		close(vm->vm_kernel);
1065		vm->vm_kernel = -1;
1066	}
1067	if (vm->vm_cdrom != -1) {
1068		close(vm->vm_cdrom);
1069		vm->vm_cdrom = -1;
1070	}
1071	if (!keeptty) {
1072		vm_closetty(vm);
1073		vm->vm_uid = 0;
1074	}
1075}
1076
1077void
1078vm_remove(struct vmd_vm *vm)
1079{
1080	if (vm == NULL)
1081		return;
1082
1083	log_debug("%s: removing vm id %d from running config",
1084	    __func__, vm->vm_vmid);
1085	TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
1086	log_debug("%s: calling vm_stop", __func__);
1087	vm_stop(vm, 0);
1088	free(vm);
1089}
1090
1091int
1092vm_register(struct privsep *ps, struct vmop_create_params *vmc,
1093    struct vmd_vm **ret_vm, uint32_t id, uid_t uid)
1094{
1095	struct vmd_vm		*vm = NULL;
1096	struct vm_create_params	*vcp = &vmc->vmc_params;
1097	static const uint8_t	 zero_mac[ETHER_ADDR_LEN];
1098	uint32_t		 rng;
1099	unsigned int		 i;
1100	struct vmd_switch	*sw;
1101	char			*s;
1102
1103	errno = 0;
1104	*ret_vm = NULL;
1105
1106	if ((vm = vm_getbyname(vcp->vcp_name)) != NULL ||
1107	    (vm = vm_getbyvmid(vcp->vcp_id)) != NULL) {
1108		if (vm_checkperm(vm, uid) != 0) {
1109			errno = EPERM;
1110			goto fail;
1111		}
1112		*ret_vm = vm;
1113		errno = EALREADY;
1114		goto fail;
1115	}
1116
1117	/*
1118	 * non-root users can only start existing VMs
1119	 * XXX there could be a mechanism to allow overriding some options
1120	 */
1121	if (vm_checkperm(NULL, uid) != 0) {
1122		errno = EPERM;
1123		goto fail;
1124	}
1125	if (vmc->vmc_flags == 0) {
1126		errno = ENOENT;
1127		goto fail;
1128	}
1129	if (vcp->vcp_ncpus == 0)
1130		vcp->vcp_ncpus = 1;
1131	if (vcp->vcp_memranges[0].vmr_size == 0)
1132		vcp->vcp_memranges[0].vmr_size = VM_DEFAULT_MEMORY;
1133	if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) {
1134		log_warnx("invalid number of CPUs");
1135		goto fail;
1136	} else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) {
1137		log_warnx("invalid number of disks");
1138		goto fail;
1139	} else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) {
1140		log_warnx("invalid number of interfaces");
1141		goto fail;
1142	} else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) {
1143		log_warnx("no kernel or disk specified");
1144		goto fail;
1145	} else if (strlen(vcp->vcp_name) == 0) {
1146		log_warnx("invalid VM name");
1147		goto fail;
1148	} else if (*vcp->vcp_name == '-' || *vcp->vcp_name == '.' ||
1149		   *vcp->vcp_name == '_') {
1150		log_warnx("Invalid VM name");
1151		goto fail;
1152	} else {
1153		for (s = vcp->vcp_name; *s != '\0'; ++s) {
1154			if (!(isalnum(*s) || *s == '.' || *s == '-' ||
1155			      *s == '_')) {
1156				log_warnx("Invalid VM name");
1157				goto fail;
1158			}
1159		}
1160	}
1161
1162	if ((vm = calloc(1, sizeof(*vm))) == NULL)
1163		goto fail;
1164
1165	memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params));
1166	vmc = &vm->vm_params;
1167	vcp = &vmc->vmc_params;
1168	vm->vm_pid = -1;
1169	vm->vm_tty = -1;
1170	vm->vm_receive_fd = -1;
1171	vm->vm_paused = 0;
1172
1173	for (i = 0; i < vcp->vcp_ndisks; i++)
1174		vm->vm_disks[i] = -1;
1175	for (i = 0; i < vcp->vcp_nnics; i++) {
1176		vm->vm_ifs[i].vif_fd = -1;
1177
1178		if ((sw = switch_getbyname(vmc->vmc_ifswitch[i])) != NULL) {
1179			/* inherit per-interface flags from the switch */
1180			vmc->vmc_ifflags[i] |= (sw->sw_flags & VMIFF_OPTMASK);
1181		}
1182
1183		/*
1184		 * If the MAC address is zero, always randomize it in vmd(8)
1185		 * because we cannot rely on the guest OS to do the right
1186		 * thing like OpenBSD does.  Based on ether_fakeaddr()
1187		 * from the kernel, incremented by one to differentiate
1188		 * the source.
1189		 */
1190		if (memcmp(zero_mac, &vcp->vcp_macs[i], ETHER_ADDR_LEN) == 0) {
1191			rng = arc4random();
1192			vcp->vcp_macs[i][0] = 0xfe;
1193			vcp->vcp_macs[i][1] = 0xe1;
1194			vcp->vcp_macs[i][2] = 0xba + 1;
1195			vcp->vcp_macs[i][3] = 0xd0 | ((i + 1) & 0xf);
1196			vcp->vcp_macs[i][4] = rng;
1197			vcp->vcp_macs[i][5] = rng >> 8;
1198		}
1199	}
1200	vm->vm_kernel = -1;
1201	vm->vm_cdrom = -1;
1202	vm->vm_iev.ibuf.fd = -1;
1203
1204	if (++env->vmd_nvm == 0)
1205		fatalx("too many vms");
1206
1207	/* Assign a new internal Id if not specified */
1208	vm->vm_vmid = id == 0 ? env->vmd_nvm : id;
1209
1210	log_debug("%s: registering vm %d", __func__, vm->vm_vmid);
1211	TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry);
1212
1213	*ret_vm = vm;
1214	return (0);
1215 fail:
1216	if (errno == 0)
1217		errno = EINVAL;
1218	return (-1);
1219}
1220
1221/*
1222 * vm_checkperm
1223 *
1224 * Checks if the user represented by the 'uid' parameter is allowed to
1225 * manipulate the VM described by the 'vm' parameter (or connect to said VM's
1226 * console.)
1227 *
1228 * Parameters:
1229 *  vm: the VM whose permission is to be checked
1230 *  uid: the user ID of the user making the request
1231 *
1232 * Return values:
1233 *   0: the permission should be granted
1234 *  -1: the permission check failed (also returned if vm == null)
1235 */
1236int
1237vm_checkperm(struct vmd_vm *vm, uid_t uid)
1238{
1239	struct group	*gr;
1240	struct passwd	*pw;
1241	char		**grmem;
1242
1243	/* root has no restrictions */
1244	if (uid == 0)
1245		return (0);
1246
1247	if (vm == NULL)
1248		return (-1);
1249
1250	/* check supplementary groups */
1251	if (vm->vm_params.vmc_gid != -1 &&
1252	    (pw = getpwuid(uid)) != NULL &&
1253	    (gr = getgrgid(vm->vm_params.vmc_gid)) != NULL) {
1254		for (grmem = gr->gr_mem; *grmem; grmem++)
1255			if (strcmp(*grmem, pw->pw_name) == 0)
1256				return (0);
1257	}
1258
1259	/* check user */
1260	if ((vm->vm_running && vm->vm_uid == uid) ||
1261	    (!vm->vm_running && vm->vm_params.vmc_uid == uid))
1262		return (0);
1263
1264	return (-1);
1265}
1266
1267int
1268vm_opentty(struct vmd_vm *vm)
1269{
1270	struct ptmget		 ptm;
1271	struct stat		 st;
1272	struct group		*gr;
1273	uid_t			 uid;
1274	gid_t			 gid;
1275	mode_t			 mode;
1276
1277	/*
1278	 * Open tty with pre-opened PTM fd
1279	 */
1280	if ((ioctl(env->vmd_ptmfd, PTMGET, &ptm) == -1))
1281		return (-1);
1282
1283	vm->vm_tty = ptm.cfd;
1284	close(ptm.sfd);
1285	if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL)
1286		goto fail;
1287
1288	uid = vm->vm_uid;
1289	gid = vm->vm_params.vmc_gid;
1290
1291	if (vm->vm_params.vmc_gid != -1) {
1292		mode = 0660;
1293	} else if ((gr = getgrnam("tty")) != NULL) {
1294		gid = gr->gr_gid;
1295		mode = 0620;
1296	} else {
1297		mode = 0600;
1298		gid = 0;
1299	}
1300
1301	log_debug("%s: vm %s tty %s uid %d gid %d mode %o",
1302	    __func__, vm->vm_params.vmc_params.vcp_name,
1303	    vm->vm_ttyname, uid, gid, mode);
1304
1305	/*
1306	 * Change ownership and mode of the tty as required.
1307	 * Loosely based on the implementation of sshpty.c
1308	 */
1309	if (stat(vm->vm_ttyname, &st) == -1)
1310		goto fail;
1311
1312	if (st.st_uid != uid || st.st_gid != gid) {
1313		if (chown(vm->vm_ttyname, uid, gid) == -1) {
1314			log_warn("chown %s %d %d failed, uid %d",
1315			    vm->vm_ttyname, uid, gid, getuid());
1316
1317			/* Ignore failure on read-only filesystems */
1318			if (!((errno == EROFS) &&
1319			    (st.st_uid == uid || st.st_uid == 0)))
1320				goto fail;
1321		}
1322	}
1323
1324	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
1325		if (chmod(vm->vm_ttyname, mode) == -1) {
1326			log_warn("chmod %s %o failed, uid %d",
1327			    vm->vm_ttyname, mode, getuid());
1328
1329			/* Ignore failure on read-only filesystems */
1330			if (!((errno == EROFS) &&
1331			    (st.st_uid == uid || st.st_uid == 0)))
1332				goto fail;
1333		}
1334	}
1335
1336	return (0);
1337 fail:
1338	vm_closetty(vm);
1339	return (-1);
1340}
1341
1342void
1343vm_closetty(struct vmd_vm *vm)
1344{
1345	if (vm->vm_tty != -1) {
1346		/* Release and close the tty */
1347		if (fchown(vm->vm_tty, 0, 0) == -1)
1348			log_warn("chown %s 0 0 failed", vm->vm_ttyname);
1349		if (fchmod(vm->vm_tty, 0666) == -1)
1350			log_warn("chmod %s 0666 failed", vm->vm_ttyname);
1351		close(vm->vm_tty);
1352		vm->vm_tty = -1;
1353	}
1354	free(vm->vm_ttyname);
1355	vm->vm_ttyname = NULL;
1356}
1357
1358void
1359switch_remove(struct vmd_switch *vsw)
1360{
1361	if (vsw == NULL)
1362		return;
1363
1364	TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry);
1365
1366	free(vsw->sw_group);
1367	free(vsw->sw_name);
1368	free(vsw);
1369}
1370
1371struct vmd_switch *
1372switch_getbyname(const char *name)
1373{
1374	struct vmd_switch	*vsw;
1375
1376	if (name == NULL)
1377		return (NULL);
1378	TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
1379		if (strcmp(vsw->sw_name, name) == 0)
1380			return (vsw);
1381	}
1382
1383	return (NULL);
1384}
1385
1386char *
1387get_string(uint8_t *ptr, size_t len)
1388{
1389	size_t	 i;
1390
1391	for (i = 0; i < len; i++)
1392		if (!isprint(ptr[i]))
1393			break;
1394
1395	return strndup(ptr, i);
1396}
1397
1398uint32_t
1399prefixlen2mask(uint8_t prefixlen)
1400{
1401	if (prefixlen == 0)
1402		return (0);
1403
1404	if (prefixlen > 32)
1405		prefixlen = 32;
1406
1407	return (htonl(0xffffffff << (32 - prefixlen)));
1408}
1409