xref: /openbsd-src/usr.sbin/vmctl/vmctl.c (revision dd7efffe3c4cdd741d86865c59e163a63c166133)
1*dd7efffeSclaudio /*	$OpenBSD: vmctl.c,v 1.92 2024/11/21 13:17:02 claudio Exp $	*/
2fb548b91Sreyk 
3eb8d1da1Sreyk /*
4eb8d1da1Sreyk  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
5eb8d1da1Sreyk  *
6eb8d1da1Sreyk  * Permission to use, copy, modify, and distribute this software for any
7eb8d1da1Sreyk  * purpose with or without fee is hereby granted, provided that the above
8eb8d1da1Sreyk  * copyright notice and this permission notice appear in all copies.
9eb8d1da1Sreyk  *
10eb8d1da1Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eb8d1da1Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eb8d1da1Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eb8d1da1Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eb8d1da1Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15eb8d1da1Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16eb8d1da1Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eb8d1da1Sreyk  */
18eb8d1da1Sreyk 
19eb8d1da1Sreyk #include <sys/queue.h>
20eb8d1da1Sreyk #include <sys/uio.h>
21eb8d1da1Sreyk #include <sys/stat.h>
22eb8d1da1Sreyk #include <sys/socket.h>
23eb8d1da1Sreyk #include <sys/un.h>
24eb8d1da1Sreyk 
25e9affaf6Sjasper #include <ctype.h>
26eb8d1da1Sreyk #include <err.h>
27eb8d1da1Sreyk #include <errno.h>
28eb8d1da1Sreyk #include <fcntl.h>
29eb8d1da1Sreyk #include <imsg.h>
30eb8d1da1Sreyk #include <limits.h>
31eb8d1da1Sreyk #include <stdio.h>
32eb8d1da1Sreyk #include <stdlib.h>
33eb8d1da1Sreyk #include <string.h>
34eb8d1da1Sreyk #include <unistd.h>
35b8a9a3ffSreyk #include <util.h>
36e5d5b350Sreyk #include <pwd.h>
37e5d5b350Sreyk #include <grp.h>
38eb8d1da1Sreyk 
39eb8d1da1Sreyk #include "vmd.h"
404d2a1fb2Sreyk #include "virtio.h"
41eb8d1da1Sreyk #include "vmctl.h"
42eed20f3bSpd #include "atomicio.h"
43eb8d1da1Sreyk 
44eb8d1da1Sreyk extern char *__progname;
45eb8d1da1Sreyk uint32_t info_id;
4659fb7762Sreyk char info_name[VMM_MAX_NAME_LEN];
47e0b12962Sreyk enum actions info_action;
48e0b12962Sreyk unsigned int info_flags;
49eb8d1da1Sreyk 
507bfb33a3Sderaadt struct imsgbuf *ibuf;
517bfb33a3Sderaadt 
52eb8d1da1Sreyk /*
5323659fc3Smlarkin  * vm_start
54eb8d1da1Sreyk  *
55eb8d1da1Sreyk  * Request vmd to start the VM defined by the supplied parameters
56eb8d1da1Sreyk  *
57eb8d1da1Sreyk  * Parameters:
58eb1cd41dSreyk  *  start_id: optional ID of the VM
59eb8d1da1Sreyk  *  name: optional name of the VM
60ead1b146Sdv  *  memsize: memory size (in bytes) of the VM to create
61eb8d1da1Sreyk  *  nnics: number of vionet network interfaces to create
62b657d36cSreyk  *  nics: switch names of the network interfaces to create
63eb8d1da1Sreyk  *  ndisks: number of disk images
64eb8d1da1Sreyk  *  disks: disk image file names
65eb8d1da1Sreyk  *  kernel: kernel image to load
6695ab188fSccardenas  *  iso: iso image file
676429e633Sreyk  *  instance: create instance from vm
68eb8d1da1Sreyk  *
69eb8d1da1Sreyk  * Return:
70eb8d1da1Sreyk  *  0 if the request to start the VM was sent successfully.
71eb8d1da1Sreyk  *  ENOMEM if a memory allocation failure occurred.
72eb8d1da1Sreyk  */
73eb8d1da1Sreyk int
74e545c54cSdv vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics,
75f224f92aSccardenas     char **nics, int ndisks, char **disks, int *disktypes, char *kernel,
76917458a3Sclaudio     char *iso, char *instance, unsigned int bootdevice)
77eb8d1da1Sreyk {
7881994c8fSreyk 	struct vmop_create_params *vmc;
79eb8d1da1Sreyk 	struct vm_create_params *vcp;
800e4dbc4aSdv 	struct stat sb;
81619bf29aSreyk 	unsigned int flags = 0;
82eb8d1da1Sreyk 	int i;
83e9affaf6Sjasper 	const char *s;
84eb8d1da1Sreyk 
85b848b186Sdv 	if (kernel) {
86b848b186Sdv 		if (unveil(kernel, "r") == -1)
87b848b186Sdv 			err(1, "unveil boot kernel");
88b848b186Sdv 	} else {
89b848b186Sdv 		/* We can drop sendfd promise. */
90b848b186Sdv 		if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1)
91b848b186Sdv 			err(1, "pledge");
92b848b186Sdv 	}
93b848b186Sdv 
94619bf29aSreyk 	if (memsize)
95619bf29aSreyk 		flags |= VMOP_CREATE_MEMORY;
96619bf29aSreyk 	if (nnics)
97619bf29aSreyk 		flags |= VMOP_CREATE_NETWORK;
98619bf29aSreyk 	if (ndisks)
99619bf29aSreyk 		flags |= VMOP_CREATE_DISK;
100619bf29aSreyk 	if (kernel)
101619bf29aSreyk 		flags |= VMOP_CREATE_KERNEL;
10295ab188fSccardenas 	if (iso)
10395ab188fSccardenas 		flags |= VMOP_CREATE_CDROM;
1046429e633Sreyk 	if (instance)
1056429e633Sreyk 		flags |= VMOP_CREATE_INSTANCE;
1066429e633Sreyk 	else if (flags != 0) {
107d79c5b04Smlarkin 		if (memsize < 1)
10885b9df96Sreyk 			memsize = VM_DEFAULT_MEMORY;
109d489aa7eSdv 		if (ndisks > VM_MAX_DISKS_PER_VM)
110d79c5b04Smlarkin 			errx(1, "too many disks");
111b848b186Sdv 		else if (kernel == NULL && ndisks == 0)
112d79c5b04Smlarkin 			warnx("starting without disks");
113d9672895Sreyk 		if (kernel == NULL && ndisks == 0 && !iso)
114d9672895Sreyk 			errx(1, "no kernel or disk/cdrom specified");
115d79c5b04Smlarkin 		if (nnics == -1)
116d79c5b04Smlarkin 			nnics = 0;
117d489aa7eSdv 		if (nnics > VM_MAX_NICS_PER_VM)
118b657d36cSreyk 			errx(1, "too many network interfaces");
119b848b186Sdv 		if (kernel == NULL && nnics == 0)
120d79c5b04Smlarkin 			warnx("starting without network interfaces");
121619bf29aSreyk 	}
122d79c5b04Smlarkin 
123eab0cc3bSreyk 	if ((vmc = calloc(1, sizeof(struct vmop_create_params))) == NULL)
124eb8d1da1Sreyk 		return (ENOMEM);
125b848b186Sdv 	vmc->vmc_kernel = -1;
126619bf29aSreyk 	vmc->vmc_flags = flags;
127619bf29aSreyk 
12881994c8fSreyk 	/* vcp includes configuration that is shared with the kernel */
12981994c8fSreyk 	vcp = &vmc->vmc_params;
130eb8d1da1Sreyk 
13140a3b6a0Sstefan 	/*
13240a3b6a0Sstefan 	 * XXX: vmd(8) fills in the actual memory ranges. vmctl(8)
133e545c54cSdv 	 * just passes in the actual memory size here.
13440a3b6a0Sstefan 	 */
13540a3b6a0Sstefan 	vcp->vcp_nmemranges = 1;
13640a3b6a0Sstefan 	vcp->vcp_memranges[0].vmr_size = memsize;
13740a3b6a0Sstefan 
138eb8d1da1Sreyk 	vcp->vcp_ncpus = 1;
139eb1cd41dSreyk 	vcp->vcp_id = start_id;
140eb8d1da1Sreyk 
14173a98491Sdv 	vmc->vmc_ndisks = ndisks;
14273a98491Sdv 	vmc->vmc_nnics = nnics;
14373a98491Sdv 
144f224f92aSccardenas 	for (i = 0 ; i < ndisks; i++) {
14573a98491Sdv 		if (strlcpy(vmc->vmc_disks[i], disks[i],
14673a98491Sdv 		    sizeof(vmc->vmc_disks[i])) >=
14773a98491Sdv 		    sizeof(vmc->vmc_disks[i]))
148eab0cc3bSreyk 			errx(1, "disk path too long");
149f224f92aSccardenas 		vmc->vmc_disktypes[i] = disktypes[i];
150f224f92aSccardenas 	}
151d4a2df92Sclaudio 	for (i = 0 ; i < nnics; i++) {
152d4a2df92Sclaudio 		vmc->vmc_ifflags[i] = VMIFF_UP;
153470adcf5Sreyk 
154470adcf5Sreyk 		if (strcmp(".", nics[i]) == 0) {
155470adcf5Sreyk 			/* Add a "local" interface */
156eab0cc3bSreyk 			(void)strlcpy(vmc->vmc_ifswitch[i], "",
157eab0cc3bSreyk 			    sizeof(vmc->vmc_ifswitch[i]));
158470adcf5Sreyk 			vmc->vmc_ifflags[i] |= VMIFF_LOCAL;
159470adcf5Sreyk 		} else {
1606fd9d8e9Sjasper 			/* Add an interface to a switch */
161eab0cc3bSreyk 			if (strlcpy(vmc->vmc_ifswitch[i], nics[i],
162eab0cc3bSreyk 			    sizeof(vmc->vmc_ifswitch[i])) >=
163eab0cc3bSreyk 			    sizeof(vmc->vmc_ifswitch[i]))
164eab0cc3bSreyk 				errx(1, "interface name too long");
165470adcf5Sreyk 		}
166d4a2df92Sclaudio 	}
167e9affaf6Sjasper 	if (name != NULL) {
1686d26286fSreyk 		/*
1696d26286fSreyk 		 * Allow VMs names with alphanumeric characters, dot, hyphen
170e9affaf6Sjasper 		 * and underscore. But disallow dot, hyphen and underscore at
171e9affaf6Sjasper 		 * the start.
172e9affaf6Sjasper 		 */
173e9affaf6Sjasper 		if (*name == '-' || *name == '.' || *name == '_')
1746d26286fSreyk 			errx(1, "invalid VM name");
175e9affaf6Sjasper 
176e9affaf6Sjasper 		for (s = name; *s != '\0'; ++s) {
177e9affaf6Sjasper 			if (!(isalnum(*s) || *s == '.' || *s == '-' ||
178e9affaf6Sjasper 			    *s == '_'))
1796d26286fSreyk 				errx(1, "invalid VM name");
180e9affaf6Sjasper 		}
181e9affaf6Sjasper 
182eab0cc3bSreyk 		if (strlcpy(vcp->vcp_name, name,
183eab0cc3bSreyk 		    sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name))
184eab0cc3bSreyk 			errx(1, "vm name too long");
185e9affaf6Sjasper 	}
186b848b186Sdv 	if (kernel != NULL) {
187b848b186Sdv 		if (strnlen(kernel, PATH_MAX) == PATH_MAX)
188eab0cc3bSreyk 			errx(1, "kernel name too long");
189b848b186Sdv 		vmc->vmc_kernel = open(kernel, O_RDONLY);
190b848b186Sdv 		if (vmc->vmc_kernel == -1)
191b848b186Sdv 			err(1, "cannot open kernel '%s'", kernel);
1920e4dbc4aSdv 		memset(&sb, 0, sizeof(sb));
1930e4dbc4aSdv 		if (fstat(vmc->vmc_kernel, &sb) == -1)
1940e4dbc4aSdv 			err(1, "fstat kernel");
1950e4dbc4aSdv 		if (!S_ISREG(sb.st_mode))
1960e4dbc4aSdv 			errx(1, "kernel must be a regular file");
197b848b186Sdv 	}
19895ab188fSccardenas 	if (iso != NULL)
19973a98491Sdv 		if (strlcpy(vmc->vmc_cdrom, iso,
20073a98491Sdv 		    sizeof(vmc->vmc_cdrom)) >= sizeof(vmc->vmc_cdrom))
201eab0cc3bSreyk 			errx(1, "cdrom name too long");
2026429e633Sreyk 	if (instance != NULL)
2036429e633Sreyk 		if (strlcpy(vmc->vmc_instance, instance,
2046429e633Sreyk 		    sizeof(vmc->vmc_instance)) >= sizeof(vmc->vmc_instance))
2056429e633Sreyk 			errx(1, "instance vm name too long");
206917458a3Sclaudio 	vmc->vmc_bootdevice = bootdevice;
20795ab188fSccardenas 
208b848b186Sdv 	imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, vmc->vmc_kernel,
20981994c8fSreyk 	    vmc, sizeof(struct vmop_create_params));
210eb8d1da1Sreyk 
2111e337eb1Sjsg 	free(vmc);
212eb8d1da1Sreyk 	return (0);
213eb8d1da1Sreyk }
214eb8d1da1Sreyk 
215eb8d1da1Sreyk /*
21676031b44Sreyk  * vm_start_complete
217eb8d1da1Sreyk  *
218eb8d1da1Sreyk  * Callback function invoked when we are expecting an
219eb8d1da1Sreyk  * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of
220eb8d1da1Sreyk  * a start vm operation.
221eb8d1da1Sreyk  *
222eb8d1da1Sreyk  * Parameters:
223eb8d1da1Sreyk  *  imsg : response imsg received from vmd
224eb8d1da1Sreyk  *  ret  : return value
225fe102934Sreyk  *  autoconnect : open the console after startup
226eb8d1da1Sreyk  *
227eb8d1da1Sreyk  * Return:
228eb8d1da1Sreyk  *  Always 1 to indicate we have processed the return message (even if it
229eb8d1da1Sreyk  *  was an incorrect/failure message)
230eb8d1da1Sreyk  *
231eb8d1da1Sreyk  *  The function also sets 'ret' to the error code as follows:
232eb8d1da1Sreyk  *   0     : Message successfully processed
233eb8d1da1Sreyk  *   EINVAL: Invalid or unexpected response from vmd
23476031b44Sreyk  *   EIO   : vm_start command failed
2354ca02d76Smlarkin  *   ENOENT: a specified component of the VM could not be found (disk image,
2364ca02d76Smlarkin  *    BIOS firmware image, etc)
237eb8d1da1Sreyk  */
238eb8d1da1Sreyk int
23976031b44Sreyk vm_start_complete(struct imsg *imsg, int *ret, int autoconnect)
240eb8d1da1Sreyk {
24148665f9bSreyk 	struct vmop_result *vmr;
242eb8d1da1Sreyk 	int res;
243eb8d1da1Sreyk 
244eb8d1da1Sreyk 	if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) {
24548665f9bSreyk 		vmr = (struct vmop_result *)imsg->data;
246eb8d1da1Sreyk 		res = vmr->vmr_result;
247eb8d1da1Sreyk 		if (res) {
2484ca02d76Smlarkin 			switch (res) {
2494ca02d76Smlarkin 			case VMD_BIOS_MISSING:
2504ca02d76Smlarkin 				warnx("vmm bios firmware file not found.");
2514ca02d76Smlarkin 				*ret = ENOENT;
2524ca02d76Smlarkin 				break;
2534ca02d76Smlarkin 			case VMD_DISK_MISSING:
2546429e633Sreyk 				warnx("could not open disk image(s)");
255ad4191f5Sjasper 				*ret = ENOENT;
2564ca02d76Smlarkin 				break;
25795ab188fSccardenas 			case VMD_CDROM_MISSING:
25895ab188fSccardenas 				warnx("could not find specified iso image");
25995ab188fSccardenas 				*ret = ENOENT;
26095ab188fSccardenas 				break;
26195ab188fSccardenas 			case VMD_CDROM_INVALID:
262a8df2bc2Smlarkin 				warnx("specified iso image is not a regular "
263a8df2bc2Smlarkin 				    "file");
26495ab188fSccardenas 				*ret = ENOENT;
26595ab188fSccardenas 				break;
266baf88fe3Santon 			case VMD_PARENT_INVALID:
267baf88fe3Santon 				warnx("invalid template");
268baf88fe3Santon 				*ret = EINVAL;
269baf88fe3Santon 				break;
2704ca02d76Smlarkin 			default:
2714ca02d76Smlarkin 				errno = res;
27248665f9bSreyk 				warn("start vm command failed");
273eb8d1da1Sreyk 				*ret = EIO;
274ad4191f5Sjasper 			}
275fe102934Sreyk 		} else if (autoconnect) {
27695009bb4Sreyk 			/* does not return */
27795009bb4Sreyk 			ctl_openconsole(vmr->vmr_ttyname);
278eb8d1da1Sreyk 		} else {
27948665f9bSreyk 			warnx("started vm %d successfully, tty %s",
28048665f9bSreyk 			    vmr->vmr_id, vmr->vmr_ttyname);
281eb8d1da1Sreyk 			*ret = 0;
282eb8d1da1Sreyk 		}
283eb8d1da1Sreyk 	} else {
28448665f9bSreyk 		warnx("unexpected response received from vmd");
285eb8d1da1Sreyk 		*ret = EINVAL;
286eb8d1da1Sreyk 	}
287eb8d1da1Sreyk 
288eb8d1da1Sreyk 	return (1);
289eb8d1da1Sreyk }
290eb8d1da1Sreyk 
29152e954a3Spd void
292eed20f3bSpd send_vm(uint32_t id, const char *name)
293eed20f3bSpd {
294eed20f3bSpd 	struct vmop_id vid;
295eed20f3bSpd 	int fds[2], readn, writen;
2960ea90cb8Smlarkin 	long pagesz;
2970ea90cb8Smlarkin 	char *buf;
2980ea90cb8Smlarkin 
2990ea90cb8Smlarkin 	pagesz = getpagesize();
3000ea90cb8Smlarkin 	buf = malloc(pagesz);
3010ea90cb8Smlarkin 	if (buf == NULL)
3020ea90cb8Smlarkin 		errx(1, "%s: memory allocation failure", __func__);
303eed20f3bSpd 
304eed20f3bSpd 	memset(&vid, 0, sizeof(vid));
305eed20f3bSpd 	vid.vid_id = id;
306eed20f3bSpd 	if (name != NULL)
307eed20f3bSpd 		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
308eed20f3bSpd 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
309eed20f3bSpd 		warnx("%s: socketpair creation failed", __func__);
310eed20f3bSpd 	} else {
311eed20f3bSpd 		imsg_compose(ibuf, IMSG_VMDOP_SEND_VM_REQUEST, 0, 0, fds[0],
312eed20f3bSpd 				&vid, sizeof(vid));
313*dd7efffeSclaudio 		imsgbuf_flush(ibuf);
314eed20f3bSpd 		while (1) {
3150ea90cb8Smlarkin 			readn = atomicio(read, fds[1], buf, pagesz);
316eed20f3bSpd 			if (!readn)
317eed20f3bSpd 				break;
318eed20f3bSpd 			writen = atomicio(vwrite, STDOUT_FILENO, buf,
319eed20f3bSpd 					readn);
320eed20f3bSpd 			if (writen != readn)
321eed20f3bSpd 				break;
322eed20f3bSpd 		}
323eed20f3bSpd 		if (vid.vid_id)
324eed20f3bSpd 			warnx("sent vm %d successfully", vid.vid_id);
325eed20f3bSpd 		else
326eed20f3bSpd 			warnx("sent vm %s successfully", vid.vid_name);
327eed20f3bSpd 	}
3280ea90cb8Smlarkin 
3290ea90cb8Smlarkin 	free(buf);
330eed20f3bSpd }
331eed20f3bSpd 
332eed20f3bSpd void
333eed20f3bSpd vm_receive(uint32_t id, const char *name)
334eed20f3bSpd {
335eed20f3bSpd 	struct vmop_id vid;
336eed20f3bSpd 	int fds[2], readn, writen;
3370ea90cb8Smlarkin 	long pagesz;
3380ea90cb8Smlarkin 	char *buf;
3390ea90cb8Smlarkin 
3400ea90cb8Smlarkin 	pagesz = getpagesize();
3410ea90cb8Smlarkin 	buf = malloc(pagesz);
3420ea90cb8Smlarkin 	if (buf == NULL)
3430ea90cb8Smlarkin 		errx(1, "%s: memory allocation failure", __func__);
344eed20f3bSpd 
345eed20f3bSpd 	memset(&vid, 0, sizeof(vid));
346eed20f3bSpd 	if (name != NULL)
347eed20f3bSpd 		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
348eed20f3bSpd 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
349eed20f3bSpd 		warnx("%s: socketpair creation failed", __func__);
350eed20f3bSpd 	} else {
351eed20f3bSpd 		imsg_compose(ibuf, IMSG_VMDOP_RECEIVE_VM_REQUEST, 0, 0, fds[0],
352eed20f3bSpd 		    &vid, sizeof(vid));
353*dd7efffeSclaudio 		imsgbuf_flush(ibuf);
354eed20f3bSpd 		while (1) {
3550ea90cb8Smlarkin 			readn = atomicio(read, STDIN_FILENO, buf, pagesz);
356eed20f3bSpd 			if (!readn) {
357eed20f3bSpd 				close(fds[1]);
358eed20f3bSpd 				break;
359eed20f3bSpd 			}
360eed20f3bSpd 			writen = atomicio(vwrite, fds[1], buf, readn);
361eed20f3bSpd 			if (writen != readn)
362eed20f3bSpd 				break;
363eed20f3bSpd 		}
364eed20f3bSpd 	}
3650ea90cb8Smlarkin 
3660ea90cb8Smlarkin 	free(buf);
367eed20f3bSpd }
368eed20f3bSpd 
369eed20f3bSpd void
37052e954a3Spd pause_vm(uint32_t pause_id, const char *name)
37152e954a3Spd {
37252e954a3Spd 	struct vmop_id vid;
37352e954a3Spd 
37452e954a3Spd 	memset(&vid, 0, sizeof(vid));
37552e954a3Spd 	vid.vid_id = pause_id;
37652e954a3Spd 	if (name != NULL)
37752e954a3Spd 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
37852e954a3Spd 
37952e954a3Spd 	imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1,
38052e954a3Spd 	    &vid, sizeof(vid));
38152e954a3Spd }
38252e954a3Spd 
38352e954a3Spd int
38452e954a3Spd pause_vm_complete(struct imsg *imsg, int *ret)
38552e954a3Spd {
38652e954a3Spd 	struct vmop_result *vmr;
38752e954a3Spd 	int res;
38852e954a3Spd 
38952e954a3Spd 	if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) {
39052e954a3Spd 		vmr = (struct vmop_result *)imsg->data;
39152e954a3Spd 		res = vmr->vmr_result;
39252e954a3Spd 		if (res) {
39352e954a3Spd 			errno = res;
39452e954a3Spd 			warn("pause vm command failed");
39552e954a3Spd 			*ret = EIO;
39652e954a3Spd 		} else {
39752e954a3Spd 			warnx("paused vm %d successfully", vmr->vmr_id);
39852e954a3Spd 			*ret = 0;
39952e954a3Spd 		}
40052e954a3Spd 	} else {
40152e954a3Spd 		warnx("unexpected response received from vmd");
40252e954a3Spd 		*ret = EINVAL;
40352e954a3Spd 	}
40452e954a3Spd 
40552e954a3Spd 	return (1);
40652e954a3Spd }
40752e954a3Spd 
40852e954a3Spd void
40952e954a3Spd unpause_vm(uint32_t pause_id, const char *name)
41052e954a3Spd {
41152e954a3Spd 	struct vmop_id vid;
41252e954a3Spd 
41352e954a3Spd 	memset(&vid, 0, sizeof(vid));
41452e954a3Spd 	vid.vid_id = pause_id;
41552e954a3Spd 	if (name != NULL)
41652e954a3Spd 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
41752e954a3Spd 
41852e954a3Spd 	imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1,
41952e954a3Spd 	    &vid, sizeof(vid));
42052e954a3Spd }
42152e954a3Spd 
42252e954a3Spd int
42352e954a3Spd unpause_vm_complete(struct imsg *imsg, int *ret)
42452e954a3Spd {
42552e954a3Spd 	struct vmop_result *vmr;
42652e954a3Spd 	int res;
42752e954a3Spd 
42852e954a3Spd 	if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) {
42952e954a3Spd 		vmr = (struct vmop_result *)imsg->data;
43052e954a3Spd 		res = vmr->vmr_result;
43152e954a3Spd 		if (res) {
43252e954a3Spd 			errno = res;
43352e954a3Spd 			warn("unpause vm command failed");
43452e954a3Spd 			*ret = EIO;
43552e954a3Spd 		} else {
43652e954a3Spd 			warnx("unpaused vm %d successfully", vmr->vmr_id);
43752e954a3Spd 			*ret = 0;
43852e954a3Spd 		}
43952e954a3Spd 	} else {
44052e954a3Spd 		warnx("unexpected response received from vmd");
44152e954a3Spd 		*ret = EINVAL;
44252e954a3Spd 	}
44352e954a3Spd 
44452e954a3Spd 	return (1);
44552e954a3Spd }
44652e954a3Spd 
447eb8d1da1Sreyk /*
448eb8d1da1Sreyk  * terminate_vm
449eb8d1da1Sreyk  *
450eb8d1da1Sreyk  * Request vmd to stop the VM indicated
451eb8d1da1Sreyk  *
452eb8d1da1Sreyk  * Parameters:
453eb8d1da1Sreyk  *  terminate_id: ID of the vm to be terminated
454822fd566Sreyk  *  name: optional name of the VM to be terminated
4553be9785fSreyk  *  flags: VMOP_FORCE or VMOP_WAIT flags
456eb8d1da1Sreyk  */
457eb8d1da1Sreyk void
4583be9785fSreyk terminate_vm(uint32_t terminate_id, const char *name, unsigned int flags)
459eb8d1da1Sreyk {
46059fb7762Sreyk 	struct vmop_id vid;
461eb8d1da1Sreyk 
46259fb7762Sreyk 	memset(&vid, 0, sizeof(vid));
46359fb7762Sreyk 	vid.vid_id = terminate_id;
464e0b12962Sreyk 	if (name != NULL) {
46559fb7762Sreyk 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
466e0b12962Sreyk 		fprintf(stderr, "stopping vm %s: ", name);
467e0b12962Sreyk 	} else {
468e0b12962Sreyk 		fprintf(stderr, "stopping vm: ");
469e0b12962Sreyk 	}
470eb8d1da1Sreyk 
4713be9785fSreyk 	vid.vid_flags = flags & (VMOP_FORCE|VMOP_WAIT);
472f6e5c9ebSreyk 
4733be9785fSreyk 	imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST,
4743be9785fSreyk 	    0, 0, -1, &vid, sizeof(vid));
475eb8d1da1Sreyk }
476eb8d1da1Sreyk 
477eb8d1da1Sreyk /*
478eb8d1da1Sreyk  * terminate_vm_complete
479eb8d1da1Sreyk  *
480540e29f5Sdv  * Callback function invoked when we are waiting for the response from an
481540e29f5Sdv  * IMSG_VMDOP_TERMINATE_VM_REQUEST. We expect a reply of either an
482540e29f5Sdv  * IMSG_VMDOP_TERMINATE_VM_EVENT indicating the termination of a vm or an
483540e29f5Sdv  * IMSG_VMDOP_TERMINATE_VM_RESPONSE with a success/failure result.
484eb8d1da1Sreyk  *
485eb8d1da1Sreyk  * Parameters:
486eb8d1da1Sreyk  *  imsg : response imsg received from vmd
487eb8d1da1Sreyk  *  ret  : return value
4883be9785fSreyk  *  flags: VMOP_FORCE or VMOP_WAIT flags
489eb8d1da1Sreyk  *
490eb8d1da1Sreyk  * Return:
491eb8d1da1Sreyk  *  Always 1 to indicate we have processed the return message (even if it
492eb8d1da1Sreyk  *  was an incorrect/failure message)
493eb8d1da1Sreyk  *
494eb8d1da1Sreyk  *  The function also sets 'ret' to the error code as follows:
495eb8d1da1Sreyk  *   0     : Message successfully processed
496eb8d1da1Sreyk  *   EINVAL: Invalid or unexpected response from vmd
497eb8d1da1Sreyk  *   EIO   : terminate_vm command failed
498eb8d1da1Sreyk  */
499eb8d1da1Sreyk int
5003be9785fSreyk terminate_vm_complete(struct imsg *imsg, int *ret, unsigned int flags)
501eb8d1da1Sreyk {
50248665f9bSreyk 	struct vmop_result *vmr;
503eb8d1da1Sreyk 	int res;
504eb8d1da1Sreyk 
505540e29f5Sdv 	switch (imsg->hdr.type) {
506540e29f5Sdv 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
507540e29f5Sdv 		IMSG_SIZE_CHECK(imsg, &vmr);
50848665f9bSreyk 		vmr = (struct vmop_result *)imsg->data;
50948665f9bSreyk 		res = vmr->vmr_result;
510540e29f5Sdv 
5112da5c9dbSmlarkin 		switch (res) {
512540e29f5Sdv 		case 0:
513540e29f5Sdv 			fprintf(stderr, "requested to shutdown vm %d\n",
514540e29f5Sdv 			    vmr->vmr_id);
515540e29f5Sdv 			*ret = 0;
516540e29f5Sdv 			break;
5172da5c9dbSmlarkin 		case VMD_VM_STOP_INVALID:
518e0b12962Sreyk 			fprintf(stderr,
519e0b12962Sreyk 			    "cannot stop vm that is not running\n");
5202da5c9dbSmlarkin 			*ret = EINVAL;
5212da5c9dbSmlarkin 			break;
5222da5c9dbSmlarkin 		case ENOENT:
523e0b12962Sreyk 			fprintf(stderr, "vm not found\n");
5242da5c9dbSmlarkin 			*ret = EIO;
5252da5c9dbSmlarkin 			break;
526583f6618Sclaudio 		case EINTR:
527583f6618Sclaudio 			fprintf(stderr, "interrupted call\n");
528583f6618Sclaudio 			*ret = EIO;
529583f6618Sclaudio 			break;
5302da5c9dbSmlarkin 		default:
531574a6dcbSanton 			errno = res;
532e0b12962Sreyk 			fprintf(stderr, "failed: %s\n",
533e0b12962Sreyk 			    strerror(res));
534eb8d1da1Sreyk 			*ret = EIO;
5352da5c9dbSmlarkin 		}
536540e29f5Sdv 		break;
537540e29f5Sdv 	case IMSG_VMDOP_TERMINATE_VM_EVENT:
538540e29f5Sdv 		IMSG_SIZE_CHECK(imsg, &vmr);
539540e29f5Sdv 		vmr = (struct vmop_result *)imsg->data;
540540e29f5Sdv 		if (flags & VMOP_WAIT) {
541e0b12962Sreyk 			fprintf(stderr, "terminated vm %d\n", vmr->vmr_id);
5423be9785fSreyk 		} else if (flags & VMOP_FORCE) {
543e0b12962Sreyk 			fprintf(stderr, "forced to terminate vm %d\n",
544e0b12962Sreyk 			    vmr->vmr_id);
545eb8d1da1Sreyk 		}
546540e29f5Sdv 		*ret = 0;
547540e29f5Sdv 		break;
548540e29f5Sdv 	default:
549e0b12962Sreyk 		fprintf(stderr, "unexpected response received from vmd\n");
550eb8d1da1Sreyk 		*ret = EINVAL;
551eb8d1da1Sreyk 	}
5522da5c9dbSmlarkin 	errno = *ret;
553eb8d1da1Sreyk 
554eb8d1da1Sreyk 	return (1);
555eb8d1da1Sreyk }
556eb8d1da1Sreyk 
557eb8d1da1Sreyk /*
558e0b12962Sreyk  * terminate_all
559e0b12962Sreyk  *
560e0b12962Sreyk  * Request to stop all VMs gracefully
561e0b12962Sreyk  *
562e0b12962Sreyk  * Parameters
563e0b12962Sreyk  *  list: the vm information (consolidated) returned from vmd via imsg
564e0b12962Sreyk  *  ct  : the size (number of elements in 'list') of the result
565e0b12962Sreyk  *  flags: VMOP_FORCE or VMOP_WAIT flags
566e0b12962Sreyk  */
567e0b12962Sreyk void
568e0b12962Sreyk terminate_all(struct vmop_info_result *list, size_t ct, unsigned int flags)
569e0b12962Sreyk {
570e0b12962Sreyk 	struct vm_info_result *vir;
571e0b12962Sreyk 	struct vmop_info_result *vmi;
572e0b12962Sreyk 	struct parse_result res;
573e0b12962Sreyk 	size_t i;
574e0b12962Sreyk 
575e0b12962Sreyk 	for (i = 0; i < ct; i++) {
576e0b12962Sreyk 		vmi = &list[i];
577e0b12962Sreyk 		vir = &vmi->vir_info;
578e0b12962Sreyk 
579e0b12962Sreyk 		/* The VM is already stopped */
580e0b12962Sreyk 		if (vir->vir_creator_pid == 0 || vir->vir_id == 0)
581e0b12962Sreyk 			continue;
582e0b12962Sreyk 
583e0b12962Sreyk 		memset(&res, 0, sizeof(res));
584e0b12962Sreyk 		res.action = CMD_STOP;
585e0b12962Sreyk 		res.id = 0;
586e0b12962Sreyk 		res.flags = info_flags;
587e0b12962Sreyk 
588e0b12962Sreyk 		if ((res.name = strdup(vir->vir_name)) == NULL)
589e0b12962Sreyk 			errx(1, "strdup");
590e0b12962Sreyk 
591e0b12962Sreyk 		vmmaction(&res);
592e0b12962Sreyk 	}
593e0b12962Sreyk }
594e0b12962Sreyk 
595e0b12962Sreyk /*
596583f6618Sclaudio  * waitfor_vm
597583f6618Sclaudio  *
598583f6618Sclaudio  * Wait until vmd stopped the indicated VM
599583f6618Sclaudio  *
600583f6618Sclaudio  * Parameters:
601583f6618Sclaudio  *  terminate_id: ID of the vm to be terminated
602583f6618Sclaudio  *  name: optional name of the VM to be terminated
603583f6618Sclaudio  */
604583f6618Sclaudio void
605583f6618Sclaudio waitfor_vm(uint32_t terminate_id, const char *name)
606583f6618Sclaudio {
607583f6618Sclaudio 	struct vmop_id vid;
608583f6618Sclaudio 
609583f6618Sclaudio 	memset(&vid, 0, sizeof(vid));
610583f6618Sclaudio 	vid.vid_id = terminate_id;
611583f6618Sclaudio 	if (name != NULL) {
612583f6618Sclaudio 		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
613583f6618Sclaudio 		fprintf(stderr, "waiting for vm %s: ", name);
614583f6618Sclaudio 	} else {
615583f6618Sclaudio 		fprintf(stderr, "waiting for vm: ");
616583f6618Sclaudio 	}
617583f6618Sclaudio 
618583f6618Sclaudio 	imsg_compose(ibuf, IMSG_VMDOP_WAIT_VM_REQUEST,
619583f6618Sclaudio 	    0, 0, -1, &vid, sizeof(vid));
620583f6618Sclaudio }
621583f6618Sclaudio 
622583f6618Sclaudio /*
623eb8d1da1Sreyk  * get_info_vm
624eb8d1da1Sreyk  *
625822fd566Sreyk  * Return the list of all running VMs or find a specific VM by ID or name.
626822fd566Sreyk  *
627822fd566Sreyk  * Parameters:
628822fd566Sreyk  *  id: optional ID of a VM to list
629822fd566Sreyk  *  name: optional name of a VM to list
630e0b12962Sreyk  *  action: if CMD_CONSOLE or CMD_STOP open a console or terminate the VM.
631e0b12962Sreyk  *  flags: optional flags used by the CMD_STOP action.
632822fd566Sreyk  *
633eb8d1da1Sreyk  * Request a list of running VMs from vmd
634eb8d1da1Sreyk  */
635eb8d1da1Sreyk void
636e0b12962Sreyk get_info_vm(uint32_t id, const char *name, enum actions action,
637e0b12962Sreyk     unsigned int flags)
638eb8d1da1Sreyk {
639eb8d1da1Sreyk 	info_id = id;
64059fb7762Sreyk 	if (name != NULL)
64159fb7762Sreyk 		(void)strlcpy(info_name, name, sizeof(info_name));
642e0b12962Sreyk 	info_action = action;
643e0b12962Sreyk 	info_flags = flags;
644eb8d1da1Sreyk 	imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0);
645eb8d1da1Sreyk }
646eb8d1da1Sreyk 
647eb8d1da1Sreyk /*
6487aff4c38Smlarkin  * check_info_id
64959fb7762Sreyk  *
650822fd566Sreyk  * Check if requested name or ID of a VM matches specified arguments
651822fd566Sreyk  *
652822fd566Sreyk  * Parameters:
653822fd566Sreyk  *  name: name of the VM
654822fd566Sreyk  *  id: ID of the VM
65559fb7762Sreyk  */
65659fb7762Sreyk int
65759fb7762Sreyk check_info_id(const char *name, uint32_t id)
65859fb7762Sreyk {
65959fb7762Sreyk 	if (info_id == 0 && *info_name == '\0')
66059fb7762Sreyk 		return (-1);
66159fb7762Sreyk 	if (info_id != 0 && info_id == id)
66259fb7762Sreyk 		return (1);
66359fb7762Sreyk 	if (*info_name != '\0' && name && strcmp(info_name, name) == 0)
66459fb7762Sreyk 		return (1);
66559fb7762Sreyk 	return (0);
66659fb7762Sreyk }
66759fb7762Sreyk 
66859fb7762Sreyk /*
669eb8d1da1Sreyk  * add_info
670eb8d1da1Sreyk  *
671eb8d1da1Sreyk  * Callback function invoked when we are expecting an
672eb8d1da1Sreyk  * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional
673eb8d1da1Sreyk  * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating
674eb8d1da1Sreyk  * that no additional "list vm" data will be forthcoming.
675eb8d1da1Sreyk  *
676eb8d1da1Sreyk  * Parameters:
677eb8d1da1Sreyk  *  imsg : response imsg received from vmd
678eb8d1da1Sreyk  *  ret  : return value
679eb8d1da1Sreyk  *
680eb8d1da1Sreyk  * Return:
681eb8d1da1Sreyk  *  0     : the returned data was successfully added to the "list vm" data.
682eb8d1da1Sreyk  *          The caller can expect more data.
683eb8d1da1Sreyk  *  1     : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call
684eb8d1da1Sreyk  *          add_info again), or an error occurred adding the returned data
685eb8d1da1Sreyk  *          to the "list vm" data. The caller should check the value of
686eb8d1da1Sreyk  *          'ret' to determine which case occurred.
687eb8d1da1Sreyk  *
688e0b12962Sreyk  * This function does not return if a VM is found and info_action is CMD_CONSOLE
689822fd566Sreyk  *
690eb8d1da1Sreyk  *  The function also sets 'ret' to the error code as follows:
691eb8d1da1Sreyk  *   0     : Message successfully processed
692eb8d1da1Sreyk  *   EINVAL: Invalid or unexpected response from vmd
693eb8d1da1Sreyk  *   ENOMEM: memory allocation failure
69485846d3eSmlarkin  *   ENOENT: no entries
695eb8d1da1Sreyk  */
696eb8d1da1Sreyk int
697eb8d1da1Sreyk add_info(struct imsg *imsg, int *ret)
698eb8d1da1Sreyk {
699eb8d1da1Sreyk 	static size_t ct = 0;
7003a45f7b6Sreyk 	static struct vmop_info_result *vir = NULL;
701eb8d1da1Sreyk 
70285846d3eSmlarkin 	*ret = 0;
70385846d3eSmlarkin 
704eb8d1da1Sreyk 	if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) {
705eb8d1da1Sreyk 		vir = reallocarray(vir, ct + 1,
7063a45f7b6Sreyk 		    sizeof(struct vmop_info_result));
707eb8d1da1Sreyk 		if (vir == NULL) {
708eb8d1da1Sreyk 			*ret = ENOMEM;
709eb8d1da1Sreyk 			return (1);
710eb8d1da1Sreyk 		}
7113a45f7b6Sreyk 		memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result));
712eb8d1da1Sreyk 		ct++;
713eb8d1da1Sreyk 		return (0);
714eb8d1da1Sreyk 	} else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) {
715e0b12962Sreyk 		switch (info_action) {
716e0b12962Sreyk 		case CMD_CONSOLE:
71795009bb4Sreyk 			vm_console(vir, ct);
718e0b12962Sreyk 			break;
719e0b12962Sreyk 		case CMD_STOPALL:
720e0b12962Sreyk 			terminate_all(vir, ct, info_flags);
721e0b12962Sreyk 			break;
722e0b12962Sreyk 		default:
72385846d3eSmlarkin 			*ret = print_vm_info(vir, ct);
724e0b12962Sreyk 			break;
725e0b12962Sreyk 		}
726eb8d1da1Sreyk 		free(vir);
727eb8d1da1Sreyk 		return (1);
728eb8d1da1Sreyk 	} else {
729eb8d1da1Sreyk 		*ret = EINVAL;
730eb8d1da1Sreyk 		return (1);
731eb8d1da1Sreyk 	}
732eb8d1da1Sreyk }
733eb8d1da1Sreyk 
734eb8d1da1Sreyk /*
7353f77225dSjasper  * vm_state
7363f77225dSjasper  *
7373f77225dSjasper  * Returns a string representing the current VM state, note that the order
7383f77225dSjasper  * matters. A paused VM does have the VM_STATE_RUNNING bit set, but
7398b85f8f9Skn  * VM_STATE_PAUSED is more significant to report. Same goes for stopping VMs.
7403f77225dSjasper  *
7413f77225dSjasper  * Parameters
7423f77225dSjasper  *  vm_state: mask indicating the vm state
7433f77225dSjasper  */
7443f77225dSjasper const char *
7453f77225dSjasper vm_state(unsigned int mask)
7463f77225dSjasper {
7478f40ce4bSjasper 	if (mask & VM_STATE_PAUSED)
7483f77225dSjasper 		return "paused";
749de12a377Spd 	else if (mask & VM_STATE_WAITING)
750de12a377Spd 		return "waiting";
7518f40ce4bSjasper 	else if (mask & VM_STATE_SHUTDOWN)
7528f40ce4bSjasper 		return "stopping";
7538b85f8f9Skn 	else if (mask & VM_STATE_RUNNING)
7548b85f8f9Skn 		return "running";
7558f40ce4bSjasper 	/* Presence of absence of other flags */
7568f40ce4bSjasper 	else if (!mask || (mask & VM_STATE_DISABLED))
7578f40ce4bSjasper 		return "stopped";
7583f77225dSjasper 
7593f77225dSjasper 	return "unknown";
7603f77225dSjasper }
7613f77225dSjasper 
7623f77225dSjasper /*
763eb8d1da1Sreyk  * print_vm_info
764eb8d1da1Sreyk  *
765eb8d1da1Sreyk  * Prints the vm information returned from vmd in 'list' to stdout.
766eb8d1da1Sreyk  *
767eb8d1da1Sreyk  * Parameters
768eb8d1da1Sreyk  *  list: the vm information (consolidated) returned from vmd via imsg
769eb8d1da1Sreyk  *  ct  : the size (number of elements in 'list') of the result
77085846d3eSmlarkin  *
77185846d3eSmlarkin  * Return values:
77285846d3eSmlarkin  *  0: no error
77385846d3eSmlarkin  *  ENOENT: no entries printed
774eb8d1da1Sreyk  */
77585846d3eSmlarkin int
7763a45f7b6Sreyk print_vm_info(struct vmop_info_result *list, size_t ct)
777eb8d1da1Sreyk {
7783a45f7b6Sreyk 	struct vm_info_result *vir;
779b8a9a3ffSreyk 	struct vmop_info_result *vmi;
780628e8b46Skn 	size_t i;
781628e8b46Skn 	char *tty;
782b8a9a3ffSreyk 	char curmem[FMT_SCALED_STRSIZE];
783b8a9a3ffSreyk 	char maxmem[FMT_SCALED_STRSIZE];
784af56b46dSreyk 	char user[16], group[16];
785dee31871Smillert 	const char *name;
78685846d3eSmlarkin 	int running, found_running;
787986b002cSmlarkin 	extern int stat_rflag;
788eb8d1da1Sreyk 
78985846d3eSmlarkin 	found_running = 0;
79085846d3eSmlarkin 
7913f77225dSjasper 	printf("%5s %5s %5s %7s %7s %7s %12s %8s %s\n", "ID", "PID", "VCPUS",
7923f77225dSjasper 	    "MAXMEM", "CURMEM", "TTY", "OWNER", "STATE", "NAME");
793b8a9a3ffSreyk 
794eb8d1da1Sreyk 	for (i = 0; i < ct; i++) {
795b8a9a3ffSreyk 		vmi = &list[i];
796b8a9a3ffSreyk 		vir = &vmi->vir_info;
797628e8b46Skn 		running = (vir->vir_creator_pid != 0 && vir->vir_id != 0);
798986b002cSmlarkin 		if (!running && stat_rflag)
799986b002cSmlarkin 			continue;
80085846d3eSmlarkin 
80185846d3eSmlarkin 		found_running++;
80285846d3eSmlarkin 
80359fb7762Sreyk 		if (check_info_id(vir->vir_name, vir->vir_id)) {
804e5d5b350Sreyk 			/* get user name */
805dee31871Smillert 			name = user_from_uid(vmi->vir_uid, 1);
806dee31871Smillert 			if (name == NULL)
807e5d5b350Sreyk 				(void)snprintf(user, sizeof(user),
808e5d5b350Sreyk 				    "%d", vmi->vir_uid);
809e5d5b350Sreyk 			else
810dee31871Smillert 				(void)strlcpy(user, name, sizeof(user));
811e5d5b350Sreyk 			/* get group name */
812e5d5b350Sreyk 			if (vmi->vir_gid != -1) {
813dee31871Smillert 				name = group_from_gid(vmi->vir_gid, 1);
814dee31871Smillert 				if (name == NULL)
815af56b46dSreyk 					(void)snprintf(group, sizeof(group),
816af56b46dSreyk 					    ":%lld", vmi->vir_gid);
817e5d5b350Sreyk 				else
818af56b46dSreyk 					(void)snprintf(group, sizeof(group),
819dee31871Smillert 					    ":%s", name);
820af56b46dSreyk 				(void)strlcat(user, group, sizeof(user));
821e5d5b350Sreyk 			}
822e5d5b350Sreyk 
823b8a9a3ffSreyk 			(void)strlcpy(curmem, "-", sizeof(curmem));
824b8a9a3ffSreyk 			(void)strlcpy(maxmem, "-", sizeof(maxmem));
825b8a9a3ffSreyk 
82639e812afSdv 			(void)fmt_scaled(vir->vir_memory_size, maxmem);
827b8a9a3ffSreyk 
828628e8b46Skn 			if (running) {
829ed2fe504Sreyk 				if (*vmi->vir_ttyname == '\0')
830ed2fe504Sreyk 					tty = "-";
831b8a9a3ffSreyk 				/* get tty - skip /dev/ path */
832ed2fe504Sreyk 				else if ((tty = strrchr(vmi->vir_ttyname,
83390d78c52Stb 				    '/')) == NULL || *++tty == '\0')
834b8a9a3ffSreyk 					tty = list[i].vir_ttyname;
835b8a9a3ffSreyk 
836b8a9a3ffSreyk 				(void)fmt_scaled(vir->vir_used_size, curmem);
837b8a9a3ffSreyk 
838371c1bb3Sedd 				/* running vm */
8393f77225dSjasper 				printf("%5u %5u %5zd %7s %7s %7s %12s %8s %s\n",
8403a45f7b6Sreyk 				    vir->vir_id, vir->vir_creator_pid,
841b8a9a3ffSreyk 				    vir->vir_ncpus, maxmem, curmem,
8423f77225dSjasper 				    tty, user, vm_state(vmi->vir_state),
8433f77225dSjasper 				    vir->vir_name);
844371c1bb3Sedd 			} else {
845371c1bb3Sedd 				/* disabled vm */
8463f77225dSjasper 				printf("%5u %5s %5zd %7s %7s %7s %12s %8s %s\n",
847eb1cd41dSreyk 				    vir->vir_id, "-",
848b8a9a3ffSreyk 				    vir->vir_ncpus, maxmem, curmem,
8493f77225dSjasper 				    "-", user, vm_state(vmi->vir_state),
8503f77225dSjasper 				    vir->vir_name);
851371c1bb3Sedd 			}
85259fb7762Sreyk 		}
853eb8d1da1Sreyk 	}
85485846d3eSmlarkin 
85585846d3eSmlarkin 	if (found_running)
85685846d3eSmlarkin 		return (0);
85785846d3eSmlarkin 	else
85885846d3eSmlarkin 		return (ENOENT);
859eb8d1da1Sreyk }
860eb8d1da1Sreyk 
861eb8d1da1Sreyk /*
86295009bb4Sreyk  * vm_console
86395009bb4Sreyk  *
86495009bb4Sreyk  * Connects to the vm console returned from vmd in 'list'.
86595009bb4Sreyk  *
86695009bb4Sreyk  * Parameters
86795009bb4Sreyk  *  list: the vm information (consolidated) returned from vmd via imsg
86895009bb4Sreyk  *  ct  : the size (number of elements in 'list') of the result
86995009bb4Sreyk  */
87095009bb4Sreyk __dead void
87195009bb4Sreyk vm_console(struct vmop_info_result *list, size_t ct)
87295009bb4Sreyk {
87395009bb4Sreyk 	struct vmop_info_result *vir;
87495009bb4Sreyk 	size_t i;
87595009bb4Sreyk 
87695009bb4Sreyk 	for (i = 0; i < ct; i++) {
87795009bb4Sreyk 		vir = &list[i];
8788fde259dSjasper 		if ((check_info_id(vir->vir_info.vir_name,
8798fde259dSjasper 		    vir->vir_info.vir_id) > 0) &&
8808fde259dSjasper 			(vir->vir_ttyname[0] != '\0')) {
88195009bb4Sreyk 			/* does not return */
88295009bb4Sreyk 			ctl_openconsole(vir->vir_ttyname);
88395009bb4Sreyk 		}
88495009bb4Sreyk 	}
88595009bb4Sreyk 
8865fd19063Sccardenas 	errx(1, "console not found");
88795009bb4Sreyk }
88895009bb4Sreyk 
88995009bb4Sreyk /*
8904d2a1fb2Sreyk  * open_imagefile
8914d2a1fb2Sreyk  *
8924d2a1fb2Sreyk  * Open an imagefile with the specified type, path and size.
8934d2a1fb2Sreyk  *
8944d2a1fb2Sreyk  * Parameters:
8954d2a1fb2Sreyk  *  type        : format of the image file
8964d2a1fb2Sreyk  *  imgfile_path: path to the image file to create
8974d2a1fb2Sreyk  *  flags       : flags for open(2), e.g. O_RDONLY
8984d2a1fb2Sreyk  *  file        : file structure
8994d2a1fb2Sreyk  *  sz		: size of the image file
9004d2a1fb2Sreyk  *
9014d2a1fb2Sreyk  * Return:
9024d2a1fb2Sreyk  *  fd          : Returns file descriptor of the new image file
9034d2a1fb2Sreyk  *  -1          : Operation failed.  errno is set.
9044d2a1fb2Sreyk  */
9054d2a1fb2Sreyk int
9064d2a1fb2Sreyk open_imagefile(int type, const char *imgfile_path, int flags,
9074d2a1fb2Sreyk     struct virtio_backing *file, off_t *sz)
9084d2a1fb2Sreyk {
9094d2a1fb2Sreyk 	int	 fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i;
9104d2a1fb2Sreyk 	char	 path[PATH_MAX];
9114d2a1fb2Sreyk 
9124d2a1fb2Sreyk 	*sz = 0;
9134d2a1fb2Sreyk 	if ((fd = open(imgfile_path, flags)) == -1)
9144d2a1fb2Sreyk 		return (-1);
9154d2a1fb2Sreyk 
9164d2a1fb2Sreyk 	basefd[0] = fd;
9174d2a1fb2Sreyk 	nfd = 1;
9184d2a1fb2Sreyk 
9194d2a1fb2Sreyk 	errno = 0;
9204d2a1fb2Sreyk 	switch (type) {
9214d2a1fb2Sreyk 	case VMDF_QCOW2:
9224d2a1fb2Sreyk 		if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path))
9234d2a1fb2Sreyk 			return (-1);
9244d2a1fb2Sreyk 		for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) {
9254d2a1fb2Sreyk 			if ((ret = virtio_qcow2_get_base(basefd[i],
9264d2a1fb2Sreyk 			    path, sizeof(path), imgfile_path)) == -1) {
9274d2a1fb2Sreyk 				log_debug("%s: failed to get base %d", __func__, i);
9284d2a1fb2Sreyk 				return -1;
9294d2a1fb2Sreyk 			} else if (ret == 0)
9304d2a1fb2Sreyk 				break;
9314d2a1fb2Sreyk 
9324d2a1fb2Sreyk 			if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) {
9334d2a1fb2Sreyk 				log_warn("%s: failed to open base %s",
9344d2a1fb2Sreyk 				    __func__, path);
9354d2a1fb2Sreyk 				return (-1);
9364d2a1fb2Sreyk 			}
9374d2a1fb2Sreyk 		}
93862df93eeSreyk 		ret = virtio_qcow2_init(file, sz, basefd, nfd);
9394d2a1fb2Sreyk 		break;
9404d2a1fb2Sreyk 	default:
94162df93eeSreyk 		ret = virtio_raw_init(file, sz, &fd, 1);
9424d2a1fb2Sreyk 		break;
9434d2a1fb2Sreyk 	}
9444d2a1fb2Sreyk 
9454d2a1fb2Sreyk 	if (ret == -1) {
9464d2a1fb2Sreyk 		for (i = 0; i < nfd; i++)
9474d2a1fb2Sreyk 			close(basefd[i]);
9484d2a1fb2Sreyk 		return (-1);
9494d2a1fb2Sreyk 	}
9504d2a1fb2Sreyk 
9514d2a1fb2Sreyk 	return (fd);
9524d2a1fb2Sreyk }
9534d2a1fb2Sreyk 
9544d2a1fb2Sreyk /*
9554d2a1fb2Sreyk  * create_imagefile
9564d2a1fb2Sreyk  *
9574d2a1fb2Sreyk  * Create an empty imagefile with the specified type, path and size.
9584d2a1fb2Sreyk  *
9594d2a1fb2Sreyk  * Parameters:
9604d2a1fb2Sreyk  *  type        : format of the image file
9614d2a1fb2Sreyk  *  imgfile_path: path to the image file to create
9624d2a1fb2Sreyk  *  base_path   : path to the qcow2 base image
963ead1b146Sdv  *  imgsize     : size of the image file to create (in bytes)
9644d2a1fb2Sreyk  *  format      : string identifying the format
9654d2a1fb2Sreyk  *
9664d2a1fb2Sreyk  * Return:
9674d2a1fb2Sreyk  *  EEXIST: The requested image file already exists
9684d2a1fb2Sreyk  *  0     : Image file successfully created
9694d2a1fb2Sreyk  *  Exxxx : Various other Exxxx errno codes due to other I/O errors
9704d2a1fb2Sreyk  */
9714d2a1fb2Sreyk int
9724d2a1fb2Sreyk create_imagefile(int type, const char *imgfile_path, const char *base_path,
9737ac3f975Syasuoka     uint64_t imgsize, const char **format)
9744d2a1fb2Sreyk {
9754d2a1fb2Sreyk 	int	 ret;
9764d2a1fb2Sreyk 
9774d2a1fb2Sreyk 	switch (type) {
9784d2a1fb2Sreyk 	case VMDF_QCOW2:
9794d2a1fb2Sreyk 		*format = "qcow2";
98062df93eeSreyk 		ret = virtio_qcow2_create(imgfile_path, base_path, imgsize);
9814d2a1fb2Sreyk 		break;
9824d2a1fb2Sreyk 	default:
9834d2a1fb2Sreyk 		*format = "raw";
98462df93eeSreyk 		ret = virtio_raw_create(imgfile_path, imgsize);
9854d2a1fb2Sreyk 		break;
9864d2a1fb2Sreyk 	}
9874d2a1fb2Sreyk 
9884d2a1fb2Sreyk 	return (ret);
9894d2a1fb2Sreyk }
990