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