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