1/*- 2 * Copyright (c) 2015 Antti Kantee. All Rights Reserved. 3 * Copyright (c) 2014 Martin Lucina. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 15 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27/* 28 * NOTE: this implementation is currently a sketch of what things 29 * should looks like. 30 */ 31 32#include <sys/param.h> 33#include <sys/disklabel.h> 34#include <sys/ioctl.h> 35#include <sys/stat.h> 36 37#include <ufs/ufs/ufsmount.h> 38#include <isofs/cd9660/cd9660_mount.h> 39 40#include <dev/vndvar.h> 41 42#include <assert.h> 43#include <err.h> 44#include <errno.h> 45#include <fcntl.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50 51#include <rump/rump.h> 52#include <rump/netconfig.h> 53 54#include <rumprun-base/config.h> 55#include <rumprun-base/parseargs.h> 56 57#include <bmk-core/jsmn.h> 58 59/* helper macros */ 60#define T_SIZE(t) ((t)->end - (t)->start) 61#define T_STR(t,d) ((t)->start + d) 62#define T_PRINTFSTAR(t,d) T_SIZE(t), T_STR(t,d) 63#define T_STREQ(t, d, str) (strncmp(T_STR(t,d), str, T_SIZE(t)) == 0) 64 65#define T_STRCPY(dest, destsize, t, d) \ 66 do { \ 67 unsigned long strsize = MIN(destsize-1,T_SIZE(t)); \ 68 strncpy(dest, T_STR(t, d), strsize); \ 69 dest[strsize] = '\0'; \ 70 } while (/*CONSTCOND*/0) 71 72#define T_CHECKTYPE(t, data, exp, fun) \ 73 do { \ 74 if (t->type != exp) { \ 75 errx(1, "unexpected type for token \"%.*s\" " \ 76 "in \"%s\"", T_PRINTFSTAR(t,data), fun); \ 77 } \ 78 } while (/*CONSTCOND*/0) 79 80#define T_CHECKSIZE(t, data, exp, fun) \ 81 do { \ 82 if (t->size != exp) { \ 83 errx(1, "unexpected size for token \"%.*s\" " \ 84 "in \"%s\"", T_PRINTFSTAR(t,data), fun); \ 85 } \ 86 } while (/*CONSTCOND*/0) 87 88static char * 89token2cstr(jsmntok_t *t, char *data) 90{ 91 92 *(T_STR(t, data) + T_SIZE(t)) = '\0'; 93 return T_STR(t, data); 94} 95 96struct rumprun_execs rumprun_execs = TAILQ_HEAD_INITIALIZER(rumprun_execs); 97 98static void 99makeargv(char *argvstr) 100{ 101 struct rumprun_exec *rre; 102 char **argv; 103 int nargs; 104 105 rumprun_parseargs(argvstr, &nargs, 0); 106 rre = malloc(sizeof(*rre) + (nargs+1) * sizeof(*argv)); 107 if (rre == NULL) 108 err(1, "could not allocate rre"); 109 110 rumprun_parseargs(argvstr, &nargs, rre->rre_argv); 111 rre->rre_argv[nargs] = NULL; 112 rre->rre_flags = RUMPRUN_EXEC_CMDLINE; 113 rre->rre_argc = nargs; 114 115 TAILQ_INSERT_TAIL(&rumprun_execs, rre, rre_entries); 116} 117 118static int 119handle_cmdline(jsmntok_t *t, int left, char *data) 120{ 121 122 T_CHECKTYPE(t, data, JSMN_STRING, __func__); 123 124 makeargv(token2cstr(t, data)); 125 126 return 1; 127} 128 129/* 130 * "rc": [ 131 * { "bin" : "binname", 132 * "argv" : [ "arg1", "arg2", ... ], (optional) 133 * "runmode" : "& OR |" (optional) 134 * }, 135 * .... 136 * ] 137 */ 138static int 139addbin(jsmntok_t *t, char *data) 140{ 141 jsmntok_t *t_bin, *t_argv, *t_runmode; 142 struct rumprun_exec *rre; 143 jsmntok_t *key, *value; 144 char *binname; 145 int binsize = 1; 146 int objleft = t->size; 147 int rreflags, i; 148 149 T_CHECKTYPE(t, data, JSMN_OBJECT, __func__); 150 t++; 151 152 /* process and validate data */ 153 t_bin = t_argv = t_runmode = NULL; 154 while (objleft--) { 155 int mysize; 156 157 key = t; 158 value = t+1; 159 160 T_CHECKTYPE(key, data, JSMN_STRING, __func__); 161 162 if (T_STREQ(key, data, "bin")) { 163 t_bin = value; 164 165 T_CHECKSIZE(key, data, 1, __func__); 166 T_CHECKTYPE(value, data, JSMN_STRING, __func__); 167 T_CHECKSIZE(value, data, 0, __func__); 168 169 mysize = 1 + 1; 170 } else if (T_STREQ(key, data, "argv")) { 171 T_CHECKTYPE(value, data, JSMN_ARRAY, __func__); 172 173 t_argv = value; 174 175 /* key + array + array contents */ 176 mysize = 1 + 1 + value->size; 177 } else if (T_STREQ(key, data, "runmode")) { 178 t_runmode = value; 179 180 T_CHECKSIZE(key, data, 1, __func__); 181 T_CHECKTYPE(value, data, JSMN_STRING, __func__); 182 T_CHECKSIZE(value, data, 0, __func__); 183 184 mysize = 1 + 1; 185 } else { 186 errx(1, "unexpected key \"%.*s\" in \"%s\"", 187 T_PRINTFSTAR(key, data), __func__); 188 } 189 190 t += mysize; 191 binsize += mysize; 192 } 193 194 if (!t_bin) 195 errx(1, "missing \"bin\" for rc entry"); 196 binname = token2cstr(t_bin, data); 197 198 if (t_runmode) { 199 bool sizeok = T_SIZE(t_runmode) == 1; 200 201 if (sizeok && *T_STR(t_runmode,data) == '&') { 202 rreflags = RUMPRUN_EXEC_BACKGROUND; 203 } else if (sizeok && *T_STR(t_runmode,data) == '|') { 204 rreflags = RUMPRUN_EXEC_PIPE; 205 } else { 206 errx(1, "invalid runmode \"%.*s\" for bin \"%.*s\"", 207 T_PRINTFSTAR(t_runmode, data), 208 T_PRINTFSTAR(t_bin, data)); 209 } 210 } else { 211 rreflags = 0; 212 } 213 214 /* ok, we got everything. save into rumprun_exec structure */ 215 rre = malloc(sizeof(*rre) + (2+t_argv->size) * sizeof(char *)); 216 if (rre == NULL) 217 err(1, "allocate rumprun_exec"); 218 rre->rre_flags = rreflags; 219 rre->rre_argc = 1+t_argv->size; 220 rre->rre_argv[0] = binname; 221 for (i = 1, t = t_argv+1; i <= t_argv->size; i++, t++) { 222 T_CHECKTYPE(t, data, JSMN_STRING, __func__); 223 rre->rre_argv[i] = token2cstr(t, data); 224 } 225 rre->rre_argv[rre->rre_argc] = NULL; 226 227 TAILQ_INSERT_TAIL(&rumprun_execs, rre, rre_entries); 228 229 return binsize; 230} 231 232static int 233handle_rc(jsmntok_t *t, int left, char *data) 234{ 235 int onesize, totsize, rem; 236 237 T_CHECKTYPE(t, data, JSMN_ARRAY, __func__); 238 239 rem = t->size; 240 for (t++, totsize = 1; rem; rem--) { 241 onesize = addbin(t, data); 242 totsize += onesize; 243 t += onesize; 244 } 245 246 return totsize; 247} 248 249static int 250handle_env(jsmntok_t *t, int left, char *data) 251{ 252 253 T_CHECKTYPE(t, data, JSMN_STRING, __func__); 254 255 if (putenv(token2cstr(t, data)) == -1) 256 err(1, "putenv"); 257 258 return 1; 259} 260 261static int 262handle_hostname(jsmntok_t *t, int left, char *data) 263{ 264 265 T_CHECKTYPE(t, data, JSMN_STRING, __func__); 266 267 if (sethostname(token2cstr(t, data), T_SIZE(t)) == -1) 268 err(1, "sethostname"); 269 270 return 1; 271} 272 273static void 274config_ipv4(const char *ifname, const char *method, 275 const char *addr, const char *mask, const char *gw) 276{ 277 int rv; 278 279 if (strcmp(method, "dhcp") == 0) { 280 if ((rv = rump_pub_netconfig_dhcp_ipv4_oneshot(ifname)) != 0) 281 errx(1, "configuring dhcp for %s failed: %d", 282 ifname, rv); 283 } else { 284 if (strcmp(method, "static") != 0) { 285 errx(1, "method \"static\" or \"dhcp\" expected, " 286 "got \"%s\"", method); 287 } 288 289 if (!addr || !mask) { 290 errx(1, "static net cfg missing addr or mask"); 291 } 292 293 if ((rv = rump_pub_netconfig_ipv4_ifaddr_cidr(ifname, 294 addr, atoi(mask))) != 0) { 295 errx(1, "ifconfig \"%s\" for \"%s/%s\" failed", 296 ifname, addr, mask); 297 } 298 if (gw && (rv = rump_pub_netconfig_ipv4_gw(gw)) != 0) { 299 errx(1, "gw \"%s\" addition failed", gw); 300 } 301 } 302} 303 304static void 305config_ipv6(const char *ifname, const char *method, 306 const char *addr, const char *mask, const char *gw) 307{ 308 int rv; 309 310 if (strcmp(method, "auto") == 0) { 311 if ((rv = rump_pub_netconfig_auto_ipv6(ifname)) != 0) { 312 errx(1, "ipv6 autoconfig failed"); 313 } 314 } else { 315 if (strcmp(method, "static") != 0) { 316 errx(1, "method \"static\" or \"dhcp\" expected, " 317 "got \"%s\"", method); 318 } 319 320 if (!addr || !mask) { 321 errx(1, "static net cfg missing addr or mask"); 322 } 323 324 if ((rv = rump_pub_netconfig_ipv6_ifaddr(ifname, 325 addr, atoi(mask))) != 0) { 326 errx(1, "ifconfig \"%s\" for \"%s/%s\" failed", 327 ifname, addr, mask); 328 } 329 if (gw && (rv = rump_pub_netconfig_ipv6_gw(gw)) != 0) { 330 errx(1, "gw \"%s\" addition failed", gw); 331 } 332 } 333} 334 335static int 336handle_net(jsmntok_t *t, int left, char *data) 337{ 338 const char *ifname, *cloner, *type, *method; 339 const char *addr, *mask, *gw; 340 jsmntok_t *key, *value; 341 int i, objsize; 342 int rv; 343 static int configured; 344 345 T_CHECKTYPE(t, data, JSMN_OBJECT, __func__); 346 347 /* we expect straight key-value pairs (at least for now) */ 348 objsize = t->size; 349 if (left < 2*objsize + 1) { 350 return -1; 351 } 352 t++; 353 354 if (configured) { 355 errx(1, "currently only 1 \"net\" configuration is supported"); 356 } 357 358 ifname = cloner = type = method = NULL; 359 addr = mask = gw = NULL; 360 361 for (i = 0; i < objsize; i++, t+=2) { 362 const char *valuestr; 363 key = t; 364 value = t+1; 365 366 T_CHECKTYPE(key, data, JSMN_STRING, __func__); 367 T_CHECKSIZE(key, data, 1, __func__); 368 369 T_CHECKTYPE(value, data, JSMN_STRING, __func__); 370 T_CHECKSIZE(value, data, 0, __func__); 371 372 /* 373 * XXX: this mimics the structure from Xen. We probably 374 * want a richer structure, but let's be happy to not 375 * diverge for now. 376 */ 377 valuestr = token2cstr(value, data); 378 if (T_STREQ(key, data, "if")) { 379 ifname = valuestr; 380 } else if (T_STREQ(key, data, "cloner")) { 381 cloner = valuestr; 382 } else if (T_STREQ(key, data, "type")) { 383 type = valuestr; 384 } else if (T_STREQ(key, data, "method")) { 385 method = valuestr; 386 } else if (T_STREQ(key, data, "addr")) { 387 addr = valuestr; 388 } else if (T_STREQ(key, data, "mask")) { 389 /* XXX: we could also pass mask as a number ... */ 390 mask = valuestr; 391 } else if (T_STREQ(key, data, "gw")) { 392 gw = valuestr; 393 } else { 394 errx(1, "unexpected key \"%.*s\" in \"%s\"", 395 T_PRINTFSTAR(key, data), __func__); 396 } 397 } 398 399 if (!ifname || !type || !method) { 400 errx(1, "net cfg missing vital data, not configuring"); 401 } 402 403 if (cloner) { 404 if ((rv = rump_pub_netconfig_ifcreate(ifname)) != 0) { 405 errx(1, "rumprun_config: ifcreate %s failed: %d", 406 ifname, rv); 407 } 408 } 409 410 if (strcmp(type, "inet") == 0) { 411 config_ipv4(ifname, method, addr, mask, gw); 412 } else if (strcmp(type, "inet6") == 0) { 413 config_ipv6(ifname, method, addr, mask, gw); 414 } else { 415 errx(1, "network type \"%s\" not supported", type); 416 } 417 418 return 2*objsize + 1; 419} 420 421static void 422makevnddev(int israw, int unit, int part, char *storage, size_t storagesize) 423{ 424 425 snprintf(storage, storagesize, "/dev/%svnd%d%c", 426 israw ? "r" : "", unit, 'a' + part); 427} 428 429static devmajor_t 430getvndmajor(int israw) 431{ 432 struct stat sb; 433 char path[32]; 434 435 makevnddev(israw, 0, RAW_PART, path, sizeof(path)); 436 if (stat(path, &sb) == -1) 437 err(1, "failed to stat %s", path); 438 return major(sb.st_rdev); 439} 440 441static char * 442configvnd(const char *path) 443{ 444 static int nextvnd; 445 struct vnd_ioctl vndio; 446 char bbuf[32], rbuf[32]; 447 int fd; 448 449 makevnddev(0, nextvnd, RAW_PART, bbuf, sizeof(bbuf)); 450 makevnddev(1, nextvnd, RAW_PART, rbuf, sizeof(rbuf)); 451 452 memset(&vndio, 0, sizeof(vndio)); 453 vndio.vnd_file = __UNCONST(path); 454 vndio.vnd_flags = VNDIOF_READONLY; 455 456 fd = open(rbuf, O_RDWR); 457 if (fd == -1) { 458 /* 459 * node doesn't exist? try creating it. use majors from 460 * vnd0, which we (obviously) assume/hope exists 461 */ 462 if (errno == ENOENT) { 463 const devmajor_t bmaj = getvndmajor(0); 464 const devmajor_t rmaj = getvndmajor(1); 465 466 if (mknod(bbuf, 0666 | S_IFBLK, 467 MAKEDISKDEV(bmaj, nextvnd, RAW_PART)) == -1) 468 err(1, "mknod %s", bbuf); 469 if (mknod(rbuf, 0666 | S_IFBLK, 470 MAKEDISKDEV(rmaj, nextvnd, RAW_PART)) == -1) 471 err(1, "mknod %s", rbuf); 472 473 fd = open(rbuf, O_RDWR); 474 } 475 if (fd == -1) 476 err(1, "cannot open %s", rbuf); 477 } 478 479 if (ioctl(fd, VNDIOCSET, &vndio) == -1) 480 err(1, "vndset failed"); 481 close(fd); 482 483 nextvnd++; 484 return strdup(bbuf); 485} 486 487static char * 488configetfs(const char *path, int hard) 489{ 490 char buf[32]; 491 char epath[32]; 492 char *p; 493 int rv; 494 495 snprintf(epath, sizeof(epath), "XENBLK_%s", path); 496 snprintf(buf, sizeof(buf), "/dev/%s", path); 497 rv = rump_pub_etfs_register(buf, epath, RUMP_ETFS_BLK); 498 if (rv != 0) { 499 if (!hard) 500 return NULL; 501 errx(1, "etfs register for \"%s\" failed: %d", path, rv); 502 } 503 504 if ((p = strdup(buf)) == NULL) 505 err(1, "failed to allocate pathbuf"); 506 return p; 507} 508 509static bool 510mount_blk(const char *dev, const char *mp) 511{ 512 struct ufs_args mntargs_ufs = { .fspec = __UNCONST(dev) }; 513 struct iso_args mntargs_iso = { .fspec = dev }; 514 515 if (mount(MOUNT_FFS, mp, 0, &mntargs_ufs, sizeof(mntargs_ufs)) == 0) 516 return true; 517 if (mount(MOUNT_EXT2FS, mp, 0, &mntargs_ufs, sizeof(mntargs_ufs)) == 0) 518 return true; 519 if (mount(MOUNT_CD9660, 520 mp, MNT_RDONLY, &mntargs_iso, sizeof(mntargs_iso)) == 0) 521 return true; 522 523 return false; 524} 525 526static bool 527mount_kernfs(const char *dev, const char *mp) 528{ 529 530 if (mount(MOUNT_KERNFS, mp, 0, NULL, 0) == 0) 531 return true; 532 533 return false; 534} 535 536struct { 537 const char *mt_fstype; 538 bool (*mt_mount)(const char *, const char *); 539} mounters[] = { 540 { "blk", mount_blk }, 541 { "kernfs", mount_kernfs }, 542}; 543 544static int 545handle_blk(jsmntok_t *t, int left, char *data) 546{ 547 const char *source, *origpath, *fstype; 548 char *mp, *path; 549 jsmntok_t *key, *value; 550 int i, objsize; 551 552 T_CHECKTYPE(t, data, JSMN_OBJECT, __func__); 553 554 /* we expect straight key-value pairs */ 555 objsize = t->size; 556 if (left < 2*objsize + 1) { 557 return -1; 558 } 559 t++; 560 561 fstype = source = origpath = mp = path = NULL; 562 563 for (i = 0; i < objsize; i++, t+=2) { 564 char *valuestr; 565 key = t; 566 value = t+1; 567 568 T_CHECKTYPE(key, data, JSMN_STRING, __func__); 569 T_CHECKSIZE(key, data, 1, __func__); 570 571 T_CHECKTYPE(value, data, JSMN_STRING, __func__); 572 T_CHECKSIZE(value, data, 0, __func__); 573 574 valuestr = token2cstr(value, data); 575 if (T_STREQ(key, data, "source")) { 576 source = valuestr; 577 } else if (T_STREQ(key, data, "path")) { 578 origpath = path = valuestr; 579 } else if (T_STREQ(key, data, "fstype")) { 580 fstype = valuestr; 581 } else if (T_STREQ(key, data, "mountpoint")) { 582 mp = valuestr; 583 } else { 584 errx(1, "unexpected key \"%.*s\" in \"%s\"", 585 T_PRINTFSTAR(key, data), __func__); 586 } 587 } 588 589 if (!source || !path) { 590 errx(1, "blk cfg missing vital data"); 591 } 592 593 if (strcmp(source, "dev") == 0) { 594 /* nothing to do here */ 595 } else if (strcmp(source, "vnd") == 0) { 596 path = configvnd(path); 597 } else if (strcmp(source, "etfs") == 0) { 598 path = configetfs(path, 1); 599 } else { 600 errx(1, "unsupported blk source \"%s\"", source); 601 } 602 603 /* we only need to do something only if a mountpoint is specified */ 604 if (mp) { 605 char *chunk; 606 unsigned mi; 607 608 if (!fstype) { 609 errx(1, "no fstype for mountpoint \"%s\"\n", mp); 610 } 611 612 for (chunk = mp;;) { 613 bool end; 614 615 /* find & terminate the next chunk */ 616 chunk += strspn(chunk, "/"); 617 chunk += strcspn(chunk, "/"); 618 end = (*chunk == '\0'); 619 *chunk = '\0'; 620 621 if (mkdir(mp, 0755) == -1) { 622 if (errno != EEXIST) 623 err(1, "failed to create mp dir \"%s\"", 624 chunk); 625 } 626 627 /* restore path */ 628 if (!end) 629 *chunk = '/'; 630 else 631 break; 632 } 633 634 for (mi = 0; mi < __arraycount(mounters); mi++) { 635 if (strcmp(fstype, mounters[mi].mt_fstype) == 0) { 636 if (!mounters[mi].mt_mount(path, mp)) 637 errx(1, "failed to mount fs type " 638 "\"%s\" from \"%s\" to \"%s\"", 639 fstype, path, mp); 640 break; 641 } 642 } 643 if (mi == __arraycount(mounters)) 644 errx(1, "unknown fstype \"%s\"", fstype); 645 } 646 647 if (path != origpath) 648 free(path); 649 650 return 2*objsize + 1; 651} 652 653struct { 654 const char *name; 655 int (*handler)(jsmntok_t *, int, char *); 656} parsers[] = { 657 { "cmdline", handle_cmdline }, 658 { "rc_TESTING", handle_rc }, 659 { "env", handle_env }, 660 { "hostname", handle_hostname }, 661 { "blk", handle_blk }, 662 { "net", handle_net }, 663}; 664 665/* don't believe we can have a >64k config */ 666#define CFGMAXSIZE (64*1024) 667static char * 668getcmdlinefromroot(const char *cfgname) 669{ 670 const char *tryroot[] = { 671 "/dev/ld0a", 672 "/dev/sd0a", 673 }; 674 struct stat sb; 675 unsigned int i; 676 int fd; 677 char *p; 678 679 if (mkdir("/rootfs", 0777) == -1) 680 err(1, "mkdir /rootfs failed"); 681 682 /* 683 * XXX: should not be hardcoded to cd9660. but it is for now. 684 * Maybe use mountroot() here somehow? 685 */ 686 for (i = 0; i < __arraycount(tryroot); i++) { 687 if (mount_blk(tryroot[i], "/rootfs")) 688 break; 689 } 690 691 /* didn't find it that way. one more try: etfs for sda1 (EC2) */ 692 if (i == __arraycount(tryroot)) { 693 char *devpath; 694 695 devpath = configetfs("sda1", 0); 696 if (!devpath) 697 errx(1, "failed to mount rootfs from image"); 698 699 if (!mount_blk(devpath, "/rootfs")) 700 errx(1, "failed to mount /rootfs"); 701 } 702 703 /* 704 * Ok, we've successfully mounted /rootfs. Now get the config. 705 */ 706 707 while (*cfgname == '/') 708 cfgname++; 709 if (chdir("/rootfs") == -1) 710 err(1, "chdir rootfs"); 711 712 if ((fd = open(cfgname, O_RDONLY)) == -1) 713 err(1, "open %s", cfgname); 714 if (stat(cfgname, &sb) == -1) 715 err(1, "stat %s", cfgname); 716 717 if (sb.st_size > CFGMAXSIZE) 718 errx(1, "unbelievable cfg file size, increase CFGMAXSIZE"); 719 if ((p = malloc(sb.st_size+1)) == NULL) 720 err(1, "cfgname storage"); 721 722 if (read(fd, p, sb.st_size) != sb.st_size) 723 err(1, "read cfgfile"); 724 close(fd); 725 726 p[sb.st_size] = '\0'; 727 return p; 728} 729 730 731#define ROOTCFG "_RUMPRUN_ROOTFSCFG=" 732static const size_t rootcfglen = sizeof(ROOTCFG)-1; 733static char * 734rumprun_config_path(char *cmdline) 735{ 736 char *cfg = strstr(cmdline, ROOTCFG); 737 738 if (cfg != NULL) 739 cfg += rootcfglen; 740 741 return cfg; 742} 743#undef ROOTCFG 744 745void 746rumprun_config(char *cmdline) 747{ 748 char *cfg; 749 struct rumprun_exec *rre; 750 jsmn_parser p; 751 jsmntok_t *tokens = NULL; 752 jsmntok_t *t; 753 size_t cmdline_len; 754 unsigned int i; 755 int ntok; 756 757 /* is the config file on rootfs? if so, mount & dig it out */ 758 cfg = rumprun_config_path(cmdline); 759 if (cfg != NULL) { 760 cmdline = getcmdlinefromroot(cfg); 761 if (cmdline == NULL) 762 errx(1, "could not get cfg from rootfs"); 763 } 764 765 while (*cmdline != '{') { 766 if (*cmdline == '\0') { 767 warnx("could not find start of json. no config?"); 768 makeargv(strdup("rumprun")); 769 return; 770 } 771 cmdline++; 772 } 773 774 cmdline_len = strlen(cmdline); 775 jsmn_init(&p); 776 ntok = jsmn_parse(&p, cmdline, cmdline_len, NULL, 0); 777 778 if (ntok <= 0) { 779 errx(1, "json parse failed 1"); 780 } 781 782 tokens = malloc(ntok * sizeof(*t)); 783 if (!tokens) { 784 errx(1, "failed to allocate jsmn tokens"); 785 } 786 787 jsmn_init(&p); 788 if ((ntok = jsmn_parse(&p, cmdline, cmdline_len, tokens, ntok)) < 1) { 789 errx(1, "json parse failed 2"); 790 } 791 792 T_CHECKTYPE(tokens, cmdline, JSMN_OBJECT, __func__); 793 794 for (t = &tokens[0]; t < &tokens[ntok]; ) { 795 /* allow multiple levels of object nesting */ 796 if (t->type == JSMN_OBJECT) { 797 t++; 798 continue; 799 } 800 801 T_CHECKTYPE(t, cmdline, JSMN_STRING, __func__); 802 for (i = 0; i < __arraycount(parsers); i++) { 803 if (T_STREQ(t, cmdline, parsers[i].name)) { 804 int left; 805 806 t++; 807 left = &tokens[ntok] - t; 808 t += parsers[i].handler(t, left, cmdline); 809 break; 810 } 811 } 812 if (i == __arraycount(parsers)) 813 errx(1, "no match for key \"%.*s\"", 814 T_PRINTFSTAR(t, cmdline)); 815 } 816 817 /* 818 * Before we start running things, perform some sanity checks 819 */ 820 rre = TAILQ_LAST(&rumprun_execs, rumprun_execs); 821 if (rre == NULL) { 822 errx(1, "rumprun_config: no bins"); 823 } 824 if (rre->rre_flags & RUMPRUN_EXEC_PIPE) { 825 errx(1, "rumprun_config: last bin may not output to pipe"); 826 } 827 828 free(tokens); 829} 830