1*dd7efffeSclaudio /* $OpenBSD: vmctl.c,v 1.92 2024/11/21 13:17:02 claudio Exp $ */ 2fb548b91Sreyk 3eb8d1da1Sreyk /* 4eb8d1da1Sreyk * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> 5eb8d1da1Sreyk * 6eb8d1da1Sreyk * Permission to use, copy, modify, and distribute this software for any 7eb8d1da1Sreyk * purpose with or without fee is hereby granted, provided that the above 8eb8d1da1Sreyk * copyright notice and this permission notice appear in all copies. 9eb8d1da1Sreyk * 10eb8d1da1Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11eb8d1da1Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12eb8d1da1Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13eb8d1da1Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14eb8d1da1Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15eb8d1da1Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16eb8d1da1Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17eb8d1da1Sreyk */ 18eb8d1da1Sreyk 19eb8d1da1Sreyk #include <sys/queue.h> 20eb8d1da1Sreyk #include <sys/uio.h> 21eb8d1da1Sreyk #include <sys/stat.h> 22eb8d1da1Sreyk #include <sys/socket.h> 23eb8d1da1Sreyk #include <sys/un.h> 24eb8d1da1Sreyk 25e9affaf6Sjasper #include <ctype.h> 26eb8d1da1Sreyk #include <err.h> 27eb8d1da1Sreyk #include <errno.h> 28eb8d1da1Sreyk #include <fcntl.h> 29eb8d1da1Sreyk #include <imsg.h> 30eb8d1da1Sreyk #include <limits.h> 31eb8d1da1Sreyk #include <stdio.h> 32eb8d1da1Sreyk #include <stdlib.h> 33eb8d1da1Sreyk #include <string.h> 34eb8d1da1Sreyk #include <unistd.h> 35b8a9a3ffSreyk #include <util.h> 36e5d5b350Sreyk #include <pwd.h> 37e5d5b350Sreyk #include <grp.h> 38eb8d1da1Sreyk 39eb8d1da1Sreyk #include "vmd.h" 404d2a1fb2Sreyk #include "virtio.h" 41eb8d1da1Sreyk #include "vmctl.h" 42eed20f3bSpd #include "atomicio.h" 43eb8d1da1Sreyk 44eb8d1da1Sreyk extern char *__progname; 45eb8d1da1Sreyk uint32_t info_id; 4659fb7762Sreyk char info_name[VMM_MAX_NAME_LEN]; 47e0b12962Sreyk enum actions info_action; 48e0b12962Sreyk unsigned int info_flags; 49eb8d1da1Sreyk 507bfb33a3Sderaadt struct imsgbuf *ibuf; 517bfb33a3Sderaadt 52eb8d1da1Sreyk /* 5323659fc3Smlarkin * vm_start 54eb8d1da1Sreyk * 55eb8d1da1Sreyk * Request vmd to start the VM defined by the supplied parameters 56eb8d1da1Sreyk * 57eb8d1da1Sreyk * Parameters: 58eb1cd41dSreyk * start_id: optional ID of the VM 59eb8d1da1Sreyk * name: optional name of the VM 60ead1b146Sdv * memsize: memory size (in bytes) of the VM to create 61eb8d1da1Sreyk * nnics: number of vionet network interfaces to create 62b657d36cSreyk * nics: switch names of the network interfaces to create 63eb8d1da1Sreyk * ndisks: number of disk images 64eb8d1da1Sreyk * disks: disk image file names 65eb8d1da1Sreyk * kernel: kernel image to load 6695ab188fSccardenas * iso: iso image file 676429e633Sreyk * instance: create instance from vm 68eb8d1da1Sreyk * 69eb8d1da1Sreyk * Return: 70eb8d1da1Sreyk * 0 if the request to start the VM was sent successfully. 71eb8d1da1Sreyk * ENOMEM if a memory allocation failure occurred. 72eb8d1da1Sreyk */ 73eb8d1da1Sreyk int 74e545c54cSdv vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics, 75f224f92aSccardenas char **nics, int ndisks, char **disks, int *disktypes, char *kernel, 76917458a3Sclaudio char *iso, char *instance, unsigned int bootdevice) 77eb8d1da1Sreyk { 7881994c8fSreyk struct vmop_create_params *vmc; 79eb8d1da1Sreyk struct vm_create_params *vcp; 800e4dbc4aSdv struct stat sb; 81619bf29aSreyk unsigned int flags = 0; 82eb8d1da1Sreyk int i; 83e9affaf6Sjasper const char *s; 84eb8d1da1Sreyk 85b848b186Sdv if (kernel) { 86b848b186Sdv if (unveil(kernel, "r") == -1) 87b848b186Sdv err(1, "unveil boot kernel"); 88b848b186Sdv } else { 89b848b186Sdv /* We can drop sendfd promise. */ 90b848b186Sdv if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1) 91b848b186Sdv err(1, "pledge"); 92b848b186Sdv } 93b848b186Sdv 94619bf29aSreyk if (memsize) 95619bf29aSreyk flags |= VMOP_CREATE_MEMORY; 96619bf29aSreyk if (nnics) 97619bf29aSreyk flags |= VMOP_CREATE_NETWORK; 98619bf29aSreyk if (ndisks) 99619bf29aSreyk flags |= VMOP_CREATE_DISK; 100619bf29aSreyk if (kernel) 101619bf29aSreyk flags |= VMOP_CREATE_KERNEL; 10295ab188fSccardenas if (iso) 10395ab188fSccardenas flags |= VMOP_CREATE_CDROM; 1046429e633Sreyk if (instance) 1056429e633Sreyk flags |= VMOP_CREATE_INSTANCE; 1066429e633Sreyk else if (flags != 0) { 107d79c5b04Smlarkin if (memsize < 1) 10885b9df96Sreyk memsize = VM_DEFAULT_MEMORY; 109d489aa7eSdv if (ndisks > VM_MAX_DISKS_PER_VM) 110d79c5b04Smlarkin errx(1, "too many disks"); 111b848b186Sdv else if (kernel == NULL && ndisks == 0) 112d79c5b04Smlarkin warnx("starting without disks"); 113d9672895Sreyk if (kernel == NULL && ndisks == 0 && !iso) 114d9672895Sreyk errx(1, "no kernel or disk/cdrom specified"); 115d79c5b04Smlarkin if (nnics == -1) 116d79c5b04Smlarkin nnics = 0; 117d489aa7eSdv if (nnics > VM_MAX_NICS_PER_VM) 118b657d36cSreyk errx(1, "too many network interfaces"); 119b848b186Sdv if (kernel == NULL && nnics == 0) 120d79c5b04Smlarkin warnx("starting without network interfaces"); 121619bf29aSreyk } 122d79c5b04Smlarkin 123eab0cc3bSreyk if ((vmc = calloc(1, sizeof(struct vmop_create_params))) == NULL) 124eb8d1da1Sreyk return (ENOMEM); 125b848b186Sdv vmc->vmc_kernel = -1; 126619bf29aSreyk vmc->vmc_flags = flags; 127619bf29aSreyk 12881994c8fSreyk /* vcp includes configuration that is shared with the kernel */ 12981994c8fSreyk vcp = &vmc->vmc_params; 130eb8d1da1Sreyk 13140a3b6a0Sstefan /* 13240a3b6a0Sstefan * XXX: vmd(8) fills in the actual memory ranges. vmctl(8) 133e545c54cSdv * just passes in the actual memory size here. 13440a3b6a0Sstefan */ 13540a3b6a0Sstefan vcp->vcp_nmemranges = 1; 13640a3b6a0Sstefan vcp->vcp_memranges[0].vmr_size = memsize; 13740a3b6a0Sstefan 138eb8d1da1Sreyk vcp->vcp_ncpus = 1; 139eb1cd41dSreyk vcp->vcp_id = start_id; 140eb8d1da1Sreyk 14173a98491Sdv vmc->vmc_ndisks = ndisks; 14273a98491Sdv vmc->vmc_nnics = nnics; 14373a98491Sdv 144f224f92aSccardenas for (i = 0 ; i < ndisks; i++) { 14573a98491Sdv if (strlcpy(vmc->vmc_disks[i], disks[i], 14673a98491Sdv sizeof(vmc->vmc_disks[i])) >= 14773a98491Sdv sizeof(vmc->vmc_disks[i])) 148eab0cc3bSreyk errx(1, "disk path too long"); 149f224f92aSccardenas vmc->vmc_disktypes[i] = disktypes[i]; 150f224f92aSccardenas } 151d4a2df92Sclaudio for (i = 0 ; i < nnics; i++) { 152d4a2df92Sclaudio vmc->vmc_ifflags[i] = VMIFF_UP; 153470adcf5Sreyk 154470adcf5Sreyk if (strcmp(".", nics[i]) == 0) { 155470adcf5Sreyk /* Add a "local" interface */ 156eab0cc3bSreyk (void)strlcpy(vmc->vmc_ifswitch[i], "", 157eab0cc3bSreyk sizeof(vmc->vmc_ifswitch[i])); 158470adcf5Sreyk vmc->vmc_ifflags[i] |= VMIFF_LOCAL; 159470adcf5Sreyk } else { 1606fd9d8e9Sjasper /* Add an interface to a switch */ 161eab0cc3bSreyk if (strlcpy(vmc->vmc_ifswitch[i], nics[i], 162eab0cc3bSreyk sizeof(vmc->vmc_ifswitch[i])) >= 163eab0cc3bSreyk sizeof(vmc->vmc_ifswitch[i])) 164eab0cc3bSreyk errx(1, "interface name too long"); 165470adcf5Sreyk } 166d4a2df92Sclaudio } 167e9affaf6Sjasper if (name != NULL) { 1686d26286fSreyk /* 1696d26286fSreyk * Allow VMs names with alphanumeric characters, dot, hyphen 170e9affaf6Sjasper * and underscore. But disallow dot, hyphen and underscore at 171e9affaf6Sjasper * the start. 172e9affaf6Sjasper */ 173e9affaf6Sjasper if (*name == '-' || *name == '.' || *name == '_') 1746d26286fSreyk errx(1, "invalid VM name"); 175e9affaf6Sjasper 176e9affaf6Sjasper for (s = name; *s != '\0'; ++s) { 177e9affaf6Sjasper if (!(isalnum(*s) || *s == '.' || *s == '-' || 178e9affaf6Sjasper *s == '_')) 1796d26286fSreyk errx(1, "invalid VM name"); 180e9affaf6Sjasper } 181e9affaf6Sjasper 182eab0cc3bSreyk if (strlcpy(vcp->vcp_name, name, 183eab0cc3bSreyk sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) 184eab0cc3bSreyk errx(1, "vm name too long"); 185e9affaf6Sjasper } 186b848b186Sdv if (kernel != NULL) { 187b848b186Sdv if (strnlen(kernel, PATH_MAX) == PATH_MAX) 188eab0cc3bSreyk errx(1, "kernel name too long"); 189b848b186Sdv vmc->vmc_kernel = open(kernel, O_RDONLY); 190b848b186Sdv if (vmc->vmc_kernel == -1) 191b848b186Sdv err(1, "cannot open kernel '%s'", kernel); 1920e4dbc4aSdv memset(&sb, 0, sizeof(sb)); 1930e4dbc4aSdv if (fstat(vmc->vmc_kernel, &sb) == -1) 1940e4dbc4aSdv err(1, "fstat kernel"); 1950e4dbc4aSdv if (!S_ISREG(sb.st_mode)) 1960e4dbc4aSdv errx(1, "kernel must be a regular file"); 197b848b186Sdv } 19895ab188fSccardenas if (iso != NULL) 19973a98491Sdv if (strlcpy(vmc->vmc_cdrom, iso, 20073a98491Sdv sizeof(vmc->vmc_cdrom)) >= sizeof(vmc->vmc_cdrom)) 201eab0cc3bSreyk errx(1, "cdrom name too long"); 2026429e633Sreyk if (instance != NULL) 2036429e633Sreyk if (strlcpy(vmc->vmc_instance, instance, 2046429e633Sreyk sizeof(vmc->vmc_instance)) >= sizeof(vmc->vmc_instance)) 2056429e633Sreyk errx(1, "instance vm name too long"); 206917458a3Sclaudio vmc->vmc_bootdevice = bootdevice; 20795ab188fSccardenas 208b848b186Sdv imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, vmc->vmc_kernel, 20981994c8fSreyk vmc, sizeof(struct vmop_create_params)); 210eb8d1da1Sreyk 2111e337eb1Sjsg free(vmc); 212eb8d1da1Sreyk return (0); 213eb8d1da1Sreyk } 214eb8d1da1Sreyk 215eb8d1da1Sreyk /* 21676031b44Sreyk * vm_start_complete 217eb8d1da1Sreyk * 218eb8d1da1Sreyk * Callback function invoked when we are expecting an 219eb8d1da1Sreyk * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of 220eb8d1da1Sreyk * a start vm operation. 221eb8d1da1Sreyk * 222eb8d1da1Sreyk * Parameters: 223eb8d1da1Sreyk * imsg : response imsg received from vmd 224eb8d1da1Sreyk * ret : return value 225fe102934Sreyk * autoconnect : open the console after startup 226eb8d1da1Sreyk * 227eb8d1da1Sreyk * Return: 228eb8d1da1Sreyk * Always 1 to indicate we have processed the return message (even if it 229eb8d1da1Sreyk * was an incorrect/failure message) 230eb8d1da1Sreyk * 231eb8d1da1Sreyk * The function also sets 'ret' to the error code as follows: 232eb8d1da1Sreyk * 0 : Message successfully processed 233eb8d1da1Sreyk * EINVAL: Invalid or unexpected response from vmd 23476031b44Sreyk * EIO : vm_start command failed 2354ca02d76Smlarkin * ENOENT: a specified component of the VM could not be found (disk image, 2364ca02d76Smlarkin * BIOS firmware image, etc) 237eb8d1da1Sreyk */ 238eb8d1da1Sreyk int 23976031b44Sreyk vm_start_complete(struct imsg *imsg, int *ret, int autoconnect) 240eb8d1da1Sreyk { 24148665f9bSreyk struct vmop_result *vmr; 242eb8d1da1Sreyk int res; 243eb8d1da1Sreyk 244eb8d1da1Sreyk if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) { 24548665f9bSreyk vmr = (struct vmop_result *)imsg->data; 246eb8d1da1Sreyk res = vmr->vmr_result; 247eb8d1da1Sreyk if (res) { 2484ca02d76Smlarkin switch (res) { 2494ca02d76Smlarkin case VMD_BIOS_MISSING: 2504ca02d76Smlarkin warnx("vmm bios firmware file not found."); 2514ca02d76Smlarkin *ret = ENOENT; 2524ca02d76Smlarkin break; 2534ca02d76Smlarkin case VMD_DISK_MISSING: 2546429e633Sreyk warnx("could not open disk image(s)"); 255ad4191f5Sjasper *ret = ENOENT; 2564ca02d76Smlarkin break; 25795ab188fSccardenas case VMD_CDROM_MISSING: 25895ab188fSccardenas warnx("could not find specified iso image"); 25995ab188fSccardenas *ret = ENOENT; 26095ab188fSccardenas break; 26195ab188fSccardenas case VMD_CDROM_INVALID: 262a8df2bc2Smlarkin warnx("specified iso image is not a regular " 263a8df2bc2Smlarkin "file"); 26495ab188fSccardenas *ret = ENOENT; 26595ab188fSccardenas break; 266baf88fe3Santon case VMD_PARENT_INVALID: 267baf88fe3Santon warnx("invalid template"); 268baf88fe3Santon *ret = EINVAL; 269baf88fe3Santon break; 2704ca02d76Smlarkin default: 2714ca02d76Smlarkin errno = res; 27248665f9bSreyk warn("start vm command failed"); 273eb8d1da1Sreyk *ret = EIO; 274ad4191f5Sjasper } 275fe102934Sreyk } else if (autoconnect) { 27695009bb4Sreyk /* does not return */ 27795009bb4Sreyk ctl_openconsole(vmr->vmr_ttyname); 278eb8d1da1Sreyk } else { 27948665f9bSreyk warnx("started vm %d successfully, tty %s", 28048665f9bSreyk vmr->vmr_id, vmr->vmr_ttyname); 281eb8d1da1Sreyk *ret = 0; 282eb8d1da1Sreyk } 283eb8d1da1Sreyk } else { 28448665f9bSreyk warnx("unexpected response received from vmd"); 285eb8d1da1Sreyk *ret = EINVAL; 286eb8d1da1Sreyk } 287eb8d1da1Sreyk 288eb8d1da1Sreyk return (1); 289eb8d1da1Sreyk } 290eb8d1da1Sreyk 29152e954a3Spd void 292eed20f3bSpd send_vm(uint32_t id, const char *name) 293eed20f3bSpd { 294eed20f3bSpd struct vmop_id vid; 295eed20f3bSpd int fds[2], readn, writen; 2960ea90cb8Smlarkin long pagesz; 2970ea90cb8Smlarkin char *buf; 2980ea90cb8Smlarkin 2990ea90cb8Smlarkin pagesz = getpagesize(); 3000ea90cb8Smlarkin buf = malloc(pagesz); 3010ea90cb8Smlarkin if (buf == NULL) 3020ea90cb8Smlarkin errx(1, "%s: memory allocation failure", __func__); 303eed20f3bSpd 304eed20f3bSpd memset(&vid, 0, sizeof(vid)); 305eed20f3bSpd vid.vid_id = id; 306eed20f3bSpd if (name != NULL) 307eed20f3bSpd strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 308eed20f3bSpd if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) { 309eed20f3bSpd warnx("%s: socketpair creation failed", __func__); 310eed20f3bSpd } else { 311eed20f3bSpd imsg_compose(ibuf, IMSG_VMDOP_SEND_VM_REQUEST, 0, 0, fds[0], 312eed20f3bSpd &vid, sizeof(vid)); 313*dd7efffeSclaudio imsgbuf_flush(ibuf); 314eed20f3bSpd while (1) { 3150ea90cb8Smlarkin readn = atomicio(read, fds[1], buf, pagesz); 316eed20f3bSpd if (!readn) 317eed20f3bSpd break; 318eed20f3bSpd writen = atomicio(vwrite, STDOUT_FILENO, buf, 319eed20f3bSpd readn); 320eed20f3bSpd if (writen != readn) 321eed20f3bSpd break; 322eed20f3bSpd } 323eed20f3bSpd if (vid.vid_id) 324eed20f3bSpd warnx("sent vm %d successfully", vid.vid_id); 325eed20f3bSpd else 326eed20f3bSpd warnx("sent vm %s successfully", vid.vid_name); 327eed20f3bSpd } 3280ea90cb8Smlarkin 3290ea90cb8Smlarkin free(buf); 330eed20f3bSpd } 331eed20f3bSpd 332eed20f3bSpd void 333eed20f3bSpd vm_receive(uint32_t id, const char *name) 334eed20f3bSpd { 335eed20f3bSpd struct vmop_id vid; 336eed20f3bSpd int fds[2], readn, writen; 3370ea90cb8Smlarkin long pagesz; 3380ea90cb8Smlarkin char *buf; 3390ea90cb8Smlarkin 3400ea90cb8Smlarkin pagesz = getpagesize(); 3410ea90cb8Smlarkin buf = malloc(pagesz); 3420ea90cb8Smlarkin if (buf == NULL) 3430ea90cb8Smlarkin errx(1, "%s: memory allocation failure", __func__); 344eed20f3bSpd 345eed20f3bSpd memset(&vid, 0, sizeof(vid)); 346eed20f3bSpd if (name != NULL) 347eed20f3bSpd strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 348eed20f3bSpd if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) { 349eed20f3bSpd warnx("%s: socketpair creation failed", __func__); 350eed20f3bSpd } else { 351eed20f3bSpd imsg_compose(ibuf, IMSG_VMDOP_RECEIVE_VM_REQUEST, 0, 0, fds[0], 352eed20f3bSpd &vid, sizeof(vid)); 353*dd7efffeSclaudio imsgbuf_flush(ibuf); 354eed20f3bSpd while (1) { 3550ea90cb8Smlarkin readn = atomicio(read, STDIN_FILENO, buf, pagesz); 356eed20f3bSpd if (!readn) { 357eed20f3bSpd close(fds[1]); 358eed20f3bSpd break; 359eed20f3bSpd } 360eed20f3bSpd writen = atomicio(vwrite, fds[1], buf, readn); 361eed20f3bSpd if (writen != readn) 362eed20f3bSpd break; 363eed20f3bSpd } 364eed20f3bSpd } 3650ea90cb8Smlarkin 3660ea90cb8Smlarkin free(buf); 367eed20f3bSpd } 368eed20f3bSpd 369eed20f3bSpd void 37052e954a3Spd pause_vm(uint32_t pause_id, const char *name) 37152e954a3Spd { 37252e954a3Spd struct vmop_id vid; 37352e954a3Spd 37452e954a3Spd memset(&vid, 0, sizeof(vid)); 37552e954a3Spd vid.vid_id = pause_id; 37652e954a3Spd if (name != NULL) 37752e954a3Spd (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 37852e954a3Spd 37952e954a3Spd imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1, 38052e954a3Spd &vid, sizeof(vid)); 38152e954a3Spd } 38252e954a3Spd 38352e954a3Spd int 38452e954a3Spd pause_vm_complete(struct imsg *imsg, int *ret) 38552e954a3Spd { 38652e954a3Spd struct vmop_result *vmr; 38752e954a3Spd int res; 38852e954a3Spd 38952e954a3Spd if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) { 39052e954a3Spd vmr = (struct vmop_result *)imsg->data; 39152e954a3Spd res = vmr->vmr_result; 39252e954a3Spd if (res) { 39352e954a3Spd errno = res; 39452e954a3Spd warn("pause vm command failed"); 39552e954a3Spd *ret = EIO; 39652e954a3Spd } else { 39752e954a3Spd warnx("paused vm %d successfully", vmr->vmr_id); 39852e954a3Spd *ret = 0; 39952e954a3Spd } 40052e954a3Spd } else { 40152e954a3Spd warnx("unexpected response received from vmd"); 40252e954a3Spd *ret = EINVAL; 40352e954a3Spd } 40452e954a3Spd 40552e954a3Spd return (1); 40652e954a3Spd } 40752e954a3Spd 40852e954a3Spd void 40952e954a3Spd unpause_vm(uint32_t pause_id, const char *name) 41052e954a3Spd { 41152e954a3Spd struct vmop_id vid; 41252e954a3Spd 41352e954a3Spd memset(&vid, 0, sizeof(vid)); 41452e954a3Spd vid.vid_id = pause_id; 41552e954a3Spd if (name != NULL) 41652e954a3Spd (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 41752e954a3Spd 41852e954a3Spd imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1, 41952e954a3Spd &vid, sizeof(vid)); 42052e954a3Spd } 42152e954a3Spd 42252e954a3Spd int 42352e954a3Spd unpause_vm_complete(struct imsg *imsg, int *ret) 42452e954a3Spd { 42552e954a3Spd struct vmop_result *vmr; 42652e954a3Spd int res; 42752e954a3Spd 42852e954a3Spd if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) { 42952e954a3Spd vmr = (struct vmop_result *)imsg->data; 43052e954a3Spd res = vmr->vmr_result; 43152e954a3Spd if (res) { 43252e954a3Spd errno = res; 43352e954a3Spd warn("unpause vm command failed"); 43452e954a3Spd *ret = EIO; 43552e954a3Spd } else { 43652e954a3Spd warnx("unpaused vm %d successfully", vmr->vmr_id); 43752e954a3Spd *ret = 0; 43852e954a3Spd } 43952e954a3Spd } else { 44052e954a3Spd warnx("unexpected response received from vmd"); 44152e954a3Spd *ret = EINVAL; 44252e954a3Spd } 44352e954a3Spd 44452e954a3Spd return (1); 44552e954a3Spd } 44652e954a3Spd 447eb8d1da1Sreyk /* 448eb8d1da1Sreyk * terminate_vm 449eb8d1da1Sreyk * 450eb8d1da1Sreyk * Request vmd to stop the VM indicated 451eb8d1da1Sreyk * 452eb8d1da1Sreyk * Parameters: 453eb8d1da1Sreyk * terminate_id: ID of the vm to be terminated 454822fd566Sreyk * name: optional name of the VM to be terminated 4553be9785fSreyk * flags: VMOP_FORCE or VMOP_WAIT flags 456eb8d1da1Sreyk */ 457eb8d1da1Sreyk void 4583be9785fSreyk terminate_vm(uint32_t terminate_id, const char *name, unsigned int flags) 459eb8d1da1Sreyk { 46059fb7762Sreyk struct vmop_id vid; 461eb8d1da1Sreyk 46259fb7762Sreyk memset(&vid, 0, sizeof(vid)); 46359fb7762Sreyk vid.vid_id = terminate_id; 464e0b12962Sreyk if (name != NULL) { 46559fb7762Sreyk (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 466e0b12962Sreyk fprintf(stderr, "stopping vm %s: ", name); 467e0b12962Sreyk } else { 468e0b12962Sreyk fprintf(stderr, "stopping vm: "); 469e0b12962Sreyk } 470eb8d1da1Sreyk 4713be9785fSreyk vid.vid_flags = flags & (VMOP_FORCE|VMOP_WAIT); 472f6e5c9ebSreyk 4733be9785fSreyk imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST, 4743be9785fSreyk 0, 0, -1, &vid, sizeof(vid)); 475eb8d1da1Sreyk } 476eb8d1da1Sreyk 477eb8d1da1Sreyk /* 478eb8d1da1Sreyk * terminate_vm_complete 479eb8d1da1Sreyk * 480540e29f5Sdv * Callback function invoked when we are waiting for the response from an 481540e29f5Sdv * IMSG_VMDOP_TERMINATE_VM_REQUEST. We expect a reply of either an 482540e29f5Sdv * IMSG_VMDOP_TERMINATE_VM_EVENT indicating the termination of a vm or an 483540e29f5Sdv * IMSG_VMDOP_TERMINATE_VM_RESPONSE with a success/failure result. 484eb8d1da1Sreyk * 485eb8d1da1Sreyk * Parameters: 486eb8d1da1Sreyk * imsg : response imsg received from vmd 487eb8d1da1Sreyk * ret : return value 4883be9785fSreyk * flags: VMOP_FORCE or VMOP_WAIT flags 489eb8d1da1Sreyk * 490eb8d1da1Sreyk * Return: 491eb8d1da1Sreyk * Always 1 to indicate we have processed the return message (even if it 492eb8d1da1Sreyk * was an incorrect/failure message) 493eb8d1da1Sreyk * 494eb8d1da1Sreyk * The function also sets 'ret' to the error code as follows: 495eb8d1da1Sreyk * 0 : Message successfully processed 496eb8d1da1Sreyk * EINVAL: Invalid or unexpected response from vmd 497eb8d1da1Sreyk * EIO : terminate_vm command failed 498eb8d1da1Sreyk */ 499eb8d1da1Sreyk int 5003be9785fSreyk terminate_vm_complete(struct imsg *imsg, int *ret, unsigned int flags) 501eb8d1da1Sreyk { 50248665f9bSreyk struct vmop_result *vmr; 503eb8d1da1Sreyk int res; 504eb8d1da1Sreyk 505540e29f5Sdv switch (imsg->hdr.type) { 506540e29f5Sdv case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 507540e29f5Sdv IMSG_SIZE_CHECK(imsg, &vmr); 50848665f9bSreyk vmr = (struct vmop_result *)imsg->data; 50948665f9bSreyk res = vmr->vmr_result; 510540e29f5Sdv 5112da5c9dbSmlarkin switch (res) { 512540e29f5Sdv case 0: 513540e29f5Sdv fprintf(stderr, "requested to shutdown vm %d\n", 514540e29f5Sdv vmr->vmr_id); 515540e29f5Sdv *ret = 0; 516540e29f5Sdv break; 5172da5c9dbSmlarkin case VMD_VM_STOP_INVALID: 518e0b12962Sreyk fprintf(stderr, 519e0b12962Sreyk "cannot stop vm that is not running\n"); 5202da5c9dbSmlarkin *ret = EINVAL; 5212da5c9dbSmlarkin break; 5222da5c9dbSmlarkin case ENOENT: 523e0b12962Sreyk fprintf(stderr, "vm not found\n"); 5242da5c9dbSmlarkin *ret = EIO; 5252da5c9dbSmlarkin break; 526583f6618Sclaudio case EINTR: 527583f6618Sclaudio fprintf(stderr, "interrupted call\n"); 528583f6618Sclaudio *ret = EIO; 529583f6618Sclaudio break; 5302da5c9dbSmlarkin default: 531574a6dcbSanton errno = res; 532e0b12962Sreyk fprintf(stderr, "failed: %s\n", 533e0b12962Sreyk strerror(res)); 534eb8d1da1Sreyk *ret = EIO; 5352da5c9dbSmlarkin } 536540e29f5Sdv break; 537540e29f5Sdv case IMSG_VMDOP_TERMINATE_VM_EVENT: 538540e29f5Sdv IMSG_SIZE_CHECK(imsg, &vmr); 539540e29f5Sdv vmr = (struct vmop_result *)imsg->data; 540540e29f5Sdv if (flags & VMOP_WAIT) { 541e0b12962Sreyk fprintf(stderr, "terminated vm %d\n", vmr->vmr_id); 5423be9785fSreyk } else if (flags & VMOP_FORCE) { 543e0b12962Sreyk fprintf(stderr, "forced to terminate vm %d\n", 544e0b12962Sreyk vmr->vmr_id); 545eb8d1da1Sreyk } 546540e29f5Sdv *ret = 0; 547540e29f5Sdv break; 548540e29f5Sdv default: 549e0b12962Sreyk fprintf(stderr, "unexpected response received from vmd\n"); 550eb8d1da1Sreyk *ret = EINVAL; 551eb8d1da1Sreyk } 5522da5c9dbSmlarkin errno = *ret; 553eb8d1da1Sreyk 554eb8d1da1Sreyk return (1); 555eb8d1da1Sreyk } 556eb8d1da1Sreyk 557eb8d1da1Sreyk /* 558e0b12962Sreyk * terminate_all 559e0b12962Sreyk * 560e0b12962Sreyk * Request to stop all VMs gracefully 561e0b12962Sreyk * 562e0b12962Sreyk * Parameters 563e0b12962Sreyk * list: the vm information (consolidated) returned from vmd via imsg 564e0b12962Sreyk * ct : the size (number of elements in 'list') of the result 565e0b12962Sreyk * flags: VMOP_FORCE or VMOP_WAIT flags 566e0b12962Sreyk */ 567e0b12962Sreyk void 568e0b12962Sreyk terminate_all(struct vmop_info_result *list, size_t ct, unsigned int flags) 569e0b12962Sreyk { 570e0b12962Sreyk struct vm_info_result *vir; 571e0b12962Sreyk struct vmop_info_result *vmi; 572e0b12962Sreyk struct parse_result res; 573e0b12962Sreyk size_t i; 574e0b12962Sreyk 575e0b12962Sreyk for (i = 0; i < ct; i++) { 576e0b12962Sreyk vmi = &list[i]; 577e0b12962Sreyk vir = &vmi->vir_info; 578e0b12962Sreyk 579e0b12962Sreyk /* The VM is already stopped */ 580e0b12962Sreyk if (vir->vir_creator_pid == 0 || vir->vir_id == 0) 581e0b12962Sreyk continue; 582e0b12962Sreyk 583e0b12962Sreyk memset(&res, 0, sizeof(res)); 584e0b12962Sreyk res.action = CMD_STOP; 585e0b12962Sreyk res.id = 0; 586e0b12962Sreyk res.flags = info_flags; 587e0b12962Sreyk 588e0b12962Sreyk if ((res.name = strdup(vir->vir_name)) == NULL) 589e0b12962Sreyk errx(1, "strdup"); 590e0b12962Sreyk 591e0b12962Sreyk vmmaction(&res); 592e0b12962Sreyk } 593e0b12962Sreyk } 594e0b12962Sreyk 595e0b12962Sreyk /* 596583f6618Sclaudio * waitfor_vm 597583f6618Sclaudio * 598583f6618Sclaudio * Wait until vmd stopped the indicated VM 599583f6618Sclaudio * 600583f6618Sclaudio * Parameters: 601583f6618Sclaudio * terminate_id: ID of the vm to be terminated 602583f6618Sclaudio * name: optional name of the VM to be terminated 603583f6618Sclaudio */ 604583f6618Sclaudio void 605583f6618Sclaudio waitfor_vm(uint32_t terminate_id, const char *name) 606583f6618Sclaudio { 607583f6618Sclaudio struct vmop_id vid; 608583f6618Sclaudio 609583f6618Sclaudio memset(&vid, 0, sizeof(vid)); 610583f6618Sclaudio vid.vid_id = terminate_id; 611583f6618Sclaudio if (name != NULL) { 612583f6618Sclaudio (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 613583f6618Sclaudio fprintf(stderr, "waiting for vm %s: ", name); 614583f6618Sclaudio } else { 615583f6618Sclaudio fprintf(stderr, "waiting for vm: "); 616583f6618Sclaudio } 617583f6618Sclaudio 618583f6618Sclaudio imsg_compose(ibuf, IMSG_VMDOP_WAIT_VM_REQUEST, 619583f6618Sclaudio 0, 0, -1, &vid, sizeof(vid)); 620583f6618Sclaudio } 621583f6618Sclaudio 622583f6618Sclaudio /* 623eb8d1da1Sreyk * get_info_vm 624eb8d1da1Sreyk * 625822fd566Sreyk * Return the list of all running VMs or find a specific VM by ID or name. 626822fd566Sreyk * 627822fd566Sreyk * Parameters: 628822fd566Sreyk * id: optional ID of a VM to list 629822fd566Sreyk * name: optional name of a VM to list 630e0b12962Sreyk * action: if CMD_CONSOLE or CMD_STOP open a console or terminate the VM. 631e0b12962Sreyk * flags: optional flags used by the CMD_STOP action. 632822fd566Sreyk * 633eb8d1da1Sreyk * Request a list of running VMs from vmd 634eb8d1da1Sreyk */ 635eb8d1da1Sreyk void 636e0b12962Sreyk get_info_vm(uint32_t id, const char *name, enum actions action, 637e0b12962Sreyk unsigned int flags) 638eb8d1da1Sreyk { 639eb8d1da1Sreyk info_id = id; 64059fb7762Sreyk if (name != NULL) 64159fb7762Sreyk (void)strlcpy(info_name, name, sizeof(info_name)); 642e0b12962Sreyk info_action = action; 643e0b12962Sreyk info_flags = flags; 644eb8d1da1Sreyk imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0); 645eb8d1da1Sreyk } 646eb8d1da1Sreyk 647eb8d1da1Sreyk /* 6487aff4c38Smlarkin * check_info_id 64959fb7762Sreyk * 650822fd566Sreyk * Check if requested name or ID of a VM matches specified arguments 651822fd566Sreyk * 652822fd566Sreyk * Parameters: 653822fd566Sreyk * name: name of the VM 654822fd566Sreyk * id: ID of the VM 65559fb7762Sreyk */ 65659fb7762Sreyk int 65759fb7762Sreyk check_info_id(const char *name, uint32_t id) 65859fb7762Sreyk { 65959fb7762Sreyk if (info_id == 0 && *info_name == '\0') 66059fb7762Sreyk return (-1); 66159fb7762Sreyk if (info_id != 0 && info_id == id) 66259fb7762Sreyk return (1); 66359fb7762Sreyk if (*info_name != '\0' && name && strcmp(info_name, name) == 0) 66459fb7762Sreyk return (1); 66559fb7762Sreyk return (0); 66659fb7762Sreyk } 66759fb7762Sreyk 66859fb7762Sreyk /* 669eb8d1da1Sreyk * add_info 670eb8d1da1Sreyk * 671eb8d1da1Sreyk * Callback function invoked when we are expecting an 672eb8d1da1Sreyk * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional 673eb8d1da1Sreyk * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating 674eb8d1da1Sreyk * that no additional "list vm" data will be forthcoming. 675eb8d1da1Sreyk * 676eb8d1da1Sreyk * Parameters: 677eb8d1da1Sreyk * imsg : response imsg received from vmd 678eb8d1da1Sreyk * ret : return value 679eb8d1da1Sreyk * 680eb8d1da1Sreyk * Return: 681eb8d1da1Sreyk * 0 : the returned data was successfully added to the "list vm" data. 682eb8d1da1Sreyk * The caller can expect more data. 683eb8d1da1Sreyk * 1 : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call 684eb8d1da1Sreyk * add_info again), or an error occurred adding the returned data 685eb8d1da1Sreyk * to the "list vm" data. The caller should check the value of 686eb8d1da1Sreyk * 'ret' to determine which case occurred. 687eb8d1da1Sreyk * 688e0b12962Sreyk * This function does not return if a VM is found and info_action is CMD_CONSOLE 689822fd566Sreyk * 690eb8d1da1Sreyk * The function also sets 'ret' to the error code as follows: 691eb8d1da1Sreyk * 0 : Message successfully processed 692eb8d1da1Sreyk * EINVAL: Invalid or unexpected response from vmd 693eb8d1da1Sreyk * ENOMEM: memory allocation failure 69485846d3eSmlarkin * ENOENT: no entries 695eb8d1da1Sreyk */ 696eb8d1da1Sreyk int 697eb8d1da1Sreyk add_info(struct imsg *imsg, int *ret) 698eb8d1da1Sreyk { 699eb8d1da1Sreyk static size_t ct = 0; 7003a45f7b6Sreyk static struct vmop_info_result *vir = NULL; 701eb8d1da1Sreyk 70285846d3eSmlarkin *ret = 0; 70385846d3eSmlarkin 704eb8d1da1Sreyk if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) { 705eb8d1da1Sreyk vir = reallocarray(vir, ct + 1, 7063a45f7b6Sreyk sizeof(struct vmop_info_result)); 707eb8d1da1Sreyk if (vir == NULL) { 708eb8d1da1Sreyk *ret = ENOMEM; 709eb8d1da1Sreyk return (1); 710eb8d1da1Sreyk } 7113a45f7b6Sreyk memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result)); 712eb8d1da1Sreyk ct++; 713eb8d1da1Sreyk return (0); 714eb8d1da1Sreyk } else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) { 715e0b12962Sreyk switch (info_action) { 716e0b12962Sreyk case CMD_CONSOLE: 71795009bb4Sreyk vm_console(vir, ct); 718e0b12962Sreyk break; 719e0b12962Sreyk case CMD_STOPALL: 720e0b12962Sreyk terminate_all(vir, ct, info_flags); 721e0b12962Sreyk break; 722e0b12962Sreyk default: 72385846d3eSmlarkin *ret = print_vm_info(vir, ct); 724e0b12962Sreyk break; 725e0b12962Sreyk } 726eb8d1da1Sreyk free(vir); 727eb8d1da1Sreyk return (1); 728eb8d1da1Sreyk } else { 729eb8d1da1Sreyk *ret = EINVAL; 730eb8d1da1Sreyk return (1); 731eb8d1da1Sreyk } 732eb8d1da1Sreyk } 733eb8d1da1Sreyk 734eb8d1da1Sreyk /* 7353f77225dSjasper * vm_state 7363f77225dSjasper * 7373f77225dSjasper * Returns a string representing the current VM state, note that the order 7383f77225dSjasper * matters. A paused VM does have the VM_STATE_RUNNING bit set, but 7398b85f8f9Skn * VM_STATE_PAUSED is more significant to report. Same goes for stopping VMs. 7403f77225dSjasper * 7413f77225dSjasper * Parameters 7423f77225dSjasper * vm_state: mask indicating the vm state 7433f77225dSjasper */ 7443f77225dSjasper const char * 7453f77225dSjasper vm_state(unsigned int mask) 7463f77225dSjasper { 7478f40ce4bSjasper if (mask & VM_STATE_PAUSED) 7483f77225dSjasper return "paused"; 749de12a377Spd else if (mask & VM_STATE_WAITING) 750de12a377Spd return "waiting"; 7518f40ce4bSjasper else if (mask & VM_STATE_SHUTDOWN) 7528f40ce4bSjasper return "stopping"; 7538b85f8f9Skn else if (mask & VM_STATE_RUNNING) 7548b85f8f9Skn return "running"; 7558f40ce4bSjasper /* Presence of absence of other flags */ 7568f40ce4bSjasper else if (!mask || (mask & VM_STATE_DISABLED)) 7578f40ce4bSjasper return "stopped"; 7583f77225dSjasper 7593f77225dSjasper return "unknown"; 7603f77225dSjasper } 7613f77225dSjasper 7623f77225dSjasper /* 763eb8d1da1Sreyk * print_vm_info 764eb8d1da1Sreyk * 765eb8d1da1Sreyk * Prints the vm information returned from vmd in 'list' to stdout. 766eb8d1da1Sreyk * 767eb8d1da1Sreyk * Parameters 768eb8d1da1Sreyk * list: the vm information (consolidated) returned from vmd via imsg 769eb8d1da1Sreyk * ct : the size (number of elements in 'list') of the result 77085846d3eSmlarkin * 77185846d3eSmlarkin * Return values: 77285846d3eSmlarkin * 0: no error 77385846d3eSmlarkin * ENOENT: no entries printed 774eb8d1da1Sreyk */ 77585846d3eSmlarkin int 7763a45f7b6Sreyk print_vm_info(struct vmop_info_result *list, size_t ct) 777eb8d1da1Sreyk { 7783a45f7b6Sreyk struct vm_info_result *vir; 779b8a9a3ffSreyk struct vmop_info_result *vmi; 780628e8b46Skn size_t i; 781628e8b46Skn char *tty; 782b8a9a3ffSreyk char curmem[FMT_SCALED_STRSIZE]; 783b8a9a3ffSreyk char maxmem[FMT_SCALED_STRSIZE]; 784af56b46dSreyk char user[16], group[16]; 785dee31871Smillert const char *name; 78685846d3eSmlarkin int running, found_running; 787986b002cSmlarkin extern int stat_rflag; 788eb8d1da1Sreyk 78985846d3eSmlarkin found_running = 0; 79085846d3eSmlarkin 7913f77225dSjasper printf("%5s %5s %5s %7s %7s %7s %12s %8s %s\n", "ID", "PID", "VCPUS", 7923f77225dSjasper "MAXMEM", "CURMEM", "TTY", "OWNER", "STATE", "NAME"); 793b8a9a3ffSreyk 794eb8d1da1Sreyk for (i = 0; i < ct; i++) { 795b8a9a3ffSreyk vmi = &list[i]; 796b8a9a3ffSreyk vir = &vmi->vir_info; 797628e8b46Skn running = (vir->vir_creator_pid != 0 && vir->vir_id != 0); 798986b002cSmlarkin if (!running && stat_rflag) 799986b002cSmlarkin continue; 80085846d3eSmlarkin 80185846d3eSmlarkin found_running++; 80285846d3eSmlarkin 80359fb7762Sreyk if (check_info_id(vir->vir_name, vir->vir_id)) { 804e5d5b350Sreyk /* get user name */ 805dee31871Smillert name = user_from_uid(vmi->vir_uid, 1); 806dee31871Smillert if (name == NULL) 807e5d5b350Sreyk (void)snprintf(user, sizeof(user), 808e5d5b350Sreyk "%d", vmi->vir_uid); 809e5d5b350Sreyk else 810dee31871Smillert (void)strlcpy(user, name, sizeof(user)); 811e5d5b350Sreyk /* get group name */ 812e5d5b350Sreyk if (vmi->vir_gid != -1) { 813dee31871Smillert name = group_from_gid(vmi->vir_gid, 1); 814dee31871Smillert if (name == NULL) 815af56b46dSreyk (void)snprintf(group, sizeof(group), 816af56b46dSreyk ":%lld", vmi->vir_gid); 817e5d5b350Sreyk else 818af56b46dSreyk (void)snprintf(group, sizeof(group), 819dee31871Smillert ":%s", name); 820af56b46dSreyk (void)strlcat(user, group, sizeof(user)); 821e5d5b350Sreyk } 822e5d5b350Sreyk 823b8a9a3ffSreyk (void)strlcpy(curmem, "-", sizeof(curmem)); 824b8a9a3ffSreyk (void)strlcpy(maxmem, "-", sizeof(maxmem)); 825b8a9a3ffSreyk 82639e812afSdv (void)fmt_scaled(vir->vir_memory_size, maxmem); 827b8a9a3ffSreyk 828628e8b46Skn if (running) { 829ed2fe504Sreyk if (*vmi->vir_ttyname == '\0') 830ed2fe504Sreyk tty = "-"; 831b8a9a3ffSreyk /* get tty - skip /dev/ path */ 832ed2fe504Sreyk else if ((tty = strrchr(vmi->vir_ttyname, 83390d78c52Stb '/')) == NULL || *++tty == '\0') 834b8a9a3ffSreyk tty = list[i].vir_ttyname; 835b8a9a3ffSreyk 836b8a9a3ffSreyk (void)fmt_scaled(vir->vir_used_size, curmem); 837b8a9a3ffSreyk 838371c1bb3Sedd /* running vm */ 8393f77225dSjasper printf("%5u %5u %5zd %7s %7s %7s %12s %8s %s\n", 8403a45f7b6Sreyk vir->vir_id, vir->vir_creator_pid, 841b8a9a3ffSreyk vir->vir_ncpus, maxmem, curmem, 8423f77225dSjasper tty, user, vm_state(vmi->vir_state), 8433f77225dSjasper vir->vir_name); 844371c1bb3Sedd } else { 845371c1bb3Sedd /* disabled vm */ 8463f77225dSjasper printf("%5u %5s %5zd %7s %7s %7s %12s %8s %s\n", 847eb1cd41dSreyk vir->vir_id, "-", 848b8a9a3ffSreyk vir->vir_ncpus, maxmem, curmem, 8493f77225dSjasper "-", user, vm_state(vmi->vir_state), 8503f77225dSjasper vir->vir_name); 851371c1bb3Sedd } 85259fb7762Sreyk } 853eb8d1da1Sreyk } 85485846d3eSmlarkin 85585846d3eSmlarkin if (found_running) 85685846d3eSmlarkin return (0); 85785846d3eSmlarkin else 85885846d3eSmlarkin return (ENOENT); 859eb8d1da1Sreyk } 860eb8d1da1Sreyk 861eb8d1da1Sreyk /* 86295009bb4Sreyk * vm_console 86395009bb4Sreyk * 86495009bb4Sreyk * Connects to the vm console returned from vmd in 'list'. 86595009bb4Sreyk * 86695009bb4Sreyk * Parameters 86795009bb4Sreyk * list: the vm information (consolidated) returned from vmd via imsg 86895009bb4Sreyk * ct : the size (number of elements in 'list') of the result 86995009bb4Sreyk */ 87095009bb4Sreyk __dead void 87195009bb4Sreyk vm_console(struct vmop_info_result *list, size_t ct) 87295009bb4Sreyk { 87395009bb4Sreyk struct vmop_info_result *vir; 87495009bb4Sreyk size_t i; 87595009bb4Sreyk 87695009bb4Sreyk for (i = 0; i < ct; i++) { 87795009bb4Sreyk vir = &list[i]; 8788fde259dSjasper if ((check_info_id(vir->vir_info.vir_name, 8798fde259dSjasper vir->vir_info.vir_id) > 0) && 8808fde259dSjasper (vir->vir_ttyname[0] != '\0')) { 88195009bb4Sreyk /* does not return */ 88295009bb4Sreyk ctl_openconsole(vir->vir_ttyname); 88395009bb4Sreyk } 88495009bb4Sreyk } 88595009bb4Sreyk 8865fd19063Sccardenas errx(1, "console not found"); 88795009bb4Sreyk } 88895009bb4Sreyk 88995009bb4Sreyk /* 8904d2a1fb2Sreyk * open_imagefile 8914d2a1fb2Sreyk * 8924d2a1fb2Sreyk * Open an imagefile with the specified type, path and size. 8934d2a1fb2Sreyk * 8944d2a1fb2Sreyk * Parameters: 8954d2a1fb2Sreyk * type : format of the image file 8964d2a1fb2Sreyk * imgfile_path: path to the image file to create 8974d2a1fb2Sreyk * flags : flags for open(2), e.g. O_RDONLY 8984d2a1fb2Sreyk * file : file structure 8994d2a1fb2Sreyk * sz : size of the image file 9004d2a1fb2Sreyk * 9014d2a1fb2Sreyk * Return: 9024d2a1fb2Sreyk * fd : Returns file descriptor of the new image file 9034d2a1fb2Sreyk * -1 : Operation failed. errno is set. 9044d2a1fb2Sreyk */ 9054d2a1fb2Sreyk int 9064d2a1fb2Sreyk open_imagefile(int type, const char *imgfile_path, int flags, 9074d2a1fb2Sreyk struct virtio_backing *file, off_t *sz) 9084d2a1fb2Sreyk { 9094d2a1fb2Sreyk int fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i; 9104d2a1fb2Sreyk char path[PATH_MAX]; 9114d2a1fb2Sreyk 9124d2a1fb2Sreyk *sz = 0; 9134d2a1fb2Sreyk if ((fd = open(imgfile_path, flags)) == -1) 9144d2a1fb2Sreyk return (-1); 9154d2a1fb2Sreyk 9164d2a1fb2Sreyk basefd[0] = fd; 9174d2a1fb2Sreyk nfd = 1; 9184d2a1fb2Sreyk 9194d2a1fb2Sreyk errno = 0; 9204d2a1fb2Sreyk switch (type) { 9214d2a1fb2Sreyk case VMDF_QCOW2: 9224d2a1fb2Sreyk if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path)) 9234d2a1fb2Sreyk return (-1); 9244d2a1fb2Sreyk for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) { 9254d2a1fb2Sreyk if ((ret = virtio_qcow2_get_base(basefd[i], 9264d2a1fb2Sreyk path, sizeof(path), imgfile_path)) == -1) { 9274d2a1fb2Sreyk log_debug("%s: failed to get base %d", __func__, i); 9284d2a1fb2Sreyk return -1; 9294d2a1fb2Sreyk } else if (ret == 0) 9304d2a1fb2Sreyk break; 9314d2a1fb2Sreyk 9324d2a1fb2Sreyk if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) { 9334d2a1fb2Sreyk log_warn("%s: failed to open base %s", 9344d2a1fb2Sreyk __func__, path); 9354d2a1fb2Sreyk return (-1); 9364d2a1fb2Sreyk } 9374d2a1fb2Sreyk } 93862df93eeSreyk ret = virtio_qcow2_init(file, sz, basefd, nfd); 9394d2a1fb2Sreyk break; 9404d2a1fb2Sreyk default: 94162df93eeSreyk ret = virtio_raw_init(file, sz, &fd, 1); 9424d2a1fb2Sreyk break; 9434d2a1fb2Sreyk } 9444d2a1fb2Sreyk 9454d2a1fb2Sreyk if (ret == -1) { 9464d2a1fb2Sreyk for (i = 0; i < nfd; i++) 9474d2a1fb2Sreyk close(basefd[i]); 9484d2a1fb2Sreyk return (-1); 9494d2a1fb2Sreyk } 9504d2a1fb2Sreyk 9514d2a1fb2Sreyk return (fd); 9524d2a1fb2Sreyk } 9534d2a1fb2Sreyk 9544d2a1fb2Sreyk /* 9554d2a1fb2Sreyk * create_imagefile 9564d2a1fb2Sreyk * 9574d2a1fb2Sreyk * Create an empty imagefile with the specified type, path and size. 9584d2a1fb2Sreyk * 9594d2a1fb2Sreyk * Parameters: 9604d2a1fb2Sreyk * type : format of the image file 9614d2a1fb2Sreyk * imgfile_path: path to the image file to create 9624d2a1fb2Sreyk * base_path : path to the qcow2 base image 963ead1b146Sdv * imgsize : size of the image file to create (in bytes) 9644d2a1fb2Sreyk * format : string identifying the format 9654d2a1fb2Sreyk * 9664d2a1fb2Sreyk * Return: 9674d2a1fb2Sreyk * EEXIST: The requested image file already exists 9684d2a1fb2Sreyk * 0 : Image file successfully created 9694d2a1fb2Sreyk * Exxxx : Various other Exxxx errno codes due to other I/O errors 9704d2a1fb2Sreyk */ 9714d2a1fb2Sreyk int 9724d2a1fb2Sreyk create_imagefile(int type, const char *imgfile_path, const char *base_path, 9737ac3f975Syasuoka uint64_t imgsize, const char **format) 9744d2a1fb2Sreyk { 9754d2a1fb2Sreyk int ret; 9764d2a1fb2Sreyk 9774d2a1fb2Sreyk switch (type) { 9784d2a1fb2Sreyk case VMDF_QCOW2: 9794d2a1fb2Sreyk *format = "qcow2"; 98062df93eeSreyk ret = virtio_qcow2_create(imgfile_path, base_path, imgsize); 9814d2a1fb2Sreyk break; 9824d2a1fb2Sreyk default: 9834d2a1fb2Sreyk *format = "raw"; 98462df93eeSreyk ret = virtio_raw_create(imgfile_path, imgsize); 9854d2a1fb2Sreyk break; 9864d2a1fb2Sreyk } 9874d2a1fb2Sreyk 9884d2a1fb2Sreyk return (ret); 9894d2a1fb2Sreyk } 990