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