xref: /openbsd/usr.sbin/vmctl/vmctl.c (revision d489aa7e)
1 /*	$OpenBSD: vmctl.c,v 1.84 2023/01/28 14:40:53 dv Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 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/queue.h>
20 #include <sys/uio.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 
25 #include <machine/vmmvar.h>
26 
27 #include <ctype.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <imsg.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <util.h>
38 #include <pwd.h>
39 #include <grp.h>
40 
41 #include "vmd.h"
42 #include "virtio.h"
43 #include "vmctl.h"
44 #include "atomicio.h"
45 
46 extern char *__progname;
47 uint32_t info_id;
48 char info_name[VMM_MAX_NAME_LEN];
49 enum actions info_action;
50 unsigned int info_flags;
51 
52 struct imsgbuf *ibuf;
53 
54 /*
55  * vm_start
56  *
57  * Request vmd to start the VM defined by the supplied parameters
58  *
59  * Parameters:
60  *  start_id: optional ID of the VM
61  *  name: optional name of the VM
62  *  memsize: memory size (in bytes) of the VM to create
63  *  nnics: number of vionet network interfaces to create
64  *  nics: switch names of the network interfaces to create
65  *  ndisks: number of disk images
66  *  disks: disk image file names
67  *  kernel: kernel image to load
68  *  iso: iso image file
69  *  instance: create instance from vm
70  *
71  * Return:
72  *  0 if the request to start the VM was sent successfully.
73  *  ENOMEM if a memory allocation failure occurred.
74  */
75 int
76 vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics,
77     char **nics, int ndisks, char **disks, int *disktypes, char *kernel,
78     char *iso, char *instance, unsigned int bootdevice)
79 {
80 	struct vmop_create_params *vmc;
81 	struct vm_create_params *vcp;
82 	unsigned int flags = 0;
83 	int i;
84 	const char *s;
85 
86 	if (memsize)
87 		flags |= VMOP_CREATE_MEMORY;
88 	if (nnics)
89 		flags |= VMOP_CREATE_NETWORK;
90 	if (ndisks)
91 		flags |= VMOP_CREATE_DISK;
92 	if (kernel)
93 		flags |= VMOP_CREATE_KERNEL;
94 	if (iso)
95 		flags |= VMOP_CREATE_CDROM;
96 	if (instance)
97 		flags |= VMOP_CREATE_INSTANCE;
98 	else if (flags != 0) {
99 		if (memsize < 1)
100 			memsize = VM_DEFAULT_MEMORY;
101 		if (ndisks > VM_MAX_DISKS_PER_VM)
102 			errx(1, "too many disks");
103 		else if (ndisks == 0)
104 			warnx("starting without disks");
105 		if (kernel == NULL && ndisks == 0 && !iso)
106 			errx(1, "no kernel or disk/cdrom specified");
107 		if (nnics == -1)
108 			nnics = 0;
109 		if (nnics > VM_MAX_NICS_PER_VM)
110 			errx(1, "too many network interfaces");
111 		if (nnics == 0)
112 			warnx("starting without network interfaces");
113 	}
114 
115 	if ((vmc = calloc(1, sizeof(struct vmop_create_params))) == NULL)
116 		return (ENOMEM);
117 
118 	vmc->vmc_flags = flags;
119 
120 	/* vcp includes configuration that is shared with the kernel */
121 	vcp = &vmc->vmc_params;
122 
123 	/*
124 	 * XXX: vmd(8) fills in the actual memory ranges. vmctl(8)
125 	 * just passes in the actual memory size here.
126 	 */
127 	vcp->vcp_nmemranges = 1;
128 	vcp->vcp_memranges[0].vmr_size = memsize;
129 
130 	vcp->vcp_ncpus = 1;
131 	vcp->vcp_ndisks = ndisks;
132 	vcp->vcp_nnics = nnics;
133 	vcp->vcp_id = start_id;
134 
135 	for (i = 0 ; i < ndisks; i++) {
136 		if (strlcpy(vcp->vcp_disks[i], disks[i],
137 		    sizeof(vcp->vcp_disks[i])) >=
138 		    sizeof(vcp->vcp_disks[i]))
139 			errx(1, "disk path too long");
140 		vmc->vmc_disktypes[i] = disktypes[i];
141 	}
142 	for (i = 0 ; i < nnics; i++) {
143 		vmc->vmc_ifflags[i] = VMIFF_UP;
144 
145 		if (strcmp(".", nics[i]) == 0) {
146 			/* Add a "local" interface */
147 			(void)strlcpy(vmc->vmc_ifswitch[i], "",
148 			    sizeof(vmc->vmc_ifswitch[i]));
149 			vmc->vmc_ifflags[i] |= VMIFF_LOCAL;
150 		} else {
151 			/* Add an interface to a switch */
152 			if (strlcpy(vmc->vmc_ifswitch[i], nics[i],
153 			    sizeof(vmc->vmc_ifswitch[i])) >=
154 			    sizeof(vmc->vmc_ifswitch[i]))
155 				errx(1, "interface name too long");
156 		}
157 	}
158 	if (name != NULL) {
159 		/*
160 		 * Allow VMs names with alphanumeric characters, dot, hyphen
161 		 * and underscore. But disallow dot, hyphen and underscore at
162 		 * the start.
163 		 */
164 		if (*name == '-' || *name == '.' || *name == '_')
165 			errx(1, "invalid VM name");
166 
167 		for (s = name; *s != '\0'; ++s) {
168 			if (!(isalnum(*s) || *s == '.' || *s == '-' ||
169 			    *s == '_'))
170 				errx(1, "invalid VM name");
171 		}
172 
173 		if (strlcpy(vcp->vcp_name, name,
174 		    sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name))
175 			errx(1, "vm name too long");
176 	}
177 	if (kernel != NULL)
178 		if (strlcpy(vcp->vcp_kernel, kernel,
179 		    sizeof(vcp->vcp_kernel)) >= sizeof(vcp->vcp_kernel))
180 			errx(1, "kernel name too long");
181 	if (iso != NULL)
182 		if (strlcpy(vcp->vcp_cdrom, iso,
183 		    sizeof(vcp->vcp_cdrom)) >= sizeof(vcp->vcp_cdrom))
184 			errx(1, "cdrom name too long");
185 	if (instance != NULL)
186 		if (strlcpy(vmc->vmc_instance, instance,
187 		    sizeof(vmc->vmc_instance)) >= sizeof(vmc->vmc_instance))
188 			errx(1, "instance vm name too long");
189 	vmc->vmc_bootdevice = bootdevice;
190 
191 	imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
192 	    vmc, sizeof(struct vmop_create_params));
193 
194 	free(vcp);
195 	return (0);
196 }
197 
198 /*
199  * vm_start_complete
200  *
201  * Callback function invoked when we are expecting an
202  * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of
203  * a start vm operation.
204  *
205  * Parameters:
206  *  imsg : response imsg received from vmd
207  *  ret  : return value
208  *  autoconnect : open the console after startup
209  *
210  * Return:
211  *  Always 1 to indicate we have processed the return message (even if it
212  *  was an incorrect/failure message)
213  *
214  *  The function also sets 'ret' to the error code as follows:
215  *   0     : Message successfully processed
216  *   EINVAL: Invalid or unexpected response from vmd
217  *   EIO   : vm_start command failed
218  *   ENOENT: a specified component of the VM could not be found (disk image,
219  *    BIOS firmware image, etc)
220  */
221 int
222 vm_start_complete(struct imsg *imsg, int *ret, int autoconnect)
223 {
224 	struct vmop_result *vmr;
225 	int res;
226 
227 	if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) {
228 		vmr = (struct vmop_result *)imsg->data;
229 		res = vmr->vmr_result;
230 		if (res) {
231 			switch (res) {
232 			case VMD_BIOS_MISSING:
233 				warnx("vmm bios firmware file not found.");
234 				*ret = ENOENT;
235 				break;
236 			case VMD_DISK_MISSING:
237 				warnx("could not open disk image(s)");
238 				*ret = ENOENT;
239 				break;
240 			case VMD_CDROM_MISSING:
241 				warnx("could not find specified iso image");
242 				*ret = ENOENT;
243 				break;
244 			case VMD_CDROM_INVALID:
245 				warnx("specified iso image is not a regular "
246 				    "file");
247 				*ret = ENOENT;
248 				break;
249 			case VMD_PARENT_INVALID:
250 				warnx("invalid template");
251 				*ret = EINVAL;
252 				break;
253 			default:
254 				errno = res;
255 				warn("start vm command failed");
256 				*ret = EIO;
257 			}
258 		} else if (autoconnect) {
259 			/* does not return */
260 			ctl_openconsole(vmr->vmr_ttyname);
261 		} else {
262 			warnx("started vm %d successfully, tty %s",
263 			    vmr->vmr_id, vmr->vmr_ttyname);
264 			*ret = 0;
265 		}
266 	} else {
267 		warnx("unexpected response received from vmd");
268 		*ret = EINVAL;
269 	}
270 
271 	return (1);
272 }
273 
274 void
275 send_vm(uint32_t id, const char *name)
276 {
277 	struct vmop_id vid;
278 	int fds[2], readn, writen;
279 	long pagesz;
280 	char *buf;
281 
282 	pagesz = getpagesize();
283 	buf = malloc(pagesz);
284 	if (buf == NULL)
285 		errx(1, "%s: memory allocation failure", __func__);
286 
287 	memset(&vid, 0, sizeof(vid));
288 	vid.vid_id = id;
289 	if (name != NULL)
290 		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
291 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
292 		warnx("%s: socketpair creation failed", __func__);
293 	} else {
294 		imsg_compose(ibuf, IMSG_VMDOP_SEND_VM_REQUEST, 0, 0, fds[0],
295 				&vid, sizeof(vid));
296 		imsg_flush(ibuf);
297 		while (1) {
298 			readn = atomicio(read, fds[1], buf, pagesz);
299 			if (!readn)
300 				break;
301 			writen = atomicio(vwrite, STDOUT_FILENO, buf,
302 					readn);
303 			if (writen != readn)
304 				break;
305 		}
306 		if (vid.vid_id)
307 			warnx("sent vm %d successfully", vid.vid_id);
308 		else
309 			warnx("sent vm %s successfully", vid.vid_name);
310 	}
311 
312 	free(buf);
313 }
314 
315 void
316 vm_receive(uint32_t id, const char *name)
317 {
318 	struct vmop_id vid;
319 	int fds[2], readn, writen;
320 	long pagesz;
321 	char *buf;
322 
323 	pagesz = getpagesize();
324 	buf = malloc(pagesz);
325 	if (buf == NULL)
326 		errx(1, "%s: memory allocation failure", __func__);
327 
328 	memset(&vid, 0, sizeof(vid));
329 	if (name != NULL)
330 		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
331 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
332 		warnx("%s: socketpair creation failed", __func__);
333 	} else {
334 		imsg_compose(ibuf, IMSG_VMDOP_RECEIVE_VM_REQUEST, 0, 0, fds[0],
335 		    &vid, sizeof(vid));
336 		imsg_flush(ibuf);
337 		while (1) {
338 			readn = atomicio(read, STDIN_FILENO, buf, pagesz);
339 			if (!readn) {
340 				close(fds[1]);
341 				break;
342 			}
343 			writen = atomicio(vwrite, fds[1], buf, readn);
344 			if (writen != readn)
345 				break;
346 		}
347 	}
348 
349 	free(buf);
350 }
351 
352 void
353 pause_vm(uint32_t pause_id, const char *name)
354 {
355 	struct vmop_id vid;
356 
357 	memset(&vid, 0, sizeof(vid));
358 	vid.vid_id = pause_id;
359 	if (name != NULL)
360 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
361 
362 	imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1,
363 	    &vid, sizeof(vid));
364 }
365 
366 int
367 pause_vm_complete(struct imsg *imsg, int *ret)
368 {
369 	struct vmop_result *vmr;
370 	int res;
371 
372 	if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) {
373 		vmr = (struct vmop_result *)imsg->data;
374 		res = vmr->vmr_result;
375 		if (res) {
376 			errno = res;
377 			warn("pause vm command failed");
378 			*ret = EIO;
379 		} else {
380 			warnx("paused vm %d successfully", vmr->vmr_id);
381 			*ret = 0;
382 		}
383 	} else {
384 		warnx("unexpected response received from vmd");
385 		*ret = EINVAL;
386 	}
387 
388 	return (1);
389 }
390 
391 void
392 unpause_vm(uint32_t pause_id, const char *name)
393 {
394 	struct vmop_id vid;
395 
396 	memset(&vid, 0, sizeof(vid));
397 	vid.vid_id = pause_id;
398 	if (name != NULL)
399 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
400 
401 	imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1,
402 	    &vid, sizeof(vid));
403 }
404 
405 int
406 unpause_vm_complete(struct imsg *imsg, int *ret)
407 {
408 	struct vmop_result *vmr;
409 	int res;
410 
411 	if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) {
412 		vmr = (struct vmop_result *)imsg->data;
413 		res = vmr->vmr_result;
414 		if (res) {
415 			errno = res;
416 			warn("unpause vm command failed");
417 			*ret = EIO;
418 		} else {
419 			warnx("unpaused vm %d successfully", vmr->vmr_id);
420 			*ret = 0;
421 		}
422 	} else {
423 		warnx("unexpected response received from vmd");
424 		*ret = EINVAL;
425 	}
426 
427 	return (1);
428 }
429 
430 /*
431  * terminate_vm
432  *
433  * Request vmd to stop the VM indicated
434  *
435  * Parameters:
436  *  terminate_id: ID of the vm to be terminated
437  *  name: optional name of the VM to be terminated
438  *  flags: VMOP_FORCE or VMOP_WAIT flags
439  */
440 void
441 terminate_vm(uint32_t terminate_id, const char *name, unsigned int flags)
442 {
443 	struct vmop_id vid;
444 
445 	memset(&vid, 0, sizeof(vid));
446 	vid.vid_id = terminate_id;
447 	if (name != NULL) {
448 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
449 		fprintf(stderr, "stopping vm %s: ", name);
450 	} else {
451 		fprintf(stderr, "stopping vm: ");
452 	}
453 
454 	vid.vid_flags = flags & (VMOP_FORCE|VMOP_WAIT);
455 
456 	imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST,
457 	    0, 0, -1, &vid, sizeof(vid));
458 }
459 
460 /*
461  * terminate_vm_complete
462  *
463  * Callback function invoked when we are waiting for the response from an
464  * IMSG_VMDOP_TERMINATE_VM_REQUEST. We expect a reply of either an
465  * IMSG_VMDOP_TERMINATE_VM_EVENT indicating the termination of a vm or an
466  * IMSG_VMDOP_TERMINATE_VM_RESPONSE with a success/failure result.
467  *
468  * Parameters:
469  *  imsg : response imsg received from vmd
470  *  ret  : return value
471  *  flags: VMOP_FORCE or VMOP_WAIT flags
472  *
473  * Return:
474  *  Always 1 to indicate we have processed the return message (even if it
475  *  was an incorrect/failure message)
476  *
477  *  The function also sets 'ret' to the error code as follows:
478  *   0     : Message successfully processed
479  *   EINVAL: Invalid or unexpected response from vmd
480  *   EIO   : terminate_vm command failed
481  */
482 int
483 terminate_vm_complete(struct imsg *imsg, int *ret, unsigned int flags)
484 {
485 	struct vmop_result *vmr;
486 	int res;
487 
488 	switch (imsg->hdr.type) {
489 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
490 		IMSG_SIZE_CHECK(imsg, &vmr);
491 		vmr = (struct vmop_result *)imsg->data;
492 		res = vmr->vmr_result;
493 
494 		switch (res) {
495 		case 0:
496 			fprintf(stderr, "requested to shutdown vm %d\n",
497 			    vmr->vmr_id);
498 			*ret = 0;
499 			break;
500 		case VMD_VM_STOP_INVALID:
501 			fprintf(stderr,
502 			    "cannot stop vm that is not running\n");
503 			*ret = EINVAL;
504 			break;
505 		case ENOENT:
506 			fprintf(stderr, "vm not found\n");
507 			*ret = EIO;
508 			break;
509 		case EINTR:
510 			fprintf(stderr, "interrupted call\n");
511 			*ret = EIO;
512 			break;
513 		default:
514 			errno = res;
515 			fprintf(stderr, "failed: %s\n",
516 			    strerror(res));
517 			*ret = EIO;
518 		}
519 		break;
520 	case IMSG_VMDOP_TERMINATE_VM_EVENT:
521 		IMSG_SIZE_CHECK(imsg, &vmr);
522 		vmr = (struct vmop_result *)imsg->data;
523 		if (flags & VMOP_WAIT) {
524 			fprintf(stderr, "terminated vm %d\n", vmr->vmr_id);
525 		} else if (flags & VMOP_FORCE) {
526 			fprintf(stderr, "forced to terminate vm %d\n",
527 			    vmr->vmr_id);
528 		}
529 		*ret = 0;
530 		break;
531 	default:
532 		fprintf(stderr, "unexpected response received from vmd\n");
533 		*ret = EINVAL;
534 	}
535 	errno = *ret;
536 
537 	return (1);
538 }
539 
540 /*
541  * terminate_all
542  *
543  * Request to stop all VMs gracefully
544  *
545  * Parameters
546  *  list: the vm information (consolidated) returned from vmd via imsg
547  *  ct  : the size (number of elements in 'list') of the result
548  *  flags: VMOP_FORCE or VMOP_WAIT flags
549  */
550 void
551 terminate_all(struct vmop_info_result *list, size_t ct, unsigned int flags)
552 {
553 	struct vm_info_result *vir;
554 	struct vmop_info_result *vmi;
555 	struct parse_result res;
556 	size_t i;
557 
558 	for (i = 0; i < ct; i++) {
559 		vmi = &list[i];
560 		vir = &vmi->vir_info;
561 
562 		/* The VM is already stopped */
563 		if (vir->vir_creator_pid == 0 || vir->vir_id == 0)
564 			continue;
565 
566 		memset(&res, 0, sizeof(res));
567 		res.action = CMD_STOP;
568 		res.id = 0;
569 		res.flags = info_flags;
570 
571 		if ((res.name = strdup(vir->vir_name)) == NULL)
572 			errx(1, "strdup");
573 
574 		vmmaction(&res);
575 	}
576 }
577 
578 /*
579  * waitfor_vm
580  *
581  * Wait until vmd stopped the indicated VM
582  *
583  * Parameters:
584  *  terminate_id: ID of the vm to be terminated
585  *  name: optional name of the VM to be terminated
586  */
587 void
588 waitfor_vm(uint32_t terminate_id, const char *name)
589 {
590 	struct vmop_id vid;
591 
592 	memset(&vid, 0, sizeof(vid));
593 	vid.vid_id = terminate_id;
594 	if (name != NULL) {
595 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
596 		fprintf(stderr, "waiting for vm %s: ", name);
597 	} else {
598 		fprintf(stderr, "waiting for vm: ");
599 	}
600 
601 	imsg_compose(ibuf, IMSG_VMDOP_WAIT_VM_REQUEST,
602 	    0, 0, -1, &vid, sizeof(vid));
603 }
604 
605 /*
606  * get_info_vm
607  *
608  * Return the list of all running VMs or find a specific VM by ID or name.
609  *
610  * Parameters:
611  *  id: optional ID of a VM to list
612  *  name: optional name of a VM to list
613  *  action: if CMD_CONSOLE or CMD_STOP open a console or terminate the VM.
614  *  flags: optional flags used by the CMD_STOP action.
615  *
616  * Request a list of running VMs from vmd
617  */
618 void
619 get_info_vm(uint32_t id, const char *name, enum actions action,
620     unsigned int flags)
621 {
622 	info_id = id;
623 	if (name != NULL)
624 		(void)strlcpy(info_name, name, sizeof(info_name));
625 	info_action = action;
626 	info_flags = flags;
627 	imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0);
628 }
629 
630 /*
631  * check_info_id
632  *
633  * Check if requested name or ID of a VM matches specified arguments
634  *
635  * Parameters:
636  *  name: name of the VM
637  *  id: ID of the VM
638  */
639 int
640 check_info_id(const char *name, uint32_t id)
641 {
642 	if (info_id == 0 && *info_name == '\0')
643 		return (-1);
644 	if (info_id != 0 && info_id == id)
645 		return (1);
646 	if (*info_name != '\0' && name && strcmp(info_name, name) == 0)
647 		return (1);
648 	return (0);
649 }
650 
651 /*
652  * add_info
653  *
654  * Callback function invoked when we are expecting an
655  * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional
656  * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating
657  * that no additional "list vm" data will be forthcoming.
658  *
659  * Parameters:
660  *  imsg : response imsg received from vmd
661  *  ret  : return value
662  *
663  * Return:
664  *  0     : the returned data was successfully added to the "list vm" data.
665  *          The caller can expect more data.
666  *  1     : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call
667  *          add_info again), or an error occurred adding the returned data
668  *          to the "list vm" data. The caller should check the value of
669  *          'ret' to determine which case occurred.
670  *
671  * This function does not return if a VM is found and info_action is CMD_CONSOLE
672  *
673  *  The function also sets 'ret' to the error code as follows:
674  *   0     : Message successfully processed
675  *   EINVAL: Invalid or unexpected response from vmd
676  *   ENOMEM: memory allocation failure
677  */
678 int
679 add_info(struct imsg *imsg, int *ret)
680 {
681 	static size_t ct = 0;
682 	static struct vmop_info_result *vir = NULL;
683 
684 	if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) {
685 		vir = reallocarray(vir, ct + 1,
686 		    sizeof(struct vmop_info_result));
687 		if (vir == NULL) {
688 			*ret = ENOMEM;
689 			return (1);
690 		}
691 		memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result));
692 		ct++;
693 		*ret = 0;
694 		return (0);
695 	} else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) {
696 		switch (info_action) {
697 		case CMD_CONSOLE:
698 			vm_console(vir, ct);
699 			break;
700 		case CMD_STOPALL:
701 			terminate_all(vir, ct, info_flags);
702 			break;
703 		default:
704 			print_vm_info(vir, ct);
705 			break;
706 		}
707 		free(vir);
708 		*ret = 0;
709 		return (1);
710 	} else {
711 		*ret = EINVAL;
712 		return (1);
713 	}
714 }
715 
716 /*
717  * vm_state
718  *
719  * Returns a string representing the current VM state, note that the order
720  * matters. A paused VM does have the VM_STATE_RUNNING bit set, but
721  * VM_STATE_PAUSED is more significant to report. Same goes for stopping VMs.
722  *
723  * Parameters
724  *  vm_state: mask indicating the vm state
725  */
726 const char *
727 vm_state(unsigned int mask)
728 {
729 	if (mask & VM_STATE_PAUSED)
730 		return "paused";
731 	else if (mask & VM_STATE_WAITING)
732 		return "waiting";
733 	else if (mask & VM_STATE_SHUTDOWN)
734 		return "stopping";
735 	else if (mask & VM_STATE_RUNNING)
736 		return "running";
737 	/* Presence of absence of other flags */
738 	else if (!mask || (mask & VM_STATE_DISABLED))
739 		return "stopped";
740 
741 	return "unknown";
742 }
743 
744 /*
745  * print_vm_info
746  *
747  * Prints the vm information returned from vmd in 'list' to stdout.
748  *
749  * Parameters
750  *  list: the vm information (consolidated) returned from vmd via imsg
751  *  ct  : the size (number of elements in 'list') of the result
752  */
753 void
754 print_vm_info(struct vmop_info_result *list, size_t ct)
755 {
756 	struct vm_info_result *vir;
757 	struct vmop_info_result *vmi;
758 	size_t i;
759 	char *tty;
760 	char curmem[FMT_SCALED_STRSIZE];
761 	char maxmem[FMT_SCALED_STRSIZE];
762 	char user[16], group[16];
763 	const char *name;
764 	int running;
765 
766 	printf("%5s %5s %5s %7s %7s %7s %12s %8s %s\n", "ID", "PID", "VCPUS",
767 	    "MAXMEM", "CURMEM", "TTY", "OWNER", "STATE", "NAME");
768 
769 	for (i = 0; i < ct; i++) {
770 		vmi = &list[i];
771 		vir = &vmi->vir_info;
772 		running = (vir->vir_creator_pid != 0 && vir->vir_id != 0);
773 		if (check_info_id(vir->vir_name, vir->vir_id)) {
774 			/* get user name */
775 			name = user_from_uid(vmi->vir_uid, 1);
776 			if (name == NULL)
777 				(void)snprintf(user, sizeof(user),
778 				    "%d", vmi->vir_uid);
779 			else
780 				(void)strlcpy(user, name, sizeof(user));
781 			/* get group name */
782 			if (vmi->vir_gid != -1) {
783 				name = group_from_gid(vmi->vir_gid, 1);
784 				if (name == NULL)
785 					(void)snprintf(group, sizeof(group),
786 					    ":%lld", vmi->vir_gid);
787 				else
788 					(void)snprintf(group, sizeof(group),
789 					    ":%s", name);
790 				(void)strlcat(user, group, sizeof(user));
791 			}
792 
793 			(void)strlcpy(curmem, "-", sizeof(curmem));
794 			(void)strlcpy(maxmem, "-", sizeof(maxmem));
795 
796 			(void)fmt_scaled(vir->vir_memory_size, maxmem);
797 
798 			if (running) {
799 				if (*vmi->vir_ttyname == '\0')
800 					tty = "-";
801 				/* get tty - skip /dev/ path */
802 				else if ((tty = strrchr(vmi->vir_ttyname,
803 				    '/')) == NULL || *++tty == '\0')
804 					tty = list[i].vir_ttyname;
805 
806 				(void)fmt_scaled(vir->vir_used_size, curmem);
807 
808 				/* running vm */
809 				printf("%5u %5u %5zd %7s %7s %7s %12s %8s %s\n",
810 				    vir->vir_id, vir->vir_creator_pid,
811 				    vir->vir_ncpus, maxmem, curmem,
812 				    tty, user, vm_state(vmi->vir_state),
813 				    vir->vir_name);
814 			} else {
815 				/* disabled vm */
816 				printf("%5u %5s %5zd %7s %7s %7s %12s %8s %s\n",
817 				    vir->vir_id, "-",
818 				    vir->vir_ncpus, maxmem, curmem,
819 				    "-", user, vm_state(vmi->vir_state),
820 				    vir->vir_name);
821 			}
822 		}
823 	}
824 }
825 
826 /*
827  * vm_console
828  *
829  * Connects to the vm console returned from vmd in 'list'.
830  *
831  * Parameters
832  *  list: the vm information (consolidated) returned from vmd via imsg
833  *  ct  : the size (number of elements in 'list') of the result
834  */
835 __dead void
836 vm_console(struct vmop_info_result *list, size_t ct)
837 {
838 	struct vmop_info_result *vir;
839 	size_t i;
840 
841 	for (i = 0; i < ct; i++) {
842 		vir = &list[i];
843 		if ((check_info_id(vir->vir_info.vir_name,
844 		    vir->vir_info.vir_id) > 0) &&
845 			(vir->vir_ttyname[0] != '\0')) {
846 			/* does not return */
847 			ctl_openconsole(vir->vir_ttyname);
848 		}
849 	}
850 
851 	errx(1, "console not found");
852 }
853 
854 /*
855  * open_imagefile
856  *
857  * Open an imagefile with the specified type, path and size.
858  *
859  * Parameters:
860  *  type        : format of the image file
861  *  imgfile_path: path to the image file to create
862  *  flags       : flags for open(2), e.g. O_RDONLY
863  *  file        : file structure
864  *  sz		: size of the image file
865  *
866  * Return:
867  *  fd          : Returns file descriptor of the new image file
868  *  -1          : Operation failed.  errno is set.
869  */
870 int
871 open_imagefile(int type, const char *imgfile_path, int flags,
872     struct virtio_backing *file, off_t *sz)
873 {
874 	int	 fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i;
875 	char	 path[PATH_MAX];
876 
877 	*sz = 0;
878 	if ((fd = open(imgfile_path, flags)) == -1)
879 		return (-1);
880 
881 	basefd[0] = fd;
882 	nfd = 1;
883 
884 	errno = 0;
885 	switch (type) {
886 	case VMDF_QCOW2:
887 		if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path))
888 			return (-1);
889 		for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) {
890 			if ((ret = virtio_qcow2_get_base(basefd[i],
891 			    path, sizeof(path), imgfile_path)) == -1) {
892 				log_debug("%s: failed to get base %d", __func__, i);
893 				return -1;
894 			} else if (ret == 0)
895 				break;
896 
897 			if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) {
898 				log_warn("%s: failed to open base %s",
899 				    __func__, path);
900 				return (-1);
901 			}
902 		}
903 		ret = virtio_qcow2_init(file, sz, basefd, nfd);
904 		break;
905 	default:
906 		ret = virtio_raw_init(file, sz, &fd, 1);
907 		break;
908 	}
909 
910 	if (ret == -1) {
911 		for (i = 0; i < nfd; i++)
912 			close(basefd[i]);
913 		return (-1);
914 	}
915 
916 	return (fd);
917 }
918 
919 /*
920  * create_imagefile
921  *
922  * Create an empty imagefile with the specified type, path and size.
923  *
924  * Parameters:
925  *  type        : format of the image file
926  *  imgfile_path: path to the image file to create
927  *  base_path   : path to the qcow2 base image
928  *  imgsize     : size of the image file to create (in bytes)
929  *  format      : string identifying the format
930  *
931  * Return:
932  *  EEXIST: The requested image file already exists
933  *  0     : Image file successfully created
934  *  Exxxx : Various other Exxxx errno codes due to other I/O errors
935  */
936 int
937 create_imagefile(int type, const char *imgfile_path, const char *base_path,
938     uint64_t imgsize, const char **format)
939 {
940 	int	 ret;
941 
942 	switch (type) {
943 	case VMDF_QCOW2:
944 		*format = "qcow2";
945 		ret = virtio_qcow2_create(imgfile_path, base_path, imgsize);
946 		break;
947 	default:
948 		*format = "raw";
949 		ret = virtio_raw_create(imgfile_path, imgsize);
950 		break;
951 	}
952 
953 	return (ret);
954 }
955