1 /* $OpenBSD: vmctl.c,v 1.89 2023/11/09 12:26:08 dv Exp $ */ 2 3 /* 4 * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/uio.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 25 #include <ctype.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <imsg.h> 30 #include <limits.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <util.h> 36 #include <pwd.h> 37 #include <grp.h> 38 39 #include "vmd.h" 40 #include "virtio.h" 41 #include "vmctl.h" 42 #include "atomicio.h" 43 44 extern char *__progname; 45 uint32_t info_id; 46 char info_name[VMM_MAX_NAME_LEN]; 47 enum actions info_action; 48 unsigned int info_flags; 49 50 struct imsgbuf *ibuf; 51 52 /* 53 * vm_start 54 * 55 * Request vmd to start the VM defined by the supplied parameters 56 * 57 * Parameters: 58 * start_id: optional ID of the VM 59 * name: optional name of the VM 60 * memsize: memory size (in bytes) of the VM to create 61 * nnics: number of vionet network interfaces to create 62 * nics: switch names of the network interfaces to create 63 * ndisks: number of disk images 64 * disks: disk image file names 65 * kernel: kernel image to load 66 * iso: iso image file 67 * instance: create instance from vm 68 * 69 * Return: 70 * 0 if the request to start the VM was sent successfully. 71 * ENOMEM if a memory allocation failure occurred. 72 */ 73 int 74 vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics, 75 char **nics, int ndisks, char **disks, int *disktypes, char *kernel, 76 char *iso, char *instance, unsigned int bootdevice) 77 { 78 struct vmop_create_params *vmc; 79 struct vm_create_params *vcp; 80 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 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 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 imsg_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 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 imsg_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 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 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 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 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 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 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 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 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 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 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 */ 695 int 696 add_info(struct imsg *imsg, int *ret) 697 { 698 static size_t ct = 0; 699 static struct vmop_info_result *vir = NULL; 700 701 if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) { 702 vir = reallocarray(vir, ct + 1, 703 sizeof(struct vmop_info_result)); 704 if (vir == NULL) { 705 *ret = ENOMEM; 706 return (1); 707 } 708 memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result)); 709 ct++; 710 *ret = 0; 711 return (0); 712 } else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) { 713 switch (info_action) { 714 case CMD_CONSOLE: 715 vm_console(vir, ct); 716 break; 717 case CMD_STOPALL: 718 terminate_all(vir, ct, info_flags); 719 break; 720 default: 721 print_vm_info(vir, ct); 722 break; 723 } 724 free(vir); 725 *ret = 0; 726 return (1); 727 } else { 728 *ret = EINVAL; 729 return (1); 730 } 731 } 732 733 /* 734 * vm_state 735 * 736 * Returns a string representing the current VM state, note that the order 737 * matters. A paused VM does have the VM_STATE_RUNNING bit set, but 738 * VM_STATE_PAUSED is more significant to report. Same goes for stopping VMs. 739 * 740 * Parameters 741 * vm_state: mask indicating the vm state 742 */ 743 const char * 744 vm_state(unsigned int mask) 745 { 746 if (mask & VM_STATE_PAUSED) 747 return "paused"; 748 else if (mask & VM_STATE_WAITING) 749 return "waiting"; 750 else if (mask & VM_STATE_SHUTDOWN) 751 return "stopping"; 752 else if (mask & VM_STATE_RUNNING) 753 return "running"; 754 /* Presence of absence of other flags */ 755 else if (!mask || (mask & VM_STATE_DISABLED)) 756 return "stopped"; 757 758 return "unknown"; 759 } 760 761 /* 762 * print_vm_info 763 * 764 * Prints the vm information returned from vmd in 'list' to stdout. 765 * 766 * Parameters 767 * list: the vm information (consolidated) returned from vmd via imsg 768 * ct : the size (number of elements in 'list') of the result 769 */ 770 void 771 print_vm_info(struct vmop_info_result *list, size_t ct) 772 { 773 struct vm_info_result *vir; 774 struct vmop_info_result *vmi; 775 size_t i; 776 char *tty; 777 char curmem[FMT_SCALED_STRSIZE]; 778 char maxmem[FMT_SCALED_STRSIZE]; 779 char user[16], group[16]; 780 const char *name; 781 int running; 782 783 printf("%5s %5s %5s %7s %7s %7s %12s %8s %s\n", "ID", "PID", "VCPUS", 784 "MAXMEM", "CURMEM", "TTY", "OWNER", "STATE", "NAME"); 785 786 for (i = 0; i < ct; i++) { 787 vmi = &list[i]; 788 vir = &vmi->vir_info; 789 running = (vir->vir_creator_pid != 0 && vir->vir_id != 0); 790 if (check_info_id(vir->vir_name, vir->vir_id)) { 791 /* get user name */ 792 name = user_from_uid(vmi->vir_uid, 1); 793 if (name == NULL) 794 (void)snprintf(user, sizeof(user), 795 "%d", vmi->vir_uid); 796 else 797 (void)strlcpy(user, name, sizeof(user)); 798 /* get group name */ 799 if (vmi->vir_gid != -1) { 800 name = group_from_gid(vmi->vir_gid, 1); 801 if (name == NULL) 802 (void)snprintf(group, sizeof(group), 803 ":%lld", vmi->vir_gid); 804 else 805 (void)snprintf(group, sizeof(group), 806 ":%s", name); 807 (void)strlcat(user, group, sizeof(user)); 808 } 809 810 (void)strlcpy(curmem, "-", sizeof(curmem)); 811 (void)strlcpy(maxmem, "-", sizeof(maxmem)); 812 813 (void)fmt_scaled(vir->vir_memory_size, maxmem); 814 815 if (running) { 816 if (*vmi->vir_ttyname == '\0') 817 tty = "-"; 818 /* get tty - skip /dev/ path */ 819 else if ((tty = strrchr(vmi->vir_ttyname, 820 '/')) == NULL || *++tty == '\0') 821 tty = list[i].vir_ttyname; 822 823 (void)fmt_scaled(vir->vir_used_size, curmem); 824 825 /* running vm */ 826 printf("%5u %5u %5zd %7s %7s %7s %12s %8s %s\n", 827 vir->vir_id, vir->vir_creator_pid, 828 vir->vir_ncpus, maxmem, curmem, 829 tty, user, vm_state(vmi->vir_state), 830 vir->vir_name); 831 } else { 832 /* disabled vm */ 833 printf("%5u %5s %5zd %7s %7s %7s %12s %8s %s\n", 834 vir->vir_id, "-", 835 vir->vir_ncpus, maxmem, curmem, 836 "-", user, vm_state(vmi->vir_state), 837 vir->vir_name); 838 } 839 } 840 } 841 } 842 843 /* 844 * vm_console 845 * 846 * Connects to the vm console returned from vmd in 'list'. 847 * 848 * Parameters 849 * list: the vm information (consolidated) returned from vmd via imsg 850 * ct : the size (number of elements in 'list') of the result 851 */ 852 __dead void 853 vm_console(struct vmop_info_result *list, size_t ct) 854 { 855 struct vmop_info_result *vir; 856 size_t i; 857 858 for (i = 0; i < ct; i++) { 859 vir = &list[i]; 860 if ((check_info_id(vir->vir_info.vir_name, 861 vir->vir_info.vir_id) > 0) && 862 (vir->vir_ttyname[0] != '\0')) { 863 /* does not return */ 864 ctl_openconsole(vir->vir_ttyname); 865 } 866 } 867 868 errx(1, "console not found"); 869 } 870 871 /* 872 * open_imagefile 873 * 874 * Open an imagefile with the specified type, path and size. 875 * 876 * Parameters: 877 * type : format of the image file 878 * imgfile_path: path to the image file to create 879 * flags : flags for open(2), e.g. O_RDONLY 880 * file : file structure 881 * sz : size of the image file 882 * 883 * Return: 884 * fd : Returns file descriptor of the new image file 885 * -1 : Operation failed. errno is set. 886 */ 887 int 888 open_imagefile(int type, const char *imgfile_path, int flags, 889 struct virtio_backing *file, off_t *sz) 890 { 891 int fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i; 892 char path[PATH_MAX]; 893 894 *sz = 0; 895 if ((fd = open(imgfile_path, flags)) == -1) 896 return (-1); 897 898 basefd[0] = fd; 899 nfd = 1; 900 901 errno = 0; 902 switch (type) { 903 case VMDF_QCOW2: 904 if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path)) 905 return (-1); 906 for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) { 907 if ((ret = virtio_qcow2_get_base(basefd[i], 908 path, sizeof(path), imgfile_path)) == -1) { 909 log_debug("%s: failed to get base %d", __func__, i); 910 return -1; 911 } else if (ret == 0) 912 break; 913 914 if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) { 915 log_warn("%s: failed to open base %s", 916 __func__, path); 917 return (-1); 918 } 919 } 920 ret = virtio_qcow2_init(file, sz, basefd, nfd); 921 break; 922 default: 923 ret = virtio_raw_init(file, sz, &fd, 1); 924 break; 925 } 926 927 if (ret == -1) { 928 for (i = 0; i < nfd; i++) 929 close(basefd[i]); 930 return (-1); 931 } 932 933 return (fd); 934 } 935 936 /* 937 * create_imagefile 938 * 939 * Create an empty imagefile with the specified type, path and size. 940 * 941 * Parameters: 942 * type : format of the image file 943 * imgfile_path: path to the image file to create 944 * base_path : path to the qcow2 base image 945 * imgsize : size of the image file to create (in bytes) 946 * format : string identifying the format 947 * 948 * Return: 949 * EEXIST: The requested image file already exists 950 * 0 : Image file successfully created 951 * Exxxx : Various other Exxxx errno codes due to other I/O errors 952 */ 953 int 954 create_imagefile(int type, const char *imgfile_path, const char *base_path, 955 uint64_t imgsize, const char **format) 956 { 957 int ret; 958 959 switch (type) { 960 case VMDF_QCOW2: 961 *format = "qcow2"; 962 ret = virtio_qcow2_create(imgfile_path, base_path, imgsize); 963 break; 964 default: 965 *format = "raw"; 966 ret = virtio_raw_create(imgfile_path, imgsize); 967 break; 968 } 969 970 return (ret); 971 } 972