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 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 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 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 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 * ENOENT: no entries 695 */ 696 int 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 * 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 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 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 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 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