xref: /openbsd/usr.sbin/vmd/config.c (revision e5dd7070)
1 /*	$OpenBSD: config.c,v 1.58 2019/05/11 19:55:14 jasper 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/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/uio.h>
23 #include <sys/stat.h>
24 #include <sys/socket.h>
25 
26 #include <net/if.h>
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <termios.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <util.h>
36 #include <errno.h>
37 #include <imsg.h>
38 
39 #include "proc.h"
40 #include "vmd.h"
41 
42 /* Supported bridge types */
43 const char *vmd_descsw[] = { "switch", "bridge", NULL };
44 
45 static int	 config_init_localprefix(struct vmd_config *);
46 
47 static int
48 config_init_localprefix(struct vmd_config *cfg)
49 {
50 	struct sockaddr_in6	*sin6;
51 
52 	if (host(VMD_DHCP_PREFIX, &cfg->cfg_localprefix) == -1)
53 		return (-1);
54 
55 	/* IPv6 is disabled by default */
56 	cfg->cfg_flags &= ~VMD_CFG_INET6;
57 
58 	/* Generate random IPv6 prefix only once */
59 	if (cfg->cfg_flags & VMD_CFG_AUTOINET6)
60 		return (0);
61 	if (host(VMD_ULA_PREFIX, &cfg->cfg_localprefix6) == -1)
62 		return (-1);
63 	/* Randomize the 56 bits "Global ID" and "Subnet ID" */
64 	sin6 = ss2sin6(&cfg->cfg_localprefix6.ss);
65 	arc4random_buf(&sin6->sin6_addr.s6_addr[1], 7);
66 	cfg->cfg_flags |= VMD_CFG_AUTOINET6;
67 
68 	return (0);
69 }
70 
71 int
72 config_init(struct vmd *env)
73 {
74 	struct privsep		*ps = &env->vmd_ps;
75 	unsigned int		 what;
76 
77 	/* Global configuration */
78 	ps->ps_what[PROC_PARENT] = CONFIG_ALL;
79 	ps->ps_what[PROC_VMM] = CONFIG_VMS;
80 
81 	/* Local prefix */
82 	if (config_init_localprefix(&env->vmd_cfg) == -1)
83 		return (-1);
84 
85 	/* Other configuration */
86 	what = ps->ps_what[privsep_process];
87 	if (what & CONFIG_VMS) {
88 		if ((env->vmd_vms = calloc(1, sizeof(*env->vmd_vms))) == NULL)
89 			return (-1);
90 		if ((env->vmd_known = calloc(1, sizeof(*env->vmd_known))) == NULL)
91 			return (-1);
92 		TAILQ_INIT(env->vmd_vms);
93 		TAILQ_INIT(env->vmd_known);
94 	}
95 	if (what & CONFIG_SWITCHES) {
96 		if ((env->vmd_switches = calloc(1,
97 		    sizeof(*env->vmd_switches))) == NULL)
98 			return (-1);
99 		TAILQ_INIT(env->vmd_switches);
100 	}
101 	if (what & CONFIG_USERS) {
102 		if ((env->vmd_users = calloc(1,
103 		    sizeof(*env->vmd_users))) == NULL)
104 			return (-1);
105 		TAILQ_INIT(env->vmd_users);
106 	}
107 
108 	return (0);
109 }
110 
111 void
112 config_purge(struct vmd *env, unsigned int reset)
113 {
114 	struct privsep		*ps = &env->vmd_ps;
115 	struct name2id		*n2i;
116 	struct vmd_vm		*vm;
117 	struct vmd_switch	*vsw;
118 	unsigned int		 what;
119 
120 	DPRINTF("%s: %s purging vms and switches",
121 	    __func__, ps->ps_title[privsep_process]);
122 
123 	/* Reset global configuration (prefix was verified before) */
124 	config_init_localprefix(&env->vmd_cfg);
125 
126 	/* Reset other configuration */
127 	what = ps->ps_what[privsep_process] & reset;
128 	if (what & CONFIG_VMS && env->vmd_vms != NULL) {
129 		while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) {
130 			vm_remove(vm, __func__);
131 		}
132 		while ((n2i = TAILQ_FIRST(env->vmd_known)) != NULL) {
133 			TAILQ_REMOVE(env->vmd_known, n2i, entry);
134 			free(n2i);
135 		}
136 		env->vmd_nvm = 0;
137 	}
138 	if (what & CONFIG_SWITCHES && env->vmd_switches != NULL) {
139 		while ((vsw = TAILQ_FIRST(env->vmd_switches)) != NULL)
140 			switch_remove(vsw);
141 		env->vmd_nswitches = 0;
142 	}
143 }
144 
145 int
146 config_setconfig(struct vmd *env)
147 {
148 	struct privsep	*ps = &env->vmd_ps;
149 	unsigned int	 id;
150 
151 	DPRINTF("%s: setting config", __func__);
152 
153 	for (id = 0; id < PROC_MAX; id++) {
154 		if (id == privsep_process)
155 			continue;
156 		proc_compose(ps, id, IMSG_VMDOP_CONFIG, &env->vmd_cfg,
157 		    sizeof(env->vmd_cfg));
158 	}
159 
160 	return (0);
161 }
162 
163 int
164 config_getconfig(struct vmd *env, struct imsg *imsg)
165 {
166 	struct privsep	*ps = &env->vmd_ps;
167 
168 	log_debug("%s: %s retrieving config",
169 	    __func__, ps->ps_title[privsep_process]);
170 
171 	IMSG_SIZE_CHECK(imsg, &env->vmd_cfg);
172 	memcpy(&env->vmd_cfg, imsg->data, sizeof(env->vmd_cfg));
173 
174 	return (0);
175 }
176 
177 int
178 config_setreset(struct vmd *env, unsigned int reset)
179 {
180 	struct privsep	*ps = &env->vmd_ps;
181 	unsigned int	 id;
182 
183 	DPRINTF("%s: resetting state", __func__);
184 
185 	for (id = 0; id < PROC_MAX; id++) {
186 		if ((reset & ps->ps_what[id]) == 0 ||
187 		    id == privsep_process)
188 			continue;
189 		proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset));
190 	}
191 
192 	return (0);
193 }
194 
195 int
196 config_getreset(struct vmd *env, struct imsg *imsg)
197 {
198 	unsigned int	 mode;
199 
200 	IMSG_SIZE_CHECK(imsg, &mode);
201 	memcpy(&mode, imsg->data, sizeof(mode));
202 
203 	log_debug("%s: %s resetting state",
204 	    __func__, env->vmd_ps.ps_title[privsep_process]);
205 
206 	config_purge(env, mode);
207 
208 	return (0);
209 }
210 
211 int
212 config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
213 {
214 	int diskfds[VMM_MAX_DISKS_PER_VM][VM_MAX_BASE_PER_DISK];
215 	struct vmd_if		*vif;
216 	struct vmop_create_params *vmc = &vm->vm_params;
217 	struct vm_create_params	*vcp = &vmc->vmc_params;
218 	unsigned int		 i, j;
219 	int			 fd = -1, vmboot = 0;
220 	int			 kernfd = -1;
221 	int			*tapfds = NULL;
222 	int			 cdromfd = -1;
223 	int			 saved_errno = 0;
224 	int			 n = 0, aflags, oflags;
225 	char			 ifname[IF_NAMESIZE], *s;
226 	char			 path[PATH_MAX];
227 	char			 base[PATH_MAX];
228 	unsigned int		 unit;
229 	struct timeval		 tv, rate, since_last;
230 
231 	errno = 0;
232 
233 	if (vm->vm_state & VM_STATE_RUNNING) {
234 		log_warnx("%s: vm is already running", __func__);
235 		errno = EALREADY;
236 		return (-1);
237 	}
238 
239 	/* increase the user reference counter and check user limits */
240 	if (vm->vm_user != NULL && user_get(vm->vm_user->usr_id.uid) != NULL) {
241 		user_inc(vcp, vm->vm_user, 1);
242 		if (user_checklimit(vm->vm_user, vcp) == -1) {
243 			errno = EPERM;
244 			goto fail;
245 		}
246 	}
247 
248 	/*
249 	 * Rate-limit the VM so that it cannot restart in a loop:
250 	 * if the VM restarts after less than VM_START_RATE_SEC seconds,
251 	 * we increment the limit counter.  After VM_START_RATE_LIMIT
252 	 * of suchs fast reboots the VM is stopped.
253 	 */
254 	getmonotime(&tv);
255 	if (vm->vm_start_tv.tv_sec) {
256 		timersub(&tv, &vm->vm_start_tv, &since_last);
257 
258 		rate.tv_sec = VM_START_RATE_SEC;
259 		rate.tv_usec = 0;
260 		if (timercmp(&since_last, &rate, <))
261 			vm->vm_start_limit++;
262 		else {
263 			/* Reset counter */
264 			vm->vm_start_limit = 0;
265 		}
266 
267 		log_debug("%s: vm %u restarted after %lld.%ld seconds,"
268 		    " limit %d/%d", __func__, vcp->vcp_id, since_last.tv_sec,
269 		    since_last.tv_usec, vm->vm_start_limit,
270 		    VM_START_RATE_LIMIT);
271 
272 		if (vm->vm_start_limit >= VM_START_RATE_LIMIT) {
273 			log_warnx("%s: vm %u restarted too quickly",
274 			    __func__, vcp->vcp_id);
275 			errno = EPERM;
276 			goto fail;
277 		}
278 	}
279 	vm->vm_start_tv = tv;
280 
281 	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++)
282 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
283 			diskfds[i][j] = -1;
284 
285 	tapfds = reallocarray(NULL, vcp->vcp_nnics, sizeof(*tapfds));
286 	if (tapfds == NULL) {
287 		log_warn("%s: can't allocate tap fds", __func__);
288 		goto fail;
289 	}
290 	for (i = 0; i < vcp->vcp_nnics; i++)
291 		tapfds[i] = -1;
292 
293 	vm->vm_peerid = peerid;
294 	vm->vm_uid = uid;
295 
296 	if (!(vm->vm_state & VM_STATE_RECEIVED)) {
297 		if (strlen(vcp->vcp_kernel)) {
298 			/*
299 			 * Boot kernel from disk image if path matches the
300 			 * root disk.
301 			 */
302 			if (vcp->vcp_ndisks &&
303 			    strcmp(vcp->vcp_kernel, vcp->vcp_disks[0]) == 0)
304 				vmboot = 1;
305 			/* Open external kernel for child */
306 			else if ((kernfd =
307 			    open(vcp->vcp_kernel, O_RDONLY)) == -1) {
308 				log_warn("%s: can't open kernel or BIOS "
309 				    "boot image %s", __func__, vcp->vcp_kernel);
310 				goto fail;
311 			}
312 		}
313 
314 		/*
315 		 * Try to open the default BIOS image if no kernel/BIOS has been
316 		 * specified.  The BIOS is an external firmware file that is
317 		 * typically distributed separately due to an incompatible
318 		 * license.
319 		 */
320 		if (kernfd == -1 && !vmboot &&
321 		    (kernfd = open(VM_DEFAULT_BIOS, O_RDONLY)) == -1) {
322 			log_warn("can't open %s", VM_DEFAULT_BIOS);
323 			errno = VMD_BIOS_MISSING;
324 			goto fail;
325 		}
326 
327 		if (!vmboot && vm_checkaccess(kernfd,
328 		    vmc->vmc_checkaccess & VMOP_CREATE_KERNEL,
329 		    uid, R_OK) == -1) {
330 			log_warnx("vm \"%s\" no read access to kernel %s",
331 			    vcp->vcp_name, vcp->vcp_kernel);
332 			errno = EPERM;
333 			goto fail;
334 		}
335 	}
336 
337 	/* Open CDROM image for child */
338 	if (strlen(vcp->vcp_cdrom)) {
339 		/* Stat cdrom to ensure it is a regular file */
340 		if ((cdromfd =
341 		    open(vcp->vcp_cdrom, O_RDONLY)) == -1) {
342 			log_warn("can't open cdrom %s", vcp->vcp_cdrom);
343 			errno = VMD_CDROM_MISSING;
344 			goto fail;
345 		}
346 
347 		if (vm_checkaccess(cdromfd,
348 		    vmc->vmc_checkaccess & VMOP_CREATE_CDROM,
349 		    uid, R_OK) == -1) {
350 			log_warnx("vm \"%s\" no read access to cdrom %s",
351 			    vcp->vcp_name, vcp->vcp_cdrom);
352 			errno = EPERM;
353 			goto fail;
354 		}
355 	}
356 
357 	/* Open disk images for child */
358 	for (i = 0 ; i < vcp->vcp_ndisks; i++) {
359 		if (strlcpy(path, vcp->vcp_disks[i], sizeof(path))
360 		   >= sizeof(path))
361 			log_warnx("disk path %s too long", vcp->vcp_disks[i]);
362 		memset(vmc->vmc_diskbases, 0, sizeof(vmc->vmc_diskbases));
363 		oflags = O_RDWR|O_EXLOCK|O_NONBLOCK;
364 		aflags = R_OK|W_OK;
365 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
366 			/* Stat disk[i] to ensure it is a regular file */
367 			if ((diskfds[i][j] = open(path, oflags)) == -1) {
368 				log_warn("can't open disk %s",
369 				    vcp->vcp_disks[i]);
370 				errno = VMD_DISK_MISSING;
371 				goto fail;
372 			}
373 
374 			if (vm_checkaccess(diskfds[i][j],
375 			    vmc->vmc_checkaccess & VMOP_CREATE_DISK,
376 			    uid, aflags) == -1) {
377 				log_warnx("vm \"%s\" unable to access "
378 				    "disk %s", vcp->vcp_name, path);
379 				errno = EPERM;
380 				goto fail;
381 			}
382 
383 			/*
384 			 * Clear the write and exclusive flags for base images.
385 			 * All writes should go to the top image, allowing them
386 			 * to be shared.
387 			 */
388 			oflags = O_RDONLY|O_NONBLOCK;
389 			aflags = R_OK;
390 			n = virtio_get_base(diskfds[i][j], base, sizeof(base),
391 			    vmc->vmc_disktypes[i], path);
392 			if (n == 0)
393 				break;
394 			if (n == -1) {
395 				log_warnx("vm \"%s\" unable to read "
396 				    "base %s for disk %s", vcp->vcp_name,
397 				    base, vcp->vcp_disks[i]);
398 				goto fail;
399 			}
400 			(void)strlcpy(path, base, sizeof(path));
401 		}
402 	}
403 
404 	/* Open network interfaces */
405 	for (i = 0 ; i < vcp->vcp_nnics; i++) {
406 		vif = &vm->vm_ifs[i];
407 
408 		/* Check if the user has requested a specific tap(4) */
409 		s = vmc->vmc_ifnames[i];
410 		if (*s != '\0' && strcmp("tap", s) != 0) {
411 			if (priv_getiftype(s, ifname, &unit) == -1 ||
412 			    strcmp(ifname, "tap") != 0) {
413 				log_warnx("%s: invalid tap name %s",
414 				    __func__, s);
415 				errno = EINVAL;
416 				goto fail;
417 			}
418 		} else
419 			s = NULL;
420 
421 		/*
422 		 * Either open the requested tap(4) device or get
423 		 * the next available one.
424 		 */
425 		if (s != NULL) {
426 			snprintf(path, PATH_MAX, "/dev/%s", s);
427 			tapfds[i] = open(path, O_RDWR | O_NONBLOCK);
428 		} else {
429 			tapfds[i] = opentap(ifname);
430 			s = ifname;
431 		}
432 		if (tapfds[i] == -1) {
433 			log_warn("%s: can't open tap %s", __func__, s);
434 			goto fail;
435 		}
436 		if ((vif->vif_name = strdup(s)) == NULL) {
437 			log_warn("%s: can't save tap %s", __func__, s);
438 			goto fail;
439 		}
440 
441 		/* Check if the the interface is attached to a switch */
442 		s = vmc->vmc_ifswitch[i];
443 		if (*s != '\0') {
444 			if ((vif->vif_switch = strdup(s)) == NULL) {
445 				log_warn("%s: can't save switch %s",
446 				    __func__, s);
447 				goto fail;
448 			}
449 		}
450 
451 		/* Check if the the interface is assigned to a group */
452 		s = vmc->vmc_ifgroup[i];
453 		if (*s != '\0') {
454 			if ((vif->vif_group = strdup(s)) == NULL) {
455 				log_warn("%s: can't save group %s",
456 				    __func__, s);
457 				goto fail;
458 			}
459 		}
460 
461 		/* non-default rdomain (requires VMIFF_RDOMAIN below) */
462 		vif->vif_rdomain = vmc->vmc_ifrdomain[i];
463 
464 		/* Set the interface status */
465 		vif->vif_flags =
466 		    vmc->vmc_ifflags[i] & (VMIFF_UP|VMIFF_OPTMASK);
467 	}
468 
469 	/* Open TTY */
470 	if (vm->vm_ttyname == NULL) {
471 		if (vm_opentty(vm) == -1) {
472 			log_warn("%s: can't open tty %s", __func__,
473 			    vm->vm_ttyname == NULL ? "" : vm->vm_ttyname);
474 			goto fail;
475 		}
476 	}
477 	if ((fd = dup(vm->vm_tty)) == -1) {
478 		log_warn("%s: can't re-open tty %s", __func__, vm->vm_ttyname);
479 		goto fail;
480 	}
481 
482 	/* Send VM information */
483 	if (vm->vm_state & VM_STATE_RECEIVED)
484 		proc_compose_imsg(ps, PROC_VMM, -1,
485 		    IMSG_VMDOP_RECEIVE_VM_REQUEST, vm->vm_vmid, fd,  vmc,
486 		    sizeof(struct vmop_create_params));
487 	else
488 		proc_compose_imsg(ps, PROC_VMM, -1,
489 		    IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd,
490 		    vmc, sizeof(*vmc));
491 
492 	if (strlen(vcp->vcp_cdrom))
493 		proc_compose_imsg(ps, PROC_VMM, -1,
494 		    IMSG_VMDOP_START_VM_CDROM, vm->vm_vmid, cdromfd,
495 		    NULL, 0);
496 
497 	for (i = 0; i < vcp->vcp_ndisks; i++) {
498 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
499 			if (diskfds[i][j] == -1)
500 				break;
501 			proc_compose_imsg(ps, PROC_VMM, -1,
502 			    IMSG_VMDOP_START_VM_DISK, vm->vm_vmid,
503 			    diskfds[i][j], &i, sizeof(i));
504 		}
505 	}
506 	for (i = 0; i < vcp->vcp_nnics; i++) {
507 		proc_compose_imsg(ps, PROC_VMM, -1,
508 		    IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i],
509 		    &i, sizeof(i));
510 	}
511 
512 	if (!(vm->vm_state & VM_STATE_RECEIVED))
513 		proc_compose_imsg(ps, PROC_VMM, -1,
514 		    IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd,  NULL, 0);
515 
516 	free(tapfds);
517 
518 	vm->vm_state |= VM_STATE_RUNNING;
519 	return (0);
520 
521  fail:
522 	saved_errno = errno;
523 	log_warnx("failed to start vm %s", vcp->vcp_name);
524 
525 	if (kernfd != -1)
526 		close(kernfd);
527 	if (cdromfd != -1)
528 		close(cdromfd);
529 	for (i = 0; i < vcp->vcp_ndisks; i++)
530 		for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
531 			if (diskfds[i][j] != -1)
532 				close(diskfds[i][j]);
533 	if (tapfds != NULL) {
534 		for (i = 0; i < vcp->vcp_nnics; i++)
535 			close(tapfds[i]);
536 		free(tapfds);
537 	}
538 
539 	if (vm->vm_from_config) {
540 		vm_stop(vm, 0, __func__);
541 	} else {
542 		vm_remove(vm, __func__);
543 	}
544 	errno = saved_errno;
545 	if (errno == 0)
546 		errno = EINVAL;
547 	return (-1);
548 }
549 
550 int
551 config_getvm(struct privsep *ps, struct imsg *imsg)
552 {
553 	struct vmop_create_params	 vmc;
554 	struct vmd_vm			*vm;
555 
556 	IMSG_SIZE_CHECK(imsg, &vmc);
557 	memcpy(&vmc, imsg->data, sizeof(vmc));
558 
559 	errno = 0;
560 	if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid, 0) == -1)
561 		goto fail;
562 
563 	/* If the fd is -1, the kernel will be searched on the disk */
564 	vm->vm_kernel = imsg->fd;
565 	vm->vm_state |= VM_STATE_RUNNING;
566 	vm->vm_peerid = (uint32_t)-1;
567 
568 	return (0);
569 
570  fail:
571 	if (imsg->fd != -1) {
572 		close(imsg->fd);
573 		imsg->fd = -1;
574 	}
575 
576 	vm_remove(vm, __func__);
577 	if (errno == 0)
578 		errno = EINVAL;
579 
580 	return (-1);
581 }
582 
583 int
584 config_getdisk(struct privsep *ps, struct imsg *imsg)
585 {
586 	struct vmd_vm	*vm;
587 	unsigned int	 n, idx;
588 
589 	errno = 0;
590 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
591 		errno = ENOENT;
592 		return (-1);
593 	}
594 
595 	IMSG_SIZE_CHECK(imsg, &n);
596 	memcpy(&n, imsg->data, sizeof(n));
597 
598 	if (n >= vm->vm_params.vmc_params.vcp_ndisks || imsg->fd == -1) {
599 		log_warnx("invalid disk id");
600 		errno = EINVAL;
601 		return (-1);
602 	}
603 	idx = vm->vm_params.vmc_diskbases[n]++;
604 	if (idx >= VM_MAX_BASE_PER_DISK) {
605 		log_warnx("too many bases for disk");
606 		errno = EINVAL;
607 		return (-1);
608 	}
609 	vm->vm_disks[n][idx] = imsg->fd;
610 	return (0);
611 }
612 
613 int
614 config_getif(struct privsep *ps, struct imsg *imsg)
615 {
616 	struct vmd_vm	*vm;
617 	unsigned int	 n;
618 
619 	errno = 0;
620 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
621 		errno = ENOENT;
622 		return (-1);
623 	}
624 
625 	IMSG_SIZE_CHECK(imsg, &n);
626 	memcpy(&n, imsg->data, sizeof(n));
627 	if (n >= vm->vm_params.vmc_params.vcp_nnics ||
628 	    vm->vm_ifs[n].vif_fd != -1 || imsg->fd == -1) {
629 		log_warnx("invalid interface id");
630 		goto fail;
631 	}
632 	vm->vm_ifs[n].vif_fd = imsg->fd;
633 	return (0);
634  fail:
635 	if (imsg->fd != -1)
636 		close(imsg->fd);
637 	errno = EINVAL;
638 	return (-1);
639 }
640 
641 int
642 config_getcdrom(struct privsep *ps, struct imsg *imsg)
643 {
644 	struct vmd_vm	*vm;
645 
646 	errno = 0;
647 	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
648 		errno = ENOENT;
649 		return (-1);
650 	}
651 
652 	if (imsg->fd == -1) {
653 		log_warnx("invalid cdrom id");
654 		goto fail;
655 	}
656 
657 	vm->vm_cdrom = imsg->fd;
658 	return (0);
659  fail:
660 	if (imsg->fd != -1)
661 		close(imsg->fd);
662 	errno = EINVAL;
663 	return (-1);
664 }
665