vmd.c revision 1.27
1/* $OpenBSD: vmd.c,v 1.27 2016/02/05 11:40:15 reyk 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 vcp->vcp_id = vmr.vmr_id; 153 154 /* 155 * If the peerid is not -1, forward the response back to the 156 * the control socket. If it is -1, the request originated 157 * from the parent, not the control socket. 158 */ 159 if (vm->vm_peerid != (uint32_t)-1) { 160 vmr.vmr_result = res; 161 (void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, 162 sizeof(vmr.vmr_ttyname)); 163 if (proc_compose_imsg(ps, PROC_CONTROL, -1, 164 imsg->hdr.type, vm->vm_peerid, -1, 165 &vmr, sizeof(vmr)) == -1) { 166 errno = vmr.vmr_result; 167 log_warn("%s: failed to foward vm result", 168 vcp->vcp_name); 169 vm_remove(vm); 170 return (-1); 171 } 172 } 173 174 if (vmr.vmr_result) { 175 errno = vmr.vmr_result; 176 log_warn("%s: failed to start vm", vcp->vcp_name); 177 vm_remove(vm); 178 } else { 179 log_info("%s: started vm %d successfully, tty %s", 180 vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname); 181 } 182 break; 183 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 184 IMSG_SIZE_CHECK(imsg, &vmr); 185 memcpy(&vmr, imsg->data, sizeof(vmr)); 186 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 187 if (vmr.vmr_result == 0) { 188 /* Remove local reference */ 189 vm = vm_getbyid(vmr.vmr_id); 190 vm_remove(vm); 191 } 192 break; 193 case IMSG_VMDOP_GET_INFO_VM_DATA: 194 IMSG_SIZE_CHECK(imsg, &vir); 195 memcpy(&vir, imsg->data, sizeof(vir)); 196 if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL) 197 (void)strlcpy(vir.vir_ttyname, vm->vm_ttyname, 198 sizeof(vir.vir_ttyname)); 199 if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, 200 imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { 201 vm_remove(vm); 202 return (-1); 203 } 204 break; 205 case IMSG_VMDOP_GET_INFO_VM_END_DATA: 206 IMSG_SIZE_CHECK(imsg, &res); 207 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 208 break; 209 default: 210 return (-1); 211 } 212 213 return (0); 214} 215 216void 217vmd_sighdlr(int sig, short event, void *arg) 218{ 219 struct privsep *ps = arg; 220 int die = 0, status, fail, id; 221 pid_t pid; 222 char *cause; 223 const char *title = "vm"; 224 225 if (privsep_process != PROC_PARENT) 226 return; 227 228 switch (sig) { 229 case SIGHUP: 230 log_info("%s: reload requested with SIGHUP", __func__); 231 232 /* 233 * This is safe because libevent uses async signal handlers 234 * that run in the event loop and not in signal context. 235 */ 236 vmd_reload(1, NULL); 237 break; 238 case SIGPIPE: 239 log_info("%s: ignoring SIGPIPE", __func__); 240 break; 241 case SIGUSR1: 242 log_info("%s: ignoring SIGUSR1", __func__); 243 break; 244 case SIGTERM: 245 case SIGINT: 246 die = 1; 247 /* FALLTHROUGH */ 248 case SIGCHLD: 249 do { 250 int len; 251 252 pid = waitpid(-1, &status, WNOHANG); 253 if (pid <= 0) 254 continue; 255 256 fail = 0; 257 if (WIFSIGNALED(status)) { 258 fail = 1; 259 len = asprintf(&cause, "terminated; signal %d", 260 WTERMSIG(status)); 261 } else if (WIFEXITED(status)) { 262 if (WEXITSTATUS(status) != 0) { 263 fail = 1; 264 len = asprintf(&cause, 265 "exited abnormally"); 266 } else 267 len = asprintf(&cause, "exited okay"); 268 } else 269 fatalx("unexpected cause of SIGCHLD"); 270 271 if (len == -1) 272 fatal("asprintf"); 273 274 for (id = 0; id < PROC_MAX; id++) { 275 if (pid == ps->ps_pid[id]) { 276 die = 1; 277 title = ps->ps_title[id]; 278 break; 279 } 280 } 281 if (fail) 282 log_warnx("lost child: %s %s", title, cause); 283 284 free(cause); 285 } while (pid > 0 || (pid == -1 && errno == EINTR)); 286 287 if (die) 288 vmd_shutdown(); 289 break; 290 default: 291 fatalx("unexpected signal"); 292 } 293} 294 295__dead void 296usage(void) 297{ 298 extern char *__progname; 299 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", 300 __progname); 301 exit(1); 302} 303 304int 305main(int argc, char **argv) 306{ 307 struct privsep *ps; 308 int ch; 309 const char *conffile = VMD_CONF; 310 311 /* log to stderr until daemonized */ 312 log_init(1, LOG_DAEMON); 313 314 if ((env = calloc(1, sizeof(*env))) == NULL) 315 fatal("calloc: env"); 316 317 while ((ch = getopt(argc, argv, "D:df:vn")) != -1) { 318 switch (ch) { 319 case 'D': 320 if (cmdline_symset(optarg) < 0) 321 log_warnx("could not parse macro definition %s", 322 optarg); 323 break; 324 case 'd': 325 env->vmd_debug = 2; 326 break; 327 case 'f': 328 conffile = optarg; 329 break; 330 case 'v': 331 env->vmd_verbose++; 332 break; 333 case 'n': 334 env->vmd_noaction = 1; 335 break; 336 default: 337 usage(); 338 } 339 } 340 341 if (env->vmd_noaction && !env->vmd_debug) 342 env->vmd_debug = 1; 343 344 /* check for root privileges */ 345 if (env->vmd_noaction == 0) { 346 if (geteuid()) 347 fatalx("need root privileges"); 348 } 349 350 ps = &env->vmd_ps; 351 ps->ps_env = env; 352 353 if (config_init(env) == -1) 354 fatal("failed to initialize configuration"); 355 356 if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) 357 fatal("unknown user %s", VMD_USER); 358 359 /* Configure the control socket */ 360 ps->ps_csock.cs_name = SOCKET_NAME; 361 TAILQ_INIT(&ps->ps_rcsocks); 362 363 /* Open /dev/vmm */ 364 if (env->vmd_noaction == 0) { 365 env->vmd_fd = open(VMM_NODE, O_RDWR); 366 if (env->vmd_fd == -1) 367 fatal("%s", VMM_NODE); 368 } 369 370 /* Configuration will be parsed after forking the children */ 371 env->vmd_conffile = conffile; 372 373 log_init(env->vmd_debug, LOG_DAEMON); 374 log_verbose(env->vmd_verbose); 375 376 if (!env->vmd_debug && daemon(0, 0) == -1) 377 fatal("can't daemonize"); 378 379 log_procinit("parent"); 380 381 ps->ps_ninstances = 1; 382 383 if (!env->vmd_noaction) 384 proc_init(ps, procs, nitems(procs)); 385 386 event_init(); 387 388 signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); 389 signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); 390 signal_set(&ps->ps_evsigchld, SIGCHLD, vmd_sighdlr, ps); 391 signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); 392 signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); 393 signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); 394 395 signal_add(&ps->ps_evsigint, NULL); 396 signal_add(&ps->ps_evsigterm, NULL); 397 signal_add(&ps->ps_evsigchld, NULL); 398 signal_add(&ps->ps_evsighup, NULL); 399 signal_add(&ps->ps_evsigpipe, NULL); 400 signal_add(&ps->ps_evsigusr1, NULL); 401 402 if (!env->vmd_noaction) 403 proc_listen(ps, procs, nitems(procs)); 404 405 if (vmd_configure() == -1) 406 fatalx("configuration failed"); 407 408 event_dispatch(); 409 410 log_debug("parent exiting"); 411 412 return (0); 413} 414 415int 416vmd_configure(void) 417{ 418 /* 419 * pledge in the parent process: 420 * stdio - for malloc and basic I/O including events. 421 * rpath - for reload to open and read the configuration files. 422 * wpath - for opening disk images and tap devices. 423 * tty - for openpty. 424 * proc - run kill to terminate its children safely. 425 * sendfd - for disks, interfaces and other fds. 426 */ 427 if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1) 428 fatal("pledge"); 429 430 if (parse_config(env->vmd_conffile) == -1) { 431 proc_kill(&env->vmd_ps); 432 exit(1); 433 } 434 435 if (env->vmd_noaction) { 436 fprintf(stderr, "configuration OK\n"); 437 proc_kill(&env->vmd_ps); 438 exit(0); 439 } 440 441 return (0); 442} 443 444void 445vmd_reload(int reset, const char *filename) 446{ 447 /* Switch back to the default config file */ 448 if (filename == NULL || *filename == '\0') 449 filename = env->vmd_conffile; 450 451 log_debug("%s: level %d config file %s", __func__, reset, filename); 452 453 if (reset) 454 config_setreset(env, CONFIG_ALL); 455 456 if (parse_config(filename) == -1) { 457 log_debug("%s: failed to load config file %s", 458 __func__, filename); 459 } 460} 461 462void 463vmd_shutdown(void) 464{ 465 proc_kill(&env->vmd_ps); 466 free(env); 467 468 log_warnx("parent terminating"); 469 exit(0); 470} 471 472struct vmd_vm * 473vm_getbyvmid(uint32_t vmid) 474{ 475 struct vmd_vm *vm; 476 477 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 478 if (vm->vm_vmid == vmid) 479 return (vm); 480 } 481 482 return (NULL); 483} 484 485struct vmd_vm * 486vm_getbyid(uint32_t id) 487{ 488 struct vmd_vm *vm; 489 490 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 491 if (vm->vm_params.vcp_id == id) 492 return (vm); 493 } 494 495 return (NULL); 496} 497 498struct vmd_vm * 499vm_getbyname(const char *name) 500{ 501 struct vmd_vm *vm; 502 503 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 504 if (strcmp(vm->vm_params.vcp_name, name) == 0) 505 return (vm); 506 } 507 508 return (NULL); 509} 510 511void 512vm_remove(struct vmd_vm *vm) 513{ 514 unsigned int i; 515 516 if (vm == NULL) 517 return; 518 519 TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); 520 521 for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { 522 if (vm->vm_disks[i] != -1) 523 close(vm->vm_disks[i]); 524 } 525 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 526 if (vm->vm_ifs[i] != -1) 527 close(vm->vm_ifs[i]); 528 } 529 if (vm->vm_kernel != -1) 530 close(vm->vm_kernel); 531 if (vm->vm_tty != -1) 532 close(vm->vm_tty); 533 534 free(vm); 535} 536 537char * 538get_string(uint8_t *ptr, size_t len) 539{ 540 size_t i; 541 542 for (i = 0; i < len; i++) 543 if (!isprint(ptr[i])) 544 break; 545 546 return strndup(ptr, i); 547} 548