vmd.c revision 1.26
1/* $OpenBSD: vmd.c,v 1.26 2016/02/02 17:51:11 sthen Exp $ */ 2 3/* 4 * Copyright (c) 2015 Reyk Floeter <reyk@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/param.h> 20#include <sys/queue.h> 21#include <sys/wait.h> 22#include <sys/cdefs.h> 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <errno.h> 28#include <event.h> 29#include <fcntl.h> 30#include <pwd.h> 31#include <signal.h> 32#include <syslog.h> 33#include <unistd.h> 34#include <ctype.h> 35 36#include "proc.h" 37#include "vmd.h" 38 39__dead void usage(void); 40 41int main(int, char **); 42int vmd_configure(void); 43void vmd_sighdlr(int sig, short event, void *arg); 44void vmd_shutdown(void); 45int vmd_control_run(void); 46int vmd_dispatch_control(int, struct privsep_proc *, struct imsg *); 47int vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *); 48 49struct vmd *env; 50 51static struct privsep_proc procs[] = { 52 { "control", PROC_CONTROL, vmd_dispatch_control, control }, 53 { "vmm", PROC_VMM, vmd_dispatch_vmm, vmm }, 54}; 55 56int 57vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) 58{ 59 struct privsep *ps = p->p_ps; 60 int res = 0, cmd = 0, v = 0; 61 struct vm_create_params vcp; 62 struct vmop_id vid; 63 struct vm_terminate_params vtp; 64 struct vmop_result vmr; 65 struct vmd_vm *vm = NULL; 66 char *str = NULL; 67 uint32_t id = 0; 68 69 switch (imsg->hdr.type) { 70 case IMSG_VMDOP_START_VM_REQUEST: 71 IMSG_SIZE_CHECK(imsg, &vcp); 72 memcpy(&vcp, imsg->data, sizeof(vcp)); 73 res = config_getvm(ps, &vcp, -1, imsg->hdr.peerid); 74 if (res == -1) { 75 res = errno; 76 cmd = IMSG_VMDOP_START_VM_RESPONSE; 77 } 78 break; 79 case IMSG_VMDOP_TERMINATE_VM_REQUEST: 80 IMSG_SIZE_CHECK(imsg, &vid); 81 memcpy(&vid, imsg->data, sizeof(vid)); 82 if ((id = vid.vid_id) == 0) { 83 /* Lookup vm (id) by name */ 84 if ((vm = vm_getbyname(vid.vid_name)) == NULL) { 85 res = ENOENT; 86 cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; 87 break; 88 } 89 id = vm->vm_params.vcp_id; 90 } 91 memset(&vtp, 0, sizeof(vtp)); 92 vtp.vtp_vm_id = id; 93 if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, 94 imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1) 95 return (-1); 96 break; 97 case IMSG_VMDOP_GET_INFO_VM_REQUEST: 98 proc_forward_imsg(ps, imsg, PROC_VMM, -1); 99 break; 100 case IMSG_VMDOP_RELOAD: 101 v = 1; 102 case IMSG_VMDOP_LOAD: 103 if (IMSG_DATA_SIZE(imsg) > 0) 104 str = get_string((uint8_t *)imsg->data, 105 IMSG_DATA_SIZE(imsg)); 106 vmd_reload(v, str); 107 free(str); 108 break; 109 default: 110 return (-1); 111 } 112 113 switch (cmd) { 114 case 0: 115 break; 116 case IMSG_VMDOP_START_VM_RESPONSE: 117 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 118 memset(&vmr, 0, sizeof(vmr)); 119 vmr.vmr_result = res; 120 vmr.vmr_id = id; 121 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 122 imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1) 123 return (-1); 124 break; 125 default: 126 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 127 imsg->hdr.peerid, -1, &res, sizeof(res)) == -1) 128 return (-1); 129 break; 130 } 131 132 return (0); 133} 134 135int 136vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg) 137{ 138 struct vmop_result vmr; 139 struct privsep *ps = p->p_ps; 140 int res = 0; 141 struct vmd_vm *vm; 142 struct vm_create_params *vcp; 143 struct vmop_info_result vir; 144 145 switch (imsg->hdr.type) { 146 case IMSG_VMDOP_START_VM_RESPONSE: 147 IMSG_SIZE_CHECK(imsg, &vmr); 148 memcpy(&vmr, imsg->data, sizeof(vmr)); 149 if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) 150 fatalx("%s: invalid vm response", __func__); 151 vcp = &vm->vm_params; 152 if (vmr.vmr_result) { 153 errno = vmr.vmr_result; 154 log_warn("%s: failed to start vm", vcp->vcp_name); 155 vm_remove(vm); 156 } else { 157 vcp->vcp_id = vmr.vmr_id; 158 log_info("%s: started vm %d successfully, tty %s", 159 vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname); 160 } 161 /* 162 * If the peerid is -1, the request originated from 163 * the parent, not the control socket. 164 */ 165 if (vm->vm_peerid == (uint32_t)-1) 166 break; 167 vmr.vmr_result = res; 168 (void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, 169 sizeof(vmr.vmr_ttyname)); 170 if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, 171 vm->vm_peerid, -1, &vmr, sizeof(vmr)) == -1) { 172 vm_remove(vm); 173 return (-1); 174 } 175 break; 176 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 177 IMSG_SIZE_CHECK(imsg, &vmr); 178 memcpy(&vmr, imsg->data, sizeof(vmr)); 179 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 180 if (vmr.vmr_result == 0) { 181 /* Remove local reference */ 182 vm = vm_getbyid(vmr.vmr_id); 183 vm_remove(vm); 184 } 185 break; 186 case IMSG_VMDOP_GET_INFO_VM_DATA: 187 IMSG_SIZE_CHECK(imsg, &vir); 188 memcpy(&vir, imsg->data, sizeof(vir)); 189 if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL) 190 (void)strlcpy(vir.vir_ttyname, vm->vm_ttyname, 191 sizeof(vir.vir_ttyname)); 192 if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, 193 imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { 194 vm_remove(vm); 195 return (-1); 196 } 197 break; 198 case IMSG_VMDOP_GET_INFO_VM_END_DATA: 199 IMSG_SIZE_CHECK(imsg, &res); 200 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 201 break; 202 default: 203 return (-1); 204 } 205 206 return (0); 207} 208 209void 210vmd_sighdlr(int sig, short event, void *arg) 211{ 212 struct privsep *ps = arg; 213 int die = 0, status, fail, id; 214 pid_t pid; 215 char *cause; 216 const char *title = "vm"; 217 218 if (privsep_process != PROC_PARENT) 219 return; 220 221 switch (sig) { 222 case SIGHUP: 223 log_info("%s: reload requested with SIGHUP", __func__); 224 225 /* 226 * This is safe because libevent uses async signal handlers 227 * that run in the event loop and not in signal context. 228 */ 229 vmd_reload(1, NULL); 230 break; 231 case SIGPIPE: 232 log_info("%s: ignoring SIGPIPE", __func__); 233 break; 234 case SIGUSR1: 235 log_info("%s: ignoring SIGUSR1", __func__); 236 break; 237 case SIGTERM: 238 case SIGINT: 239 die = 1; 240 /* FALLTHROUGH */ 241 case SIGCHLD: 242 do { 243 int len; 244 245 pid = waitpid(-1, &status, WNOHANG); 246 if (pid <= 0) 247 continue; 248 249 fail = 0; 250 if (WIFSIGNALED(status)) { 251 fail = 1; 252 len = asprintf(&cause, "terminated; signal %d", 253 WTERMSIG(status)); 254 } else if (WIFEXITED(status)) { 255 if (WEXITSTATUS(status) != 0) { 256 fail = 1; 257 len = asprintf(&cause, 258 "exited abnormally"); 259 } else 260 len = asprintf(&cause, "exited okay"); 261 } else 262 fatalx("unexpected cause of SIGCHLD"); 263 264 if (len == -1) 265 fatal("asprintf"); 266 267 for (id = 0; id < PROC_MAX; id++) { 268 if (pid == ps->ps_pid[id]) { 269 die = 1; 270 title = ps->ps_title[id]; 271 break; 272 } 273 } 274 if (fail) 275 log_warnx("lost child: %s %s", title, cause); 276 277 free(cause); 278 } while (pid > 0 || (pid == -1 && errno == EINTR)); 279 280 if (die) 281 vmd_shutdown(); 282 break; 283 default: 284 fatalx("unexpected signal"); 285 } 286} 287 288__dead void 289usage(void) 290{ 291 extern char *__progname; 292 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", 293 __progname); 294 exit(1); 295} 296 297int 298main(int argc, char **argv) 299{ 300 struct privsep *ps; 301 int ch; 302 const char *conffile = VMD_CONF; 303 304 /* log to stderr until daemonized */ 305 log_init(1, LOG_DAEMON); 306 307 if ((env = calloc(1, sizeof(*env))) == NULL) 308 fatal("calloc: env"); 309 310 while ((ch = getopt(argc, argv, "D:df:vn")) != -1) { 311 switch (ch) { 312 case 'D': 313 if (cmdline_symset(optarg) < 0) 314 log_warnx("could not parse macro definition %s", 315 optarg); 316 break; 317 case 'd': 318 env->vmd_debug = 2; 319 break; 320 case 'f': 321 conffile = optarg; 322 break; 323 case 'v': 324 env->vmd_verbose++; 325 break; 326 case 'n': 327 env->vmd_noaction = 1; 328 break; 329 default: 330 usage(); 331 } 332 } 333 334 if (env->vmd_noaction && !env->vmd_debug) 335 env->vmd_debug = 1; 336 337 /* check for root privileges */ 338 if (env->vmd_noaction == 0) { 339 if (geteuid()) 340 fatalx("need root privileges"); 341 } 342 343 ps = &env->vmd_ps; 344 ps->ps_env = env; 345 346 if (config_init(env) == -1) 347 fatal("failed to initialize configuration"); 348 349 if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) 350 fatal("unknown user %s", VMD_USER); 351 352 /* Configure the control socket */ 353 ps->ps_csock.cs_name = SOCKET_NAME; 354 TAILQ_INIT(&ps->ps_rcsocks); 355 356 /* Open /dev/vmm */ 357 if (env->vmd_noaction == 0) { 358 env->vmd_fd = open(VMM_NODE, O_RDWR); 359 if (env->vmd_fd == -1) 360 fatal("%s", VMM_NODE); 361 } 362 363 /* Configuration will be parsed after forking the children */ 364 env->vmd_conffile = conffile; 365 366 log_init(env->vmd_debug, LOG_DAEMON); 367 log_verbose(env->vmd_verbose); 368 369 if (!env->vmd_debug && daemon(0, 0) == -1) 370 fatal("can't daemonize"); 371 372 log_procinit("parent"); 373 374 ps->ps_ninstances = 1; 375 376 if (!env->vmd_noaction) 377 proc_init(ps, procs, nitems(procs)); 378 379 event_init(); 380 381 signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); 382 signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); 383 signal_set(&ps->ps_evsigchld, SIGCHLD, vmd_sighdlr, ps); 384 signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); 385 signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); 386 signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); 387 388 signal_add(&ps->ps_evsigint, NULL); 389 signal_add(&ps->ps_evsigterm, NULL); 390 signal_add(&ps->ps_evsigchld, NULL); 391 signal_add(&ps->ps_evsighup, NULL); 392 signal_add(&ps->ps_evsigpipe, NULL); 393 signal_add(&ps->ps_evsigusr1, NULL); 394 395 if (!env->vmd_noaction) 396 proc_listen(ps, procs, nitems(procs)); 397 398 if (vmd_configure() == -1) 399 fatalx("configuration failed"); 400 401 event_dispatch(); 402 403 log_debug("parent exiting"); 404 405 return (0); 406} 407 408int 409vmd_configure(void) 410{ 411 /* 412 * pledge in the parent process: 413 * stdio - for malloc and basic I/O including events. 414 * rpath - for reload to open and read the configuration files. 415 * wpath - for opening disk images and tap devices. 416 * tty - for openpty. 417 * proc - run kill to terminate its children safely. 418 * sendfd - for disks, interfaces and other fds. 419 */ 420 if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1) 421 fatal("pledge"); 422 423 if (parse_config(env->vmd_conffile) == -1) { 424 proc_kill(&env->vmd_ps); 425 exit(1); 426 } 427 428 if (env->vmd_noaction) { 429 fprintf(stderr, "configuration OK\n"); 430 proc_kill(&env->vmd_ps); 431 exit(0); 432 } 433 434 return (0); 435} 436 437void 438vmd_reload(int reset, const char *filename) 439{ 440 /* Switch back to the default config file */ 441 if (filename == NULL || *filename == '\0') 442 filename = env->vmd_conffile; 443 444 log_debug("%s: level %d config file %s", __func__, reset, filename); 445 446 if (reset) 447 config_setreset(env, CONFIG_ALL); 448 449 if (parse_config(filename) == -1) { 450 log_debug("%s: failed to load config file %s", 451 __func__, filename); 452 } 453} 454 455void 456vmd_shutdown(void) 457{ 458 proc_kill(&env->vmd_ps); 459 free(env); 460 461 log_warnx("parent terminating"); 462 exit(0); 463} 464 465struct vmd_vm * 466vm_getbyvmid(uint32_t vmid) 467{ 468 struct vmd_vm *vm; 469 470 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 471 if (vm->vm_vmid == vmid) 472 return (vm); 473 } 474 475 return (NULL); 476} 477 478struct vmd_vm * 479vm_getbyid(uint32_t id) 480{ 481 struct vmd_vm *vm; 482 483 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 484 if (vm->vm_params.vcp_id == id) 485 return (vm); 486 } 487 488 return (NULL); 489} 490 491struct vmd_vm * 492vm_getbyname(const char *name) 493{ 494 struct vmd_vm *vm; 495 496 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 497 if (strcmp(vm->vm_params.vcp_name, name) == 0) 498 return (vm); 499 } 500 501 return (NULL); 502} 503 504void 505vm_remove(struct vmd_vm *vm) 506{ 507 unsigned int i; 508 509 if (vm == NULL) 510 return; 511 512 TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); 513 514 for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { 515 if (vm->vm_disks[i] != -1) 516 close(vm->vm_disks[i]); 517 } 518 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 519 if (vm->vm_ifs[i] != -1) 520 close(vm->vm_ifs[i]); 521 } 522 if (vm->vm_kernel != -1) 523 close(vm->vm_kernel); 524 if (vm->vm_tty != -1) 525 close(vm->vm_tty); 526 527 free(vm); 528} 529 530char * 531get_string(uint8_t *ptr, size_t len) 532{ 533 size_t i; 534 535 for (i = 0; i < len; i++) 536 if (!isprint(ptr[i])) 537 break; 538 539 return strndup(ptr, i); 540} 541