1 /* $OpenBSD: main.c,v 1.17 2016/05/10 11:00:54 mlarkin 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/types.h> 20 #include <sys/socket.h> 21 #include <sys/queue.h> 22 #include <sys/un.h> 23 24 #include <machine/vmmvar.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdint.h> 31 #include <limits.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <util.h> 35 #include <imsg.h> 36 37 #include "vmd.h" 38 #include "proc.h" 39 #include "vmctl.h" 40 41 static const char *socket_name = SOCKET_NAME; 42 static int ctl_sock = -1; 43 static int tty_autoconnect = 0; 44 45 __dead void usage(void); 46 __dead void ctl_usage(struct ctl_command *); 47 48 int vmm_action(struct parse_result *); 49 50 int ctl_console(struct parse_result *, int, char *[]); 51 int ctl_create(struct parse_result *, int, char *[]); 52 int ctl_load(struct parse_result *, int, char *[]); 53 int ctl_start(struct parse_result *, int, char *[]); 54 int ctl_status(struct parse_result *, int, char *[]); 55 int ctl_stop(struct parse_result *, int, char *[]); 56 57 struct ctl_command ctl_commands[] = { 58 { "console", CMD_CONSOLE, ctl_console, "id" }, 59 { "create", CMD_CREATE, ctl_create, "\"path\" -s size", 1 }, 60 { "load", CMD_LOAD, ctl_load, "[path]" }, 61 { "reload", CMD_RELOAD, ctl_load, "[path]" }, 62 { "start", CMD_START, ctl_start, "\"name\"" 63 " [-c] -k kernel -m size [-i count] [-d disk]*" }, 64 { "status", CMD_STATUS, ctl_status, "[id]" }, 65 { "stop", CMD_STOP, ctl_stop, "id" }, 66 { NULL } 67 }; 68 69 __dead void 70 usage(void) 71 { 72 extern char *__progname; 73 int i; 74 75 fprintf(stderr, "usage:\t%s command [arg ...]\n", 76 __progname); 77 for (i = 0; ctl_commands[i].name != NULL; i++) { 78 fprintf(stderr, "\t%s %s %s\n", __progname, 79 ctl_commands[i].name, ctl_commands[i].usage); 80 } 81 exit(1); 82 } 83 84 __dead void 85 ctl_usage(struct ctl_command *ctl) 86 { 87 extern char *__progname; 88 89 fprintf(stderr, "usage:\t%s %s %s\n", __progname, 90 ctl->name, ctl->usage); 91 exit(1); 92 } 93 94 int 95 main(int argc, char *argv[]) 96 { 97 int ch; 98 99 while ((ch = getopt(argc, argv, "")) != -1) { 100 switch (ch) { 101 default: 102 usage(); 103 /* NOTREACHED */ 104 } 105 } 106 argc -= optind; 107 argv += optind; 108 optreset = 1; 109 110 if (argc < 1) 111 usage(); 112 113 return (parse(argc, argv)); 114 } 115 116 int 117 parse(int argc, char *argv[]) 118 { 119 struct ctl_command *ctl = NULL; 120 struct parse_result res; 121 int i; 122 123 memset(&res, 0, sizeof(res)); 124 res.nifs = -1; 125 126 for (i = 0; ctl_commands[i].name != NULL; i++) { 127 if (strncmp(ctl_commands[i].name, 128 argv[0], strlen(argv[0])) == 0) { 129 if (ctl != NULL) { 130 fprintf(stderr, 131 "ambiguous argument: %s\n", argv[0]); 132 usage(); 133 } 134 ctl = &ctl_commands[i]; 135 } 136 } 137 138 if (ctl == NULL) { 139 fprintf(stderr, "unknown argument: %s\n", argv[0]); 140 usage(); 141 } 142 143 res.action = ctl->action; 144 res.ctl = ctl; 145 146 if (!ctl->has_pledge) { 147 /* pledge(2) default if command doesn't have its own pledge */ 148 if (pledge("stdio rpath exec unix", NULL) == -1) 149 err(1, "pledge"); 150 } 151 if (ctl->main(&res, argc, argv) != 0) 152 err(1, "failed"); 153 154 if (ctl_sock != -1) { 155 close(ibuf->fd); 156 free(ibuf); 157 } 158 159 return (0); 160 } 161 162 int 163 vmmaction(struct parse_result *res) 164 { 165 struct sockaddr_un sun; 166 struct imsg imsg; 167 int done = 0; 168 int n; 169 int ret, action; 170 171 if (ctl_sock == -1) { 172 if ((ctl_sock = socket(AF_UNIX, 173 SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) 174 err(1, "socket"); 175 176 bzero(&sun, sizeof(sun)); 177 sun.sun_family = AF_UNIX; 178 strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); 179 180 if (connect(ctl_sock, 181 (struct sockaddr *)&sun, sizeof(sun)) == -1) 182 err(1, "connect: %s", socket_name); 183 184 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 185 err(1, "malloc"); 186 imsg_init(ibuf, ctl_sock); 187 } 188 189 switch (res->action) { 190 case CMD_START: 191 ret = start_vm(res->name, res->size, res->nifs, 192 res->ndisks, res->disks, res->path); 193 if (ret) { 194 errno = ret; 195 err(1, "start VM operation failed"); 196 } 197 break; 198 case CMD_STOP: 199 terminate_vm(res->id, res->name); 200 break; 201 case CMD_STATUS: 202 get_info_vm(res->id, res->name, 0); 203 break; 204 case CMD_CONSOLE: 205 get_info_vm(res->id, res->name, 1); 206 break; 207 case CMD_RELOAD: 208 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, 209 res->path, res->path == NULL ? 0 : strlen(res->path) + 1); 210 done = 1; 211 break; 212 case CMD_LOAD: 213 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 214 res->path, res->path == NULL ? 0 : strlen(res->path) + 1); 215 done = 1; 216 break; 217 case CMD_CREATE: 218 case NONE: 219 break; 220 } 221 222 action = res->action; 223 parse_free(res); 224 225 while (ibuf->w.queued) 226 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 227 err(1, "write error"); 228 229 while (!done) { 230 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 231 errx(1, "imsg_read error"); 232 if (n == 0) 233 errx(1, "pipe closed"); 234 235 while (!done) { 236 if ((n = imsg_get(ibuf, &imsg)) == -1) 237 errx(1, "imsg_get error"); 238 if (n == 0) 239 break; 240 241 if (imsg.hdr.type == IMSG_CTL_FAIL) { 242 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) { 243 memcpy(&ret, imsg.data, sizeof(ret)); 244 errno = ret; 245 warn("command failed"); 246 } else { 247 warnx("command failed"); 248 } 249 done = 1; 250 break; 251 } 252 253 ret = 0; 254 switch (action) { 255 case CMD_START: 256 done = start_vm_complete(&imsg, &ret, 257 tty_autoconnect); 258 break; 259 case CMD_STOP: 260 done = terminate_vm_complete(&imsg, &ret); 261 break; 262 case CMD_CONSOLE: 263 case CMD_STATUS: 264 done = add_info(&imsg, &ret); 265 break; 266 default: 267 done = 1; 268 break; 269 } 270 271 imsg_free(&imsg); 272 } 273 } 274 275 return (0); 276 } 277 278 void 279 parse_free(struct parse_result *res) 280 { 281 size_t i; 282 283 free(res->name); 284 free(res->path); 285 for (i = 0; i < res->ndisks; i++) 286 free(res->disks[i]); 287 free(res->disks); 288 memset(res, 0, sizeof(*res)); 289 } 290 291 int 292 parse_ifs(struct parse_result *res, char *word, int val) 293 { 294 const char *error; 295 296 if (word != NULL) { 297 val = strtonum(word, 0, INT_MAX, &error); 298 if (error != NULL) { 299 warnx("invalid count \"%s\": %s", word, error); 300 return (-1); 301 } 302 } 303 res->nifs = val; 304 return (0); 305 } 306 307 int 308 parse_size(struct parse_result *res, char *word, long long val) 309 { 310 if (word != NULL) { 311 if (scan_scaled(word, &val) != 0) { 312 warn("invalid size: %s", word); 313 return (-1); 314 } 315 } 316 317 if (val < (1024 * 1024)) { 318 warnx("size must be at least one megabyte"); 319 return (-1); 320 } else 321 res->size = val / 1024 / 1024; 322 323 if ((res->size * 1024 * 1024) != val) 324 warnx("size rounded to %lld megabytes", res->size); 325 326 return (0); 327 } 328 329 int 330 parse_disk(struct parse_result *res, char *word) 331 { 332 char **disks; 333 char *s; 334 335 if ((disks = reallocarray(res->disks, res->ndisks + 1, 336 sizeof(char *))) == NULL) { 337 warn("reallocarray"); 338 return (-1); 339 } 340 if ((s = strdup(word)) == NULL) { 341 warn("strdup"); 342 return (-1); 343 } 344 disks[res->ndisks] = s; 345 res->disks = disks; 346 res->ndisks++; 347 348 return (0); 349 } 350 351 int 352 parse_vmid(struct parse_result *res, char *word) 353 { 354 const char *error; 355 uint32_t id; 356 357 if (word == NULL) { 358 warnx("missing vmid argument"); 359 return (-1); 360 } 361 id = strtonum(word, 0, UINT32_MAX, &error); 362 if (error == NULL) { 363 res->id = id; 364 res->name = NULL; 365 } else { 366 if (strlen(word) >= VMM_MAX_NAME_LEN) { 367 warnx("name too long"); 368 return (-1); 369 } 370 res->id = 0; 371 if ((res->name = strdup(word)) == NULL) 372 errx(1, "strdup"); 373 } 374 375 return (0); 376 } 377 378 int 379 ctl_create(struct parse_result *res, int argc, char *argv[]) 380 { 381 int ch, ret; 382 const char *paths[2]; 383 384 if (argc < 2) 385 ctl_usage(res->ctl); 386 387 paths[0] = argv[1]; 388 paths[1] = NULL; 389 if (pledge("stdio rpath wpath cpath", NULL) == -1) 390 err(1, "pledge"); 391 argc--; 392 argv++; 393 394 while ((ch = getopt(argc, argv, "s:")) != -1) { 395 switch (ch) { 396 case 's': 397 if (parse_size(res, optarg, 0) != 0) 398 errx(1, "invalid size: %s", optarg); 399 break; 400 default: 401 ctl_usage(res->ctl); 402 /* NOTREACHED */ 403 } 404 } 405 406 if (res->size == 0) { 407 fprintf(stderr, "missing size argument\n"); 408 ctl_usage(res->ctl); 409 } 410 ret = create_imagefile(paths[0], res->size); 411 if (ret != 0) { 412 errno = ret; 413 err(1, "create imagefile operation failed"); 414 } else 415 warnx("imagefile created"); 416 return (0); 417 } 418 419 int 420 ctl_status(struct parse_result *res, int argc, char *argv[]) 421 { 422 if (argc == 2) { 423 if (parse_vmid(res, argv[1]) == -1) 424 errx(1, "invalid id: %s", argv[1]); 425 } else if (argc > 2) 426 ctl_usage(res->ctl); 427 428 return (vmmaction(res)); 429 } 430 431 int 432 ctl_load(struct parse_result *res, int argc, char *argv[]) 433 { 434 char *config_file = NULL; 435 436 if (argc == 2) 437 config_file = argv[1]; 438 else if (argc > 2) 439 ctl_usage(res->ctl); 440 441 if (config_file != NULL && 442 (res->path = strdup(config_file)) == NULL) 443 err(1, "strdup"); 444 445 return (vmmaction(res)); 446 } 447 448 int 449 ctl_start(struct parse_result *res, int argc, char *argv[]) 450 { 451 int ch; 452 char path[PATH_MAX]; 453 454 if (argc < 2) 455 ctl_usage(res->ctl); 456 457 if ((res->name = strdup(argv[1])) == NULL) 458 errx(1, "strdup"); 459 argc--; 460 argv++; 461 462 while ((ch = getopt(argc, argv, "ck:m:d:i:")) != -1) { 463 switch (ch) { 464 case 'c': 465 tty_autoconnect = 1; 466 break; 467 case 'k': 468 if (res->path) 469 errx(1, "kernel specified multiple times"); 470 if (realpath(optarg, path) == NULL) 471 err(1, "invalid kernel path"); 472 if ((res->path = strdup(path)) == NULL) 473 errx(1, "strdup"); 474 break; 475 case 'm': 476 if (res->size) 477 errx(1, "memory specified multiple times"); 478 if (parse_size(res, optarg, 0) != 0) 479 errx(1, "invalid memory size: %s", optarg); 480 break; 481 case 'd': 482 if (realpath(optarg, path) == NULL) 483 err(1, "invalid disk path"); 484 if (parse_disk(res, path) != 0) 485 errx(1, "invalid disk: %s", optarg); 486 break; 487 case 'i': 488 if (res->nifs != -1) 489 errx(1, "interfaces specified multiple times"); 490 if (parse_ifs(res, optarg, 0) != 0) 491 errx(1, "invalid interface count: %s", optarg); 492 break; 493 default: 494 ctl_usage(res->ctl); 495 /* NOTREACHED */ 496 } 497 } 498 499 return (vmmaction(res)); 500 } 501 502 int 503 ctl_stop(struct parse_result *res, int argc, char *argv[]) 504 { 505 if (argc == 2) { 506 if (parse_vmid(res, argv[1]) == -1) 507 errx(1, "invalid id: %s", argv[1]); 508 } else if (argc != 2) 509 ctl_usage(res->ctl); 510 511 return (vmmaction(res)); 512 } 513 514 int 515 ctl_console(struct parse_result *res, int argc, char *argv[]) 516 { 517 if (argc == 2) { 518 if (parse_vmid(res, argv[1]) == -1) 519 errx(1, "invalid id: %s", argv[1]); 520 } else if (argc != 2) 521 ctl_usage(res->ctl); 522 523 return (vmmaction(res)); 524 } 525 526 __dead void 527 ctl_openconsole(const char *name) 528 { 529 closefrom(STDERR_FILENO + 1); 530 execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "9600", (char *)NULL); 531 err(1, "failed to open the console"); 532 } 533