1*0a9d031fSclaudio /* $OpenBSD: control.c,v 1.49 2024/11/21 13:39:34 claudio Exp $ */ 2af96af6cSreyk 3af96af6cSreyk /* 4af96af6cSreyk * Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org> 5af96af6cSreyk * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6af96af6cSreyk * 7af96af6cSreyk * Permission to use, copy, modify, and distribute this software for any 8af96af6cSreyk * purpose with or without fee is hereby granted, provided that the above 9af96af6cSreyk * copyright notice and this permission notice appear in all copies. 10af96af6cSreyk * 11af96af6cSreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12af96af6cSreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13af96af6cSreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14af96af6cSreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15af96af6cSreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16af96af6cSreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17af96af6cSreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18af96af6cSreyk */ 19af96af6cSreyk 2024fb43d0Sderaadt #include <sys/types.h> 21af96af6cSreyk #include <sys/queue.h> 22af96af6cSreyk #include <sys/stat.h> 23af96af6cSreyk #include <sys/socket.h> 24af96af6cSreyk #include <sys/un.h> 25af96af6cSreyk 26af96af6cSreyk #include <errno.h> 27af96af6cSreyk #include <event.h> 28af96af6cSreyk #include <stdlib.h> 29af96af6cSreyk #include <string.h> 30af96af6cSreyk #include <unistd.h> 31af96af6cSreyk 32af96af6cSreyk #include "proc.h" 33af96af6cSreyk #include "vmd.h" 34af96af6cSreyk 35af96af6cSreyk #define CONTROL_BACKLOG 5 36af96af6cSreyk 37c5fa57f5Sdv struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); 38af96af6cSreyk 39e73ceaacSdv struct ctl_notify { 40e73ceaacSdv int ctl_fd; 41e73ceaacSdv uint32_t ctl_vmid; 42e73ceaacSdv TAILQ_ENTRY(ctl_notify) entry; 43e73ceaacSdv }; 44e73ceaacSdv TAILQ_HEAD(ctl_notify_q, ctl_notify) ctl_notify_q = 45e73ceaacSdv TAILQ_HEAD_INITIALIZER(ctl_notify_q); 46af96af6cSreyk void 47af96af6cSreyk control_accept(int, short, void *); 48af96af6cSreyk struct ctl_conn 49af96af6cSreyk *control_connbyfd(int); 50af96af6cSreyk void control_close(int, struct control_sock *); 51af96af6cSreyk void control_dispatch_imsg(int, short, void *); 52ee5f6d5eSreyk int control_dispatch_vmd(int, struct privsep_proc *, struct imsg *); 53af96af6cSreyk void control_run(struct privsep *, struct privsep_proc *, void *); 54af96af6cSreyk 55af96af6cSreyk static struct privsep_proc procs[] = { 56ee5f6d5eSreyk { "parent", PROC_PARENT, control_dispatch_vmd } 57af96af6cSreyk }; 58af96af6cSreyk 59bcc679a1Sreyk void 60af96af6cSreyk control(struct privsep *ps, struct privsep_proc *p) 61af96af6cSreyk { 62bcc679a1Sreyk proc_run(ps, p, procs, nitems(procs), control_run, NULL); 63af96af6cSreyk } 64af96af6cSreyk 65af96af6cSreyk void 66af96af6cSreyk control_run(struct privsep *ps, struct privsep_proc *p, void *arg) 67af96af6cSreyk { 68af96af6cSreyk /* 69af96af6cSreyk * pledge in the control process: 70af96af6cSreyk * stdio - for malloc and basic I/O including events. 71af96af6cSreyk * unix - for the control socket. 72bcc679a1Sreyk * recvfd - for the proc fd exchange. 73eed20f3bSpd * sendfd - for send and receive. 74af96af6cSreyk */ 7513184223Smestre if (pledge("stdio unix recvfd sendfd", NULL) == -1) 76af96af6cSreyk fatal("pledge"); 77af96af6cSreyk } 78af96af6cSreyk 79af96af6cSreyk int 80ee5f6d5eSreyk control_dispatch_vmd(int fd, struct privsep_proc *p, struct imsg *imsg) 81af96af6cSreyk { 82af96af6cSreyk struct ctl_conn *c; 83e73ceaacSdv struct ctl_notify *notify = NULL, *notify_next; 84c48cfcf4Sreyk struct privsep *ps = p->p_ps; 85e73ceaacSdv struct vmop_result vmr; 86e73ceaacSdv int waiting = 0; 87af96af6cSreyk 88af96af6cSreyk switch (imsg->hdr.type) { 89af96af6cSreyk case IMSG_VMDOP_START_VM_RESPONSE: 9052e954a3Spd case IMSG_VMDOP_PAUSE_VM_RESPONSE: 91eed20f3bSpd case IMSG_VMDOP_SEND_VM_RESPONSE: 9252e954a3Spd case IMSG_VMDOP_UNPAUSE_VM_RESPONSE: 93af96af6cSreyk case IMSG_VMDOP_GET_INFO_VM_DATA: 94af96af6cSreyk case IMSG_VMDOP_GET_INFO_VM_END_DATA: 95d1866b5cSreyk case IMSG_CTL_FAIL: 96d1866b5cSreyk case IMSG_CTL_OK: 97e73ceaacSdv /* Provide basic response back to a specific control client */ 98bcc679a1Sreyk if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) { 990d07b4d3Sreyk log_warnx("%s: lost control connection: fd %d", 100bcc679a1Sreyk __func__, imsg->hdr.peerid); 1010d07b4d3Sreyk return (0); 102bcc679a1Sreyk } 103af96af6cSreyk imsg_compose_event(&c->iev, imsg->hdr.type, 104e73ceaacSdv 0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg)); 105e73ceaacSdv break; 106e73ceaacSdv case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 107e73ceaacSdv IMSG_SIZE_CHECK(imsg, &vmr); 108e73ceaacSdv memcpy(&vmr, imsg->data, sizeof(vmr)); 109e73ceaacSdv 110e73ceaacSdv if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) { 111e73ceaacSdv log_warnx("%s: lost control connection: fd %d", 112e73ceaacSdv __func__, imsg->hdr.peerid); 113e73ceaacSdv return (0); 114e73ceaacSdv } 115e73ceaacSdv 116e73ceaacSdv TAILQ_FOREACH(notify, &ctl_notify_q, entry) { 117e73ceaacSdv if (notify->ctl_fd == (int) imsg->hdr.peerid) { 118e73ceaacSdv /* 119e73ceaacSdv * Update if waiting by vm name. This is only 120e73ceaacSdv * supported when stopping a single vm. If 121e73ceaacSdv * stopping all vms, vmctl(8) sends the request 122e73ceaacSdv * using the vmid. 123e73ceaacSdv */ 124e73ceaacSdv if (notify->ctl_vmid < 1) 125e73ceaacSdv notify->ctl_vmid = vmr.vmr_id; 126e73ceaacSdv waiting = 1; 127e73ceaacSdv break; 128e73ceaacSdv } 129e73ceaacSdv } 130e73ceaacSdv 131e73ceaacSdv /* An error needs to be relayed to the client immediately */ 132e73ceaacSdv if (!waiting || vmr.vmr_result) { 133e73ceaacSdv imsg_compose_event(&c->iev, imsg->hdr.type, 134e73ceaacSdv 0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg)); 135e73ceaacSdv 136e73ceaacSdv if (notify) { 137e73ceaacSdv TAILQ_REMOVE(&ctl_notify_q, notify, entry); 138e73ceaacSdv free(notify); 139e73ceaacSdv } 140e73ceaacSdv } 141e73ceaacSdv break; 142e73ceaacSdv case IMSG_VMDOP_TERMINATE_VM_EVENT: 143e73ceaacSdv /* Notify any waiting clients that a VM terminated */ 144e73ceaacSdv IMSG_SIZE_CHECK(imsg, &vmr); 145e73ceaacSdv memcpy(&vmr, imsg->data, sizeof(vmr)); 146e73ceaacSdv 147e73ceaacSdv TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) { 148e73ceaacSdv if (notify->ctl_vmid != vmr.vmr_id) 149e73ceaacSdv continue; 150e73ceaacSdv if ((c = control_connbyfd(notify->ctl_fd)) != NULL) { 151540e29f5Sdv /* Forward to the vmctl(8) client */ 152540e29f5Sdv imsg_compose_event(&c->iev, imsg->hdr.type, 153e73ceaacSdv 0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg)); 154e73ceaacSdv TAILQ_REMOVE(&ctl_notify_q, notify, entry); 155e73ceaacSdv free(notify); 156e73ceaacSdv } 157e73ceaacSdv } 158af96af6cSreyk break; 159c48cfcf4Sreyk case IMSG_VMDOP_CONFIG: 160c48cfcf4Sreyk config_getconfig(ps->ps_env, imsg); 1616cfffd57Sreyk proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0); 162c48cfcf4Sreyk break; 163c48cfcf4Sreyk case IMSG_CTL_RESET: 164c48cfcf4Sreyk config_getreset(ps->ps_env, imsg); 165c48cfcf4Sreyk break; 166af96af6cSreyk default: 167af96af6cSreyk return (-1); 168af96af6cSreyk } 169af96af6cSreyk 170af96af6cSreyk return (0); 171af96af6cSreyk } 172af96af6cSreyk 173af96af6cSreyk int 174af96af6cSreyk control_init(struct privsep *ps, struct control_sock *cs) 175af96af6cSreyk { 176af96af6cSreyk struct sockaddr_un sun; 177af96af6cSreyk int fd; 178af96af6cSreyk mode_t old_umask, mode; 179af96af6cSreyk 180af96af6cSreyk if (cs->cs_name == NULL) 181af96af6cSreyk return (0); 182af96af6cSreyk 183af96af6cSreyk if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) { 184af96af6cSreyk log_warn("%s: socket", __func__); 185af96af6cSreyk return (-1); 186af96af6cSreyk } 187af96af6cSreyk 188af96af6cSreyk sun.sun_family = AF_UNIX; 189af96af6cSreyk if (strlcpy(sun.sun_path, cs->cs_name, 190af96af6cSreyk sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { 191af96af6cSreyk log_warn("%s: %s name too long", __func__, cs->cs_name); 192af96af6cSreyk close(fd); 193af96af6cSreyk return (-1); 194af96af6cSreyk } 195af96af6cSreyk 196af96af6cSreyk if (unlink(cs->cs_name) == -1) 197af96af6cSreyk if (errno != ENOENT) { 198af96af6cSreyk log_warn("%s: unlink %s", __func__, cs->cs_name); 199af96af6cSreyk close(fd); 200af96af6cSreyk return (-1); 201af96af6cSreyk } 202af96af6cSreyk 203af96af6cSreyk if (cs->cs_restricted) { 204af96af6cSreyk old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 205af96af6cSreyk mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 206af96af6cSreyk } else { 207af96af6cSreyk old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 208af96af6cSreyk mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; 209af96af6cSreyk } 210af96af6cSreyk 211af96af6cSreyk if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 212af96af6cSreyk log_warn("%s: bind: %s", __func__, cs->cs_name); 213af96af6cSreyk close(fd); 214af96af6cSreyk (void)umask(old_umask); 215af96af6cSreyk return (-1); 216af96af6cSreyk } 217af96af6cSreyk (void)umask(old_umask); 218af96af6cSreyk 219af96af6cSreyk if (chmod(cs->cs_name, mode) == -1) { 220af96af6cSreyk log_warn("%s: chmod", __func__); 221af96af6cSreyk close(fd); 222af96af6cSreyk (void)unlink(cs->cs_name); 223af96af6cSreyk return (-1); 224af96af6cSreyk } 225af96af6cSreyk 226af96af6cSreyk cs->cs_fd = fd; 227af96af6cSreyk cs->cs_env = ps; 228af96af6cSreyk 2296cfffd57Sreyk proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0); 2306cfffd57Sreyk 2316cfffd57Sreyk return (0); 2326cfffd57Sreyk } 2336cfffd57Sreyk 2346cfffd57Sreyk int 2356cfffd57Sreyk control_reset(struct control_sock *cs) 2366cfffd57Sreyk { 2376cfffd57Sreyk /* Updating owner of the control socket */ 2386cfffd57Sreyk if (chown(cs->cs_name, cs->cs_uid, cs->cs_gid) == -1) 2396cfffd57Sreyk return (-1); 2406cfffd57Sreyk 241af96af6cSreyk return (0); 242af96af6cSreyk } 243af96af6cSreyk 244af96af6cSreyk int 245af96af6cSreyk control_listen(struct control_sock *cs) 246af96af6cSreyk { 247af96af6cSreyk if (cs->cs_name == NULL) 248af96af6cSreyk return (0); 249af96af6cSreyk 250af96af6cSreyk if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) { 251af96af6cSreyk log_warn("%s: listen", __func__); 252af96af6cSreyk return (-1); 253af96af6cSreyk } 254af96af6cSreyk 255af96af6cSreyk event_set(&cs->cs_ev, cs->cs_fd, EV_READ, 256af96af6cSreyk control_accept, cs); 257af96af6cSreyk event_add(&cs->cs_ev, NULL); 258af96af6cSreyk evtimer_set(&cs->cs_evt, control_accept, cs); 259af96af6cSreyk 260af96af6cSreyk return (0); 261af96af6cSreyk } 262af96af6cSreyk 263af96af6cSreyk void 264af96af6cSreyk control_accept(int listenfd, short event, void *arg) 265af96af6cSreyk { 266af96af6cSreyk struct control_sock *cs = arg; 267af96af6cSreyk int connfd; 268af96af6cSreyk socklen_t len; 269af96af6cSreyk struct sockaddr_un sun; 270af96af6cSreyk struct ctl_conn *c; 271af96af6cSreyk 272af96af6cSreyk event_add(&cs->cs_ev, NULL); 273af96af6cSreyk if ((event & EV_TIMEOUT)) 274af96af6cSreyk return; 275af96af6cSreyk 276af96af6cSreyk len = sizeof(sun); 277af96af6cSreyk if ((connfd = accept4(listenfd, 278af96af6cSreyk (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) { 279af96af6cSreyk /* 280af96af6cSreyk * Pause accept if we are out of file descriptors, or 281af96af6cSreyk * libevent will haunt us here too. 282af96af6cSreyk */ 283af96af6cSreyk if (errno == ENFILE || errno == EMFILE) { 284af96af6cSreyk struct timeval evtpause = { 1, 0 }; 285af96af6cSreyk 286af96af6cSreyk event_del(&cs->cs_ev); 287af96af6cSreyk evtimer_add(&cs->cs_evt, &evtpause); 288af96af6cSreyk } else if (errno != EWOULDBLOCK && errno != EINTR && 289af96af6cSreyk errno != ECONNABORTED) 290af96af6cSreyk log_warn("%s: accept", __func__); 291af96af6cSreyk return; 292af96af6cSreyk } 293af96af6cSreyk 294af96af6cSreyk if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { 295af96af6cSreyk log_warn("%s", __func__); 296af96af6cSreyk close(connfd); 297af96af6cSreyk return; 298af96af6cSreyk } 299af96af6cSreyk 300091d9ffeSreyk if (getsockopt(connfd, SOL_SOCKET, SO_PEERCRED, 301091d9ffeSreyk &c->peercred, &len) != 0) { 302091d9ffeSreyk log_warn("%s: failed to get peer credentials", __func__); 303091d9ffeSreyk close(connfd); 304091d9ffeSreyk free(c); 305091d9ffeSreyk return; 306091d9ffeSreyk } 307091d9ffeSreyk 308*0a9d031fSclaudio if (imsgbuf_init(&c->iev.ibuf, connfd) == -1) { 309*0a9d031fSclaudio log_warn("%s: failed to init imsgbuf", __func__); 310*0a9d031fSclaudio close(connfd); 311*0a9d031fSclaudio free(c); 312*0a9d031fSclaudio return; 313*0a9d031fSclaudio } 314*0a9d031fSclaudio imsgbuf_allow_fdpass(&c->iev.ibuf); 315af96af6cSreyk c->iev.handler = control_dispatch_imsg; 316af96af6cSreyk c->iev.events = EV_READ; 317af96af6cSreyk c->iev.data = cs; 318af96af6cSreyk event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, 319af96af6cSreyk c->iev.handler, c->iev.data); 320af96af6cSreyk event_add(&c->iev.ev, NULL); 321af96af6cSreyk 322af96af6cSreyk TAILQ_INSERT_TAIL(&ctl_conns, c, entry); 323af96af6cSreyk } 324af96af6cSreyk 325af96af6cSreyk struct ctl_conn * 326af96af6cSreyk control_connbyfd(int fd) 327af96af6cSreyk { 328af96af6cSreyk struct ctl_conn *c; 329af96af6cSreyk 3304ff7cad5Skrw TAILQ_FOREACH(c, &ctl_conns, entry) { 3314ff7cad5Skrw if (c->iev.ibuf.fd == fd) 3324ff7cad5Skrw break; 3334ff7cad5Skrw } 334af96af6cSreyk 335af96af6cSreyk return (c); 336af96af6cSreyk } 337af96af6cSreyk 338af96af6cSreyk void 339af96af6cSreyk control_close(int fd, struct control_sock *cs) 340af96af6cSreyk { 341af96af6cSreyk struct ctl_conn *c; 342e73ceaacSdv struct ctl_notify *notify, *notify_next; 343af96af6cSreyk 344af96af6cSreyk if ((c = control_connbyfd(fd)) == NULL) { 345af96af6cSreyk log_warn("%s: fd %d: not found", __func__, fd); 346af96af6cSreyk return; 347af96af6cSreyk } 348af96af6cSreyk 3499cbf9e90Sclaudio imsgbuf_clear(&c->iev.ibuf); 350af96af6cSreyk TAILQ_REMOVE(&ctl_conns, c, entry); 351af96af6cSreyk 352e73ceaacSdv TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) { 353e73ceaacSdv if (notify->ctl_fd == fd) { 354e73ceaacSdv TAILQ_REMOVE(&ctl_notify_q, notify, entry); 355e73ceaacSdv free(notify); 356e73ceaacSdv break; 357e73ceaacSdv } 358e73ceaacSdv } 359e73ceaacSdv 360af96af6cSreyk event_del(&c->iev.ev); 361af96af6cSreyk close(c->iev.ibuf.fd); 362af96af6cSreyk 363af96af6cSreyk /* Some file descriptors are available again. */ 364af96af6cSreyk if (evtimer_pending(&cs->cs_evt, NULL)) { 365af96af6cSreyk evtimer_del(&cs->cs_evt); 366af96af6cSreyk event_add(&cs->cs_ev, NULL); 367af96af6cSreyk } 368af96af6cSreyk 369af96af6cSreyk free(c); 370af96af6cSreyk } 371af96af6cSreyk 372af96af6cSreyk void 373af96af6cSreyk control_dispatch_imsg(int fd, short event, void *arg) 374af96af6cSreyk { 375af96af6cSreyk struct control_sock *cs = arg; 376af96af6cSreyk struct privsep *ps = cs->cs_env; 377af96af6cSreyk struct ctl_conn *c; 378af96af6cSreyk struct imsg imsg; 379bb55a3a2Sreyk struct vmop_create_params vmc; 380bb55a3a2Sreyk struct vmop_id vid; 381e73ceaacSdv struct ctl_notify *notify; 382e73ceaacSdv int n, v, wait = 0, ret = 0; 383af96af6cSreyk 384af96af6cSreyk if ((c = control_connbyfd(fd)) == NULL) { 385af96af6cSreyk log_warn("%s: fd %d: not found", __func__, fd); 386af96af6cSreyk return; 387af96af6cSreyk } 388af96af6cSreyk 389af96af6cSreyk if (event & EV_READ) { 390d12ef5f3Sclaudio if (imsgbuf_read(&c->iev.ibuf) != 1) { 391af96af6cSreyk control_close(fd, cs); 392af96af6cSreyk return; 393af96af6cSreyk } 394af96af6cSreyk } 395af96af6cSreyk if (event & EV_WRITE) { 396dd7efffeSclaudio if (imsgbuf_write(&c->iev.ibuf) == -1) { 397af96af6cSreyk control_close(fd, cs); 398af96af6cSreyk return; 399af96af6cSreyk } 400af96af6cSreyk } 401af96af6cSreyk 402af96af6cSreyk for (;;) { 403af96af6cSreyk if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { 404af96af6cSreyk control_close(fd, cs); 405af96af6cSreyk return; 406af96af6cSreyk } 407af96af6cSreyk 408af96af6cSreyk if (n == 0) 409af96af6cSreyk break; 410af96af6cSreyk 411091d9ffeSreyk switch (imsg.hdr.type) { 412008065a5Sreyk case IMSG_VMDOP_GET_INFO_VM_REQUEST: 41395176482Sclaudio case IMSG_VMDOP_WAIT_VM_REQUEST: 414e5d5b350Sreyk case IMSG_VMDOP_TERMINATE_VM_REQUEST: 415e5d5b350Sreyk case IMSG_VMDOP_START_VM_REQUEST: 416bc813cecSpd case IMSG_VMDOP_PAUSE_VM: 417bc813cecSpd case IMSG_VMDOP_UNPAUSE_VM: 418008065a5Sreyk break; 419008065a5Sreyk default: 420091d9ffeSreyk if (c->peercred.uid != 0) { 421091d9ffeSreyk log_warnx("denied request %d from uid %d", 422091d9ffeSreyk imsg.hdr.type, c->peercred.uid); 423091d9ffeSreyk ret = EPERM; 424091d9ffeSreyk goto fail; 425091d9ffeSreyk } 426091d9ffeSreyk break; 427091d9ffeSreyk } 428091d9ffeSreyk 429af96af6cSreyk switch (imsg.hdr.type) { 430af96af6cSreyk case IMSG_CTL_VERBOSE: 431bb55a3a2Sreyk if (IMSG_DATA_SIZE(&imsg) < sizeof(v)) 432bb55a3a2Sreyk goto fail; 433af96af6cSreyk memcpy(&v, imsg.data, sizeof(v)); 434871fc12cSreyk log_setverbose(v); 435af96af6cSreyk 436b55cbc1dSreyk /* FALLTHROUGH */ 437eed20f3bSpd case IMSG_VMDOP_RECEIVE_VM_REQUEST: 438eed20f3bSpd case IMSG_VMDOP_SEND_VM_REQUEST: 439b55cbc1dSreyk case IMSG_VMDOP_LOAD: 440b55cbc1dSreyk case IMSG_VMDOP_RELOAD: 441b55cbc1dSreyk case IMSG_CTL_RESET: 442d1866b5cSreyk if (proc_compose_imsg(ps, PROC_PARENT, -1, 44353027660Sclaudio imsg.hdr.type, fd, imsg_get_fd(&imsg), 444d1866b5cSreyk imsg.data, IMSG_DATA_SIZE(&imsg)) == -1) 445d1866b5cSreyk goto fail; 446af96af6cSreyk break; 447af96af6cSreyk case IMSG_VMDOP_START_VM_REQUEST: 448bb55a3a2Sreyk if (IMSG_DATA_SIZE(&imsg) < sizeof(vmc)) 449bb55a3a2Sreyk goto fail; 450e5d5b350Sreyk memcpy(&vmc, imsg.data, sizeof(vmc)); 451476d73d1Sreyk vmc.vmc_owner.uid = c->peercred.uid; 452476d73d1Sreyk vmc.vmc_owner.gid = -1; 453e5d5b350Sreyk 454b848b186Sdv /* imsg.fd may contain kernel image fd. */ 455f0bbd60cSreyk if (proc_compose_imsg(ps, PROC_PARENT, -1, 45653027660Sclaudio imsg.hdr.type, fd, imsg_get_fd(&imsg), &vmc, 457b848b186Sdv sizeof(vmc)) == -1) { 458af96af6cSreyk control_close(fd, cs); 459af96af6cSreyk return; 460af96af6cSreyk } 461af96af6cSreyk break; 46295176482Sclaudio case IMSG_VMDOP_WAIT_VM_REQUEST: 463e73ceaacSdv wait = 1; 464e73ceaacSdv /* FALLTHROUGH */ 465bb55a3a2Sreyk case IMSG_VMDOP_TERMINATE_VM_REQUEST: 466bb55a3a2Sreyk if (IMSG_DATA_SIZE(&imsg) < sizeof(vid)) 467bb55a3a2Sreyk goto fail; 468e5d5b350Sreyk memcpy(&vid, imsg.data, sizeof(vid)); 469e5d5b350Sreyk vid.vid_uid = c->peercred.uid; 470e5d5b350Sreyk 471e73ceaacSdv if (wait || vid.vid_flags & VMOP_WAIT) { 472e73ceaacSdv vid.vid_flags |= VMOP_WAIT; 473e73ceaacSdv notify = calloc(1, sizeof(struct ctl_notify)); 474e73ceaacSdv if (notify == NULL) 475e73ceaacSdv fatal("%s: calloc", __func__); 476e73ceaacSdv notify->ctl_vmid = vid.vid_id; 477e73ceaacSdv notify->ctl_fd = fd; 478e73ceaacSdv TAILQ_INSERT_TAIL(&ctl_notify_q, notify, entry); 479e73ceaacSdv log_debug("%s: registered wait for peer %d", 480e73ceaacSdv __func__, fd); 481e73ceaacSdv } 482e73ceaacSdv 483bb55a3a2Sreyk if (proc_compose_imsg(ps, PROC_PARENT, -1, 484e5d5b350Sreyk imsg.hdr.type, fd, -1, &vid, sizeof(vid)) == -1) { 485e0994759Smlarkin log_debug("%s: proc_compose_imsg failed", 486e0994759Smlarkin __func__); 487bb55a3a2Sreyk control_close(fd, cs); 488bb55a3a2Sreyk return; 489bb55a3a2Sreyk } 490bb55a3a2Sreyk break; 491bb55a3a2Sreyk case IMSG_VMDOP_GET_INFO_VM_REQUEST: 492bb55a3a2Sreyk if (IMSG_DATA_SIZE(&imsg) != 0) 493bb55a3a2Sreyk goto fail; 494bb55a3a2Sreyk if (proc_compose_imsg(ps, PROC_PARENT, -1, 495bb55a3a2Sreyk imsg.hdr.type, fd, -1, NULL, 0) == -1) { 496bb55a3a2Sreyk control_close(fd, cs); 497bb55a3a2Sreyk return; 498bb55a3a2Sreyk } 499bb55a3a2Sreyk break; 500bc813cecSpd case IMSG_VMDOP_PAUSE_VM: 501bc813cecSpd case IMSG_VMDOP_UNPAUSE_VM: 502bc813cecSpd if (IMSG_DATA_SIZE(&imsg) < sizeof(vid)) 503bc813cecSpd goto fail; 504bc813cecSpd memcpy(&vid, imsg.data, sizeof(vid)); 505bc813cecSpd vid.vid_uid = c->peercred.uid; 506bc813cecSpd log_debug("%s id: %d, name: %s, uid: %d", 507bc813cecSpd __func__, vid.vid_id, vid.vid_name, 508bc813cecSpd vid.vid_uid); 509bc813cecSpd 510bc813cecSpd if (proc_compose_imsg(ps, PROC_PARENT, -1, 51153027660Sclaudio imsg.hdr.type, fd, imsg_get_fd(&imsg), 512bc813cecSpd &vid, sizeof(vid)) == -1) 513bc813cecSpd goto fail; 514bc813cecSpd break; 515af96af6cSreyk default: 516af96af6cSreyk log_debug("%s: error handling imsg %d", 517af96af6cSreyk __func__, imsg.hdr.type); 518af96af6cSreyk control_close(fd, cs); 519af96af6cSreyk break; 520af96af6cSreyk } 521af96af6cSreyk imsg_free(&imsg); 522af96af6cSreyk } 523af96af6cSreyk 524af96af6cSreyk imsg_event_add(&c->iev); 525091d9ffeSreyk return; 526091d9ffeSreyk 527091d9ffeSreyk fail: 528bb55a3a2Sreyk if (ret == 0) 529bb55a3a2Sreyk ret = EINVAL; 530091d9ffeSreyk imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 531091d9ffeSreyk 0, 0, -1, &ret, sizeof(ret)); 532dd7efffeSclaudio imsgbuf_flush(&c->iev.ibuf); 533091d9ffeSreyk control_close(fd, cs); 534af96af6cSreyk } 535