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