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