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