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