GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/vmm.c Lines: 0 315 0.0 %
Date: 2017-11-07 Branches: 0 193 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: vmm.c,v 1.77 2017/09/15 02:36:29 mlarkin Exp $	*/
2
3
/*
4
 * Copyright (c) 2015 Mike Larkin <mlarkin@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/ioctl.h>
21
#include <sys/queue.h>
22
#include <sys/wait.h>
23
#include <sys/uio.h>
24
#include <sys/socket.h>
25
#include <sys/time.h>
26
#include <sys/mman.h>
27
28
#include <dev/ic/i8253reg.h>
29
#include <dev/isa/isareg.h>
30
#include <dev/pci/pcireg.h>
31
32
#include <machine/param.h>
33
#include <machine/psl.h>
34
#include <machine/specialreg.h>
35
#include <machine/vmmvar.h>
36
37
#include <net/if.h>
38
39
#include <errno.h>
40
#include <event.h>
41
#include <fcntl.h>
42
#include <imsg.h>
43
#include <limits.h>
44
#include <poll.h>
45
#include <pthread.h>
46
#include <stddef.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <unistd.h>
51
#include <util.h>
52
53
#include "vmd.h"
54
#include "vmm.h"
55
56
void vmm_sighdlr(int, short, void *);
57
int vmm_start_vm(struct imsg *, uint32_t *);
58
int vmm_receive_vm(struct vmd_vm * , int);
59
int vmm_dispatch_parent(int, struct privsep_proc *, struct imsg *);
60
void vmm_run(struct privsep *, struct privsep_proc *, void *);
61
void vmm_dispatch_vm(int, short, void *);
62
int terminate_vm(struct vm_terminate_params *);
63
int get_info_vm(struct privsep *, struct imsg *, int);
64
int opentap(char *);
65
66
extern struct vmd *env;
67
68
static struct privsep_proc procs[] = {
69
	{ "parent",	PROC_PARENT,	vmm_dispatch_parent  },
70
};
71
72
void
73
vmm(struct privsep *ps, struct privsep_proc *p)
74
{
75
	proc_run(ps, p, procs, nitems(procs), vmm_run, NULL);
76
}
77
78
void
79
vmm_run(struct privsep *ps, struct privsep_proc *p, void *arg)
80
{
81
	if (config_init(ps->ps_env) == -1)
82
		fatal("failed to initialize configuration");
83
84
	signal_del(&ps->ps_evsigchld);
85
	signal_set(&ps->ps_evsigchld, SIGCHLD, vmm_sighdlr, ps);
86
	signal_add(&ps->ps_evsigchld, NULL);
87
88
	/*
89
	 * pledge in the vmm process:
90
	 * stdio - for malloc and basic I/O including events.
91
	 * vmm - for the vmm ioctls and operations.
92
	 * proc - for forking and maitaining vms.
93
	 * send - for sending send/recv fds to vm proc.
94
	 * recvfd - for disks, interfaces and other fds.
95
	 */
96
	if (pledge("stdio vmm sendfd recvfd proc flock rpath cpath wpath", NULL) == -1)
97
		fatal("pledge");
98
99
	/* Get and terminate all running VMs */
100
	get_info_vm(ps, NULL, 1);
101
}
102
103
int
104
vmm_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
105
{
106
	struct privsep		*ps = p->p_ps;
107
	int			 res = 0, cmd = 0, verbose, ret;
108
	struct vmd_vm		*vm = NULL;
109
	struct vm_terminate_params vtp;
110
	struct vmop_id		 vid;
111
	struct vmop_result	 vmr;
112
	struct vmop_create_params vmc;
113
	uint32_t		 id = 0;
114
	unsigned int		 mode;
115
116
	switch (imsg->hdr.type) {
117
	case IMSG_VMDOP_START_VM_REQUEST:
118
		res = config_getvm(ps, imsg);
119
		if (res == -1) {
120
			res = errno;
121
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
122
		}
123
		break;
124
	case IMSG_VMDOP_START_VM_DISK:
125
		res = config_getdisk(ps, imsg);
126
		if (res == -1) {
127
			res = errno;
128
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
129
		}
130
		break;
131
	case IMSG_VMDOP_START_VM_IF:
132
		res = config_getif(ps, imsg);
133
		if (res == -1) {
134
			res = errno;
135
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
136
		}
137
		break;
138
	case IMSG_VMDOP_START_VM_END:
139
		res = vmm_start_vm(imsg, &id);
140
		/* Check if the ID can be mapped correctly */
141
		if ((id = vm_id2vmid(id, NULL)) == 0)
142
			res = ENOENT;
143
		cmd = IMSG_VMDOP_START_VM_RESPONSE;
144
		break;
145
	case IMSG_VMDOP_TERMINATE_VM_REQUEST:
146
		IMSG_SIZE_CHECK(imsg, &vtp);
147
		memcpy(&vtp, imsg->data, sizeof(vtp));
148
		id = vtp.vtp_vm_id;
149
		log_debug("%s: recv'ed TERMINATE_VM for %d", __func__, id);
150
151
		if (id == 0) {
152
			res = ENOENT;
153
		} else if ((vm = vm_getbyvmid(id)) != NULL) {
154
			if (vm->vm_shutdown == 0) {
155
				log_debug("%s: sending shutdown req to vm %d",
156
				    __func__, id);
157
158
				/*
159
				 * Request reboot but mark the VM as shutting
160
				 * down. This way we can terminate the VM after
161
				 * the triple fault instead of reboot and
162
				 * avoid being stuck in the ACPI-less powerdown
163
				 * ("press any key to reboot") of the VM.
164
				 */
165
				vm->vm_shutdown = 1;
166
				if (imsg_compose_event(&vm->vm_iev,
167
				    IMSG_VMDOP_VM_REBOOT,
168
				    0, 0, -1, NULL, 0) == -1)
169
					res = errno;
170
				else
171
					res = 0;
172
			} else {
173
				/* in the process of shutting down... */
174
				log_debug("%s: performing a forced shutdown",
175
				    __func__);
176
				vtp.vtp_vm_id = vm_vmid2id(vm->vm_vmid, vm);
177
				/* ensure vm_id isn't 0 */
178
				if (vtp.vtp_vm_id != 0) {
179
					res = terminate_vm(&vtp);
180
					vm_remove(vm);
181
				} else {
182
					log_debug("%s: no vm running anymore",
183
					    __func__);
184
					res = VMD_VM_STOP_INVALID;
185
				}
186
			}
187
		} else {
188
			/* vm doesn't exist, cannot stop vm */
189
			log_debug("%s: cannot stop vm that is not running",
190
			    __func__);
191
			res = VMD_VM_STOP_INVALID;
192
		}
193
		cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
194
		break;
195
	case IMSG_VMDOP_GET_INFO_VM_REQUEST:
196
		res = get_info_vm(ps, imsg, 0);
197
		cmd = IMSG_VMDOP_GET_INFO_VM_END_DATA;
198
		break;
199
	case IMSG_VMDOP_CONFIG:
200
		config_getconfig(env, imsg);
201
		break;
202
	case IMSG_CTL_RESET:
203
		IMSG_SIZE_CHECK(imsg, &mode);
204
		memcpy(&mode, imsg->data, sizeof(mode));
205
206
		if (mode & CONFIG_VMS) {
207
			/* Terminate and remove all VMs */
208
			vmm_shutdown();
209
			mode &= ~CONFIG_VMS;
210
		}
211
212
		config_getreset(env, imsg);
213
		break;
214
	case IMSG_CTL_VERBOSE:
215
		IMSG_SIZE_CHECK(imsg, &verbose);
216
		memcpy(&verbose, imsg->data, sizeof(verbose));
217
		log_setverbose(verbose);
218
219
		/* Forward message to each VM process */
220
		TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
221
			imsg_compose_event(&vm->vm_iev,
222
			    imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
223
			    -1, &verbose, sizeof(verbose));
224
		}
225
		break;
226
	case IMSG_VMDOP_PAUSE_VM:
227
		IMSG_SIZE_CHECK(imsg, &vid);
228
		memcpy(&vid, imsg->data, sizeof(vid));
229
		id = vid.vid_id;
230
		vm = vm_getbyvmid(id);
231
		if ((vm = vm_getbyvmid(id)) == NULL) {
232
			res = ENOENT;
233
			cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE;
234
			break;
235
		}
236
		imsg_compose_event(&vm->vm_iev,
237
		    imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
238
		    imsg->fd, &vid, sizeof(vid));
239
		break;
240
	case IMSG_VMDOP_UNPAUSE_VM:
241
		IMSG_SIZE_CHECK(imsg, &vid);
242
		memcpy(&vid, imsg->data, sizeof(vid));
243
		id = vid.vid_id;
244
		if ((vm = vm_getbyvmid(id)) == NULL) {
245
			res = ENOENT;
246
			cmd = IMSG_VMDOP_UNPAUSE_VM_RESPONSE;
247
			break;
248
		}
249
		imsg_compose_event(&vm->vm_iev,
250
		    imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
251
		    imsg->fd, &vid, sizeof(vid));
252
		break;
253
	case IMSG_VMDOP_SEND_VM_REQUEST:
254
		IMSG_SIZE_CHECK(imsg, &vid);
255
		memcpy(&vid, imsg->data, sizeof(vid));
256
		id = vid.vid_id;
257
		if ((vm = vm_getbyvmid(id)) == NULL) {
258
			res = ENOENT;
259
			close(imsg->fd);
260
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
261
			break;
262
		}
263
		imsg_compose_event(&vm->vm_iev,
264
		    imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
265
		    imsg->fd, &vid, sizeof(vid));
266
		break;
267
	case IMSG_VMDOP_RECEIVE_VM_REQUEST:
268
		IMSG_SIZE_CHECK(imsg, &vmc);
269
		memcpy(&vmc, imsg->data, sizeof(vmc));
270
		ret = vm_register(ps, &vmc, &vm, imsg->hdr.peerid, vmc.vmc_uid);
271
		vm->vm_tty = imsg->fd;
272
		vm->vm_received = 1;
273
		break;
274
	case IMSG_VMDOP_RECEIVE_VM_END:
275
		if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
276
			res = ENOENT;
277
			close(imsg->fd);
278
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
279
			break;
280
		}
281
		vm->vm_receive_fd = imsg->fd;
282
		res = vmm_start_vm(imsg, &id);
283
		/* Check if the ID can be mapped correctly */
284
		if ((id = vm_id2vmid(id, NULL)) == 0)
285
			res = ENOENT;
286
		cmd = IMSG_VMDOP_START_VM_RESPONSE;
287
		break;
288
	default:
289
		return (-1);
290
	}
291
292
	switch (cmd) {
293
	case 0:
294
		break;
295
	case IMSG_VMDOP_START_VM_RESPONSE:
296
		if (res != 0) {
297
			/* Remove local reference if it exists */
298
			if ((vm = vm_getbyvmid(imsg->hdr.peerid)) != NULL) {
299
				log_debug("%s: removing vm, START_VM_RESPONSE",
300
				    __func__);
301
				vm_remove(vm);
302
			}
303
		}
304
		if (id == 0)
305
			id = imsg->hdr.peerid;
306
	case IMSG_VMDOP_PAUSE_VM_RESPONSE:
307
	case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
308
	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
309
		memset(&vmr, 0, sizeof(vmr));
310
		vmr.vmr_result = res;
311
		vmr.vmr_id = id;
312
		if (proc_compose_imsg(ps, PROC_PARENT, -1, cmd,
313
		    imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1)
314
			return (-1);
315
		break;
316
	default:
317
		if (proc_compose_imsg(ps, PROC_PARENT, -1, cmd,
318
		    imsg->hdr.peerid, -1, &res, sizeof(res)) == -1)
319
			return (-1);
320
		break;
321
	}
322
323
	return (0);
324
}
325
326
void
327
vmm_sighdlr(int sig, short event, void *arg)
328
{
329
	struct privsep *ps = arg;
330
	int status, ret = 0;
331
	uint32_t vmid;
332
	pid_t pid;
333
	struct vmop_result vmr;
334
	struct vmd_vm *vm;
335
	struct vm_terminate_params vtp;
336
337
	log_debug("%s: handling signal %d", __func__, sig);
338
	switch (sig) {
339
	case SIGCHLD:
340
		do {
341
			pid = waitpid(-1, &status, WNOHANG);
342
			if (pid <= 0)
343
				continue;
344
345
			if (WIFEXITED(status) || WIFSIGNALED(status)) {
346
				vm = vm_getbypid(pid);
347
				if (vm == NULL) {
348
					/*
349
					 * If the VM is gone already, it
350
					 * got terminated via a
351
					 * IMSG_VMDOP_TERMINATE_VM_REQUEST.
352
					 */
353
					continue;
354
				}
355
356
				if (WIFEXITED(status))
357
					ret = WEXITSTATUS(status);
358
359
				/* don't reboot on pending shutdown */
360
				if (ret == EAGAIN && vm->vm_shutdown)
361
					ret = 0;
362
363
				vmid = vm->vm_params.vmc_params.vcp_id;
364
				vtp.vtp_vm_id = vmid;
365
				log_debug("%s: attempting to terminate vm %d",
366
				    __func__, vm->vm_vmid);
367
				if (terminate_vm(&vtp) == 0) {
368
					memset(&vmr, 0, sizeof(vmr));
369
					vmr.vmr_result = ret;
370
					vmr.vmr_id = vm_id2vmid(vmid, vm);
371
					if (proc_compose_imsg(ps, PROC_PARENT,
372
					    -1, IMSG_VMDOP_TERMINATE_VM_EVENT,
373
					    0, -1, &vmr, sizeof(vmr)) == -1)
374
						log_warnx("could not signal "
375
						    "termination of VM %u to "
376
						    "parent", vm->vm_vmid);
377
				} else
378
					log_warnx("could not terminate VM %u",
379
					    vm->vm_vmid);
380
381
				log_debug("%s: calling vm_remove", __func__);
382
				vm_remove(vm);
383
			} else
384
				fatalx("unexpected cause of SIGCHLD");
385
		} while (pid > 0 || (pid == -1 && errno == EINTR));
386
		break;
387
	default:
388
		fatalx("unexpected signal");
389
	}
390
}
391
392
/*
393
 * vmm_shutdown
394
 *
395
 * Terminate VMs on shutdown to avoid "zombie VM" processes.
396
 */
397
void
398
vmm_shutdown(void)
399
{
400
	struct vm_terminate_params vtp;
401
	struct vmd_vm *vm, *vm_next;
402
403
	TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) {
404
		vtp.vtp_vm_id = vm_vmid2id(vm->vm_vmid, vm);
405
406
		/* XXX suspend or request graceful shutdown */
407
		(void)terminate_vm(&vtp);
408
		log_debug("%s: calling vm_remove", __func__);
409
		vm_remove(vm);
410
	}
411
}
412
413
/*
414
 * vmm_pipe
415
 *
416
 * Create a new imsg control channel between vmm parent and a VM
417
 * (can be called on both sides).
418
 */
419
int
420
vmm_pipe(struct vmd_vm *vm, int fd, void (*cb)(int, short, void *))
421
{
422
	struct imsgev	*iev = &vm->vm_iev;
423
424
	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
425
		log_warn("failed to set nonblocking mode on vm pipe");
426
		return (-1);
427
	}
428
429
	imsg_init(&iev->ibuf, fd);
430
	iev->handler = cb;
431
	iev->data = vm;
432
	imsg_event_add(iev);
433
434
	return (0);
435
}
436
437
/*
438
 * vmm_dispatch_vm
439
 *
440
 * imsg callback for messages that are received from a VM child process.
441
 */
442
void
443
vmm_dispatch_vm(int fd, short event, void *arg)
444
{
445
	struct vmd_vm		*vm = arg;
446
	struct vmop_result	 vmr;
447
	struct imsgev		*iev = &vm->vm_iev;
448
	struct imsgbuf		*ibuf = &iev->ibuf;
449
	struct imsg		 imsg;
450
	ssize_t			 n;
451
	unsigned int		 i;
452
453
	if (event & EV_READ) {
454
		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
455
			fatal("%s: imsg_read", __func__);
456
		if (n == 0) {
457
			/* this pipe is dead, so remove the event handler */
458
			event_del(&iev->ev);
459
			return;
460
		}
461
	}
462
463
	if (event & EV_WRITE) {
464
		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
465
			fatal("%s: msgbuf_write fd %d", __func__, ibuf->fd);
466
		if (n == 0) {
467
			/* this pipe is dead, so remove the event handler */
468
			event_del(&iev->ev);
469
			return;
470
		}
471
	}
472
473
	for (;;) {
474
		if ((n = imsg_get(ibuf, &imsg)) == -1)
475
			fatal("%s: imsg_get", __func__);
476
		if (n == 0)
477
			break;
478
479
		dprintf("%s: got imsg %d from %s",
480
		    __func__, imsg.hdr.type,
481
		    vm->vm_params.vmc_params.vcp_name);
482
483
		switch (imsg.hdr.type) {
484
		case IMSG_VMDOP_VM_SHUTDOWN:
485
			vm->vm_shutdown = 1;
486
			break;
487
		case IMSG_VMDOP_VM_REBOOT:
488
			vm->vm_shutdown = 0;
489
			break;
490
		case IMSG_VMDOP_SEND_VM_RESPONSE:
491
			IMSG_SIZE_CHECK(&imsg, &vmr);
492
			memcpy(&vmr, imsg.data, sizeof(vmr));
493
			if (!vmr.vmr_result) {
494
				log_debug("%s: calling vm_remove", __func__);
495
				vm_remove(vm);
496
			}
497
		case IMSG_VMDOP_PAUSE_VM_RESPONSE:
498
		case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
499
			for (i = 0; i < sizeof(procs); i++) {
500
				if (procs[i].p_id == PROC_PARENT) {
501
					proc_forward_imsg(procs[i].p_ps,
502
					    &imsg, PROC_PARENT, -1);
503
					break;
504
				}
505
			}
506
			break;
507
508
		default:
509
			fatalx("%s: got invalid imsg %d from %s",
510
			    __func__, imsg.hdr.type,
511
			    vm->vm_params.vmc_params.vcp_name);
512
		}
513
		imsg_free(&imsg);
514
	}
515
	imsg_event_add(iev);
516
}
517
518
/*
519
 * terminate_vm
520
 *
521
 * Requests vmm(4) to terminate the VM whose ID is provided in the
522
 * supplied vm_terminate_params structure (vtp->vtp_vm_id)
523
 *
524
 * Parameters
525
 *  vtp: vm_terminate_params struct containing the ID of the VM to terminate
526
 *
527
 * Return values:
528
 *  0: success
529
 *  !0 : ioctl to vmm(4) failed (eg, ENOENT if the supplied VM is not
530
 *      valid)
531
 */
532
int
533
terminate_vm(struct vm_terminate_params *vtp)
534
{
535
	log_debug("%s: terminating vmid %d", __func__, vtp->vtp_vm_id);
536
	if (ioctl(env->vmd_fd, VMM_IOC_TERM, vtp) < 0)
537
		return (errno);
538
539
	return (0);
540
}
541
542
/*
543
 * opentap
544
 *
545
 * Opens the next available tap device, up to MAX_TAP.
546
 *
547
 * Parameters
548
 *  ifname: an optional buffer of at least IF_NAMESIZE bytes.
549
 *
550
 * Returns a file descriptor to the tap node opened, or -1 if no tap
551
 * devices were available.
552
 */
553
int
554
opentap(char *ifname)
555
{
556
	int i, fd;
557
	char path[PATH_MAX];
558
559
	strlcpy(ifname, "tap", IF_NAMESIZE);
560
	for (i = 0; i < MAX_TAP; i++) {
561
		snprintf(path, PATH_MAX, "/dev/tap%d", i);
562
		fd = open(path, O_RDWR | O_NONBLOCK);
563
		if (fd != -1) {
564
			if (ifname != NULL)
565
				snprintf(ifname, IF_NAMESIZE, "tap%d", i);
566
			return (fd);
567
		}
568
	}
569
570
	return (-1);
571
}
572
573
/*
574
 * vmm_start_vm
575
 *
576
 * Prepares and forks a new VM process.
577
 *
578
 * Parameters:
579
 *  imsg: The VM data structure that is including the VM create parameters.
580
 *  id: Returns the VM id as reported by the kernel and obtained from the VM.
581
 *
582
 * Return values:
583
 *  0: success
584
 *  !0 : failure - typically an errno indicating the source of the failure
585
 */
586
int
587
vmm_start_vm(struct imsg *imsg, uint32_t *id)
588
{
589
	struct vm_create_params	*vcp;
590
	struct vmd_vm		*vm;
591
	int			 ret = EINVAL;
592
	int			 fds[2];
593
	size_t			 i;
594
595
	if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
596
		log_warnx("%s: can't find vm", __func__);
597
		ret = ENOENT;
598
		goto err;
599
	}
600
	vcp = &vm->vm_params.vmc_params;
601
602
	if (!vm->vm_received) {
603
		if ((vm->vm_tty = imsg->fd) == -1) {
604
			log_warnx("%s: can't get tty", __func__);
605
			goto err;
606
		}
607
	}
608
609
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1)
610
		fatal("socketpair");
611
612
	/* Start child vmd for this VM (fork, chroot, drop privs) */
613
	ret = fork();
614
615
	/* Start child failed? - cleanup and leave */
616
	if (ret == -1) {
617
		log_warnx("%s: start child failed", __func__);
618
		ret = EIO;
619
		goto err;
620
	}
621
622
	if (ret > 0) {
623
		/* Parent */
624
		vm->vm_pid = ret;
625
		close(fds[1]);
626
627
		for (i = 0 ; i < vcp->vcp_ndisks; i++) {
628
			close(vm->vm_disks[i]);
629
			vm->vm_disks[i] = -1;
630
		}
631
632
		for (i = 0 ; i < vcp->vcp_nnics; i++) {
633
			close(vm->vm_ifs[i].vif_fd);
634
			vm->vm_ifs[i].vif_fd = -1;
635
		}
636
637
		close(vm->vm_kernel);
638
		vm->vm_kernel = -1;
639
640
		close(vm->vm_tty);
641
		vm->vm_tty = -1;
642
643
		/* read back the kernel-generated vm id from the child */
644
		if (read(fds[0], &vcp->vcp_id, sizeof(vcp->vcp_id)) !=
645
		    sizeof(vcp->vcp_id))
646
			fatal("read vcp id");
647
648
		if (vcp->vcp_id == 0)
649
			goto err;
650
651
		*id = vcp->vcp_id;
652
653
		if (vmm_pipe(vm, fds[0], vmm_dispatch_vm) == -1)
654
			fatal("setup vm pipe");
655
656
		return (0);
657
	} else {
658
		/* Child */
659
		close(fds[0]);
660
661
		ret = start_vm(vm, fds[1]);
662
663
		_exit(ret);
664
	}
665
666
	return (0);
667
668
 err:
669
	log_debug("%s: calling vm_remove", __func__);
670
	vm_remove(vm);
671
672
	return (ret);
673
}
674
675
/*
676
 * get_info_vm
677
 *
678
 * Returns a list of VMs known to vmm(4).
679
 *
680
 * Parameters:
681
 *  ps: the privsep context.
682
 *  imsg: the received imsg including the peer id.
683
 *  terminate: terminate the listed vm.
684
 *
685
 * Return values:
686
 *  0: success
687
 *  !0 : failure (eg, ENOMEM, EIO or another error code from vmm(4) ioctl)
688
 */
689
int
690
get_info_vm(struct privsep *ps, struct imsg *imsg, int terminate)
691
{
692
	int ret;
693
	size_t ct, i;
694
	struct vm_info_params vip;
695
	struct vm_info_result *info;
696
	struct vm_terminate_params vtp;
697
	struct vmop_info_result vir;
698
699
	/*
700
	 * We issue the VMM_IOC_INFO ioctl twice, once with an input
701
	 * buffer size of 0, which results in vmm(4) returning the
702
	 * number of bytes required back to us in vip.vip_size,
703
	 * and then we call it again after malloc'ing the required
704
	 * number of bytes.
705
	 *
706
	 * It is possible that we could fail a second time (eg, if
707
	 * another VM was created in the instant between the two
708
	 * ioctls, but in that case the caller can just try again
709
	 * as vmm(4) will return a zero-sized list in that case.
710
	 */
711
	vip.vip_size = 0;
712
	info = NULL;
713
	ret = 0;
714
	memset(&vir, 0, sizeof(vir));
715
716
	/* First ioctl to see how many bytes needed (vip.vip_size) */
717
	if (ioctl(env->vmd_fd, VMM_IOC_INFO, &vip) < 0)
718
		return (errno);
719
720
	if (vip.vip_info_ct != 0)
721
		return (EIO);
722
723
	info = malloc(vip.vip_size);
724
	if (info == NULL)
725
		return (ENOMEM);
726
727
	/* Second ioctl to get the actual list */
728
	vip.vip_info = info;
729
	if (ioctl(env->vmd_fd, VMM_IOC_INFO, &vip) < 0) {
730
		ret = errno;
731
		free(info);
732
		return (ret);
733
	}
734
735
	/* Return info */
736
	ct = vip.vip_size / sizeof(struct vm_info_result);
737
	for (i = 0; i < ct; i++) {
738
		if (terminate) {
739
			vtp.vtp_vm_id = info[i].vir_id;
740
			if ((ret = terminate_vm(&vtp)) != 0)
741
				return (ret);
742
			log_debug("%s: terminated VM %s (id %d)", __func__,
743
			    info[i].vir_name, info[i].vir_id);
744
			continue;
745
		}
746
		memcpy(&vir.vir_info, &info[i], sizeof(vir.vir_info));
747
		vir.vir_info.vir_id = vm_id2vmid(info[i].vir_id, NULL);
748
		if (proc_compose_imsg(ps, PROC_PARENT, -1,
749
		    IMSG_VMDOP_GET_INFO_VM_DATA, imsg->hdr.peerid, -1,
750
		    &vir, sizeof(vir)) == -1)
751
			return (EIO);
752
	}
753
	free(info);
754
	return (0);
755
}