1 /* 2 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2.1 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #ifndef _GNU_SOURCE 15 #define _GNU_SOURCE 16 #endif 17 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <getopt.h> 21 #include <glob.h> 22 #include <stdlib.h> 23 #include <stdbool.h> 24 #include <stdio.h> 25 #include <signal.h> 26 #include <termios.h> 27 #include <unistd.h> 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 31 #include <libubus.h> 32 #include <libubox/avl-cmp.h> 33 #include <libubox/blobmsg.h> 34 #include <libubox/blobmsg_json.h> 35 #include <libubox/ustream.h> 36 37 #include "log.h" 38 39 #define UXC_VERSION "0.3" 40 #define OCI_VERSION_STRING "1.0.2" 41 #define UXC_ETC_CONFDIR "/etc/uxc" 42 #define UXC_VOL_CONFDIR "/tmp/run/uvol/.meta/uxc" 43 44 static bool verbose = false; 45 static bool json_output = false; 46 static char *confdir = UXC_ETC_CONFDIR; 47 static struct ustream_fd cufd; 48 static struct ustream_fd lufd; 49 50 51 struct runtime_state { 52 struct avl_node avl; 53 char *container_name; 54 char *instance_name; 55 char *jail_name; 56 bool running; 57 int runtime_pid; 58 int exitcode; 59 struct blob_attr *ocistate; 60 }; 61 62 struct settings { 63 struct avl_node avl; 64 char *container_name; 65 const char *fname; 66 char *tmprwsize; 67 char *writepath; 68 signed char autostart; 69 struct blob_attr *volumes; 70 }; 71 72 enum uxc_cmd { 73 CMD_ATTACH, 74 CMD_LIST, 75 CMD_BOOT, 76 CMD_START, 77 CMD_STATE, 78 CMD_KILL, 79 CMD_ENABLE, 80 CMD_DISABLE, 81 CMD_DELETE, 82 CMD_CREATE, 83 CMD_UNKNOWN 84 }; 85 86 #define OPT_ARGS "ab:fjm:p:t:vVw:" 87 static struct option long_options[] = { 88 {"autostart", no_argument, 0, 'a' }, 89 {"console", no_argument, 0, 'c' }, 90 {"bundle", required_argument, 0, 'b' }, 91 {"force", no_argument, 0, 'f' }, 92 {"json", no_argument, 0, 'j' }, 93 {"mounts", required_argument, 0, 'm' }, 94 {"pid-file", required_argument, 0, 'p' }, 95 {"temp-overlay-size", required_argument, 0, 't' }, 96 {"write-overlay-path", required_argument, 0, 'w' }, 97 {"verbose", no_argument, 0, 'v' }, 98 {"version", no_argument, 0, 'V' }, 99 {0, 0, 0, 0 } 100 }; 101 102 AVL_TREE(runtime, avl_strcmp, false, NULL); 103 AVL_TREE(settings, avl_strcmp, false, NULL); 104 static struct blob_buf conf; 105 static struct blob_buf settingsbuf; 106 static struct blob_attr *blockinfo; 107 static struct blob_attr *fstabinfo; 108 static struct ubus_context *ctx; 109 110 static int usage(void) { 111 printf("syntax: uxc <command> [parameters ...]\n"); 112 printf("commands:\n"); 113 printf("\tlist [--json]\t\t\t\tlist all configured containers\n"); 114 printf("\tattach <conf>\t\t\t\tattach to container console\n"); 115 printf("\tcreate <conf>\t\t\t\t(re-)create <conf>\n"); 116 printf("\t\t[--bundle <path>]\t\t\tOCI bundle at <path>\n"); 117 printf("\t\t[--autostart]\t\t\t\tstart on boot\n"); 118 printf("\t\t[--temp-overlay-size <size>]\t\tuse tmpfs overlay with {size}\n"); 119 printf("\t\t[--write-overlay-path <path>]\t\tuse overlay on {path}\n"); 120 printf("\t\t[--mounts <v1>,<v2>,...,<vN>]\t\trequire filesystems to be available\n"); 121 printf("\tstart [--console] <conf>\t\tstart container <conf>\n"); 122 printf("\tstate <conf>\t\t\t\tget state of container <conf>\n"); 123 printf("\tkill <conf> [<signal>]\t\t\tsend signal to container <conf>\n"); 124 printf("\tenable <conf>\t\t\t\tstart container <conf> on boot\n"); 125 printf("\tdisable <conf>\t\t\t\tdon't start container <conf> on boot\n"); 126 printf("\tdelete <conf> [--force]\t\t\tdelete <conf>\n"); 127 return -EINVAL; 128 } 129 130 enum { 131 CONF_NAME, 132 CONF_PATH, 133 CONF_JAIL, 134 CONF_AUTOSTART, 135 CONF_PIDFILE, 136 CONF_TEMP_OVERLAY_SIZE, 137 CONF_WRITE_OVERLAY_PATH, 138 CONF_VOLUMES, 139 __CONF_MAX, 140 }; 141 142 static const struct blobmsg_policy conf_policy[__CONF_MAX] = { 143 [CONF_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 144 [CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, 145 [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING }, 146 [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL }, 147 [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING }, 148 [CONF_TEMP_OVERLAY_SIZE] = { .name = "temp-overlay-size", .type = BLOBMSG_TYPE_STRING }, 149 [CONF_WRITE_OVERLAY_PATH] = { .name = "write-overlay-path", .type = BLOBMSG_TYPE_STRING }, 150 [CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY }, 151 }; 152 153 static int conf_load(bool load_settings) 154 { 155 int gl_flags = GLOB_NOESCAPE | GLOB_MARK; 156 int j, res; 157 glob_t gl; 158 char *globstr; 159 void *c, *o; 160 struct stat sb; 161 struct blob_buf *target; 162 163 if (asprintf(&globstr, "%s/%s*.json", UXC_ETC_CONFDIR, load_settings?"settings/":"") == -1) 164 return -ENOMEM; 165 166 res = glob(globstr, gl_flags, NULL, &gl); 167 if (res == 0) 168 gl_flags |= GLOB_APPEND; 169 170 free(globstr); 171 172 if (!stat(UXC_VOL_CONFDIR, &sb)) { 173 if (sb.st_mode & S_IFDIR) { 174 if (asprintf(&globstr, "%s/%s*.json", UXC_VOL_CONFDIR, load_settings?"settings/":"") == -1) 175 return -ENOMEM; 176 177 res = glob(globstr, gl_flags, NULL, &gl); 178 free(globstr); 179 } 180 } 181 182 target = load_settings ? &settingsbuf : &conf; 183 blob_buf_init(target, 0); 184 c = blobmsg_open_table(target, NULL); 185 186 if (res < 0) 187 return 0; 188 189 for (j = 0; j < gl.gl_pathc; j++) { 190 o = blobmsg_open_table(target, strdup(gl.gl_pathv[j])); 191 if (!blobmsg_add_json_from_file(target, gl.gl_pathv[j])) { 192 ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]); 193 continue; 194 } 195 blobmsg_close_table(target, o); 196 } 197 blobmsg_close_table(target, c); 198 globfree(&gl); 199 200 return 0; 201 } 202 203 static struct settings * 204 settings_alloc(const char *container_name) 205 { 206 struct settings *s; 207 char *new_name; 208 s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1); 209 strcpy(new_name, container_name); 210 s->container_name = new_name; 211 s->avl.key = s->container_name; 212 s->autostart = -1; 213 s->tmprwsize = NULL; 214 s->writepath = NULL; 215 s->volumes = NULL; 216 return s; 217 } 218 219 static int settings_add(void) 220 { 221 struct blob_attr *cur, *tb[__CONF_MAX]; 222 struct settings *s; 223 int rem, err; 224 225 avl_init(&settings, avl_strcmp, false, NULL); 226 227 blobmsg_for_each_attr(cur, blob_data(settingsbuf.head), rem) { 228 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 229 if (!tb[CONF_NAME]) 230 continue; 231 232 if (tb[CONF_TEMP_OVERLAY_SIZE] && tb[CONF_WRITE_OVERLAY_PATH]) 233 return -EINVAL; 234 235 s = settings_alloc(blobmsg_get_string(tb[CONF_NAME])); 236 237 if (tb[CONF_AUTOSTART]) 238 s->autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]); 239 240 if (tb[CONF_TEMP_OVERLAY_SIZE]) 241 s->tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]); 242 243 if (tb[CONF_WRITE_OVERLAY_PATH]) 244 s->writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]); 245 246 s->volumes = tb[CONF_VOLUMES]; 247 s->fname = blobmsg_name(cur); 248 249 err = avl_insert(&settings, &s->avl); 250 if (err) { 251 fprintf(stderr, "error adding settings for %s\n", blobmsg_get_string(tb[CONF_NAME])); 252 free(s); 253 } 254 } 255 256 return 0; 257 } 258 259 static void settings_free(void) 260 { 261 struct settings *item, *tmp; 262 263 avl_for_each_element_safe(&settings, item, avl, tmp) { 264 avl_delete(&settings, &item->avl); 265 free(item); 266 } 267 268 return; 269 } 270 271 enum { 272 LIST_INSTANCES, 273 __LIST_MAX, 274 }; 275 276 static const struct blobmsg_policy list_policy[__LIST_MAX] = { 277 [LIST_INSTANCES] = { .name = "instances", .type = BLOBMSG_TYPE_TABLE }, 278 }; 279 280 enum { 281 INSTANCE_RUNNING, 282 INSTANCE_PID, 283 INSTANCE_EXITCODE, 284 INSTANCE_JAIL, 285 __INSTANCE_MAX, 286 }; 287 288 static const struct blobmsg_policy instance_policy[__INSTANCE_MAX] = { 289 [INSTANCE_RUNNING] = { .name = "running", .type = BLOBMSG_TYPE_BOOL }, 290 [INSTANCE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 }, 291 [INSTANCE_EXITCODE] = { .name = "exit_code", .type = BLOBMSG_TYPE_INT32 }, 292 [INSTANCE_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_TABLE }, 293 }; 294 295 enum { 296 JAIL_NAME, 297 __JAIL_MAX, 298 }; 299 300 static const struct blobmsg_policy jail_policy[__JAIL_MAX] = { 301 [JAIL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 302 }; 303 304 static struct runtime_state * 305 runtime_alloc(const char *container_name) 306 { 307 struct runtime_state *s; 308 char *new_name; 309 s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1); 310 strcpy(new_name, container_name); 311 s->container_name = new_name; 312 s->avl.key = s->container_name; 313 return s; 314 } 315 316 enum { 317 STATE_OCIVERSION, 318 STATE_ID, 319 STATE_STATUS, 320 STATE_PID, 321 STATE_BUNDLE, 322 STATE_ANNOTATIONS, 323 __STATE_MAX, 324 }; 325 326 static const struct blobmsg_policy state_policy[__STATE_MAX] = { 327 [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING }, 328 [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, 329 [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING }, 330 [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 }, 331 [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING }, 332 [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE }, 333 }; 334 335 336 static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg) 337 { 338 struct blob_attr **ocistate = (struct blob_attr **)req->priv; 339 struct blob_attr *tb[__STATE_MAX]; 340 341 blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 342 343 if (!tb[STATE_OCIVERSION] || 344 !tb[STATE_ID] || 345 !tb[STATE_STATUS] || 346 !tb[STATE_BUNDLE]) 347 return; 348 349 *ocistate = blob_memdup(msg); 350 } 351 352 static void get_ocistate(struct blob_attr **ocistate, const char *name) 353 { 354 char *objname; 355 unsigned int id; 356 int ret; 357 *ocistate = NULL; 358 359 if (asprintf(&objname, "container.%s", name) == -1) 360 exit(ENOMEM); 361 362 ret = ubus_lookup_id(ctx, objname, &id); 363 free(objname); 364 if (ret) 365 return; 366 367 ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000); 368 } 369 370 static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg) 371 { 372 struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX]; 373 int rem, remi; 374 const char *container_name, *instance_name, *jail_name; 375 bool running; 376 int pid, exitcode; 377 struct runtime_state *rs; 378 379 blobmsg_for_each_attr(cur, msg, rem) { 380 container_name = blobmsg_name(cur); 381 blobmsg_parse(list_policy, __LIST_MAX, tl, blobmsg_data(cur), blobmsg_len(cur)); 382 if (!tl[LIST_INSTANCES]) 383 continue; 384 385 blobmsg_for_each_attr(curi, tl[LIST_INSTANCES], remi) { 386 instance_name = blobmsg_name(curi); 387 blobmsg_parse(instance_policy, __INSTANCE_MAX, ti, blobmsg_data(curi), blobmsg_len(curi)); 388 389 if (!ti[INSTANCE_JAIL]) 390 continue; 391 392 blobmsg_parse(jail_policy, __JAIL_MAX, tj, blobmsg_data(ti[INSTANCE_JAIL]), blobmsg_len(ti[INSTANCE_JAIL])); 393 if (!tj[JAIL_NAME]) 394 continue; 395 396 jail_name = blobmsg_get_string(tj[JAIL_NAME]); 397 398 running = ti[INSTANCE_RUNNING] && blobmsg_get_bool(ti[INSTANCE_RUNNING]); 399 400 if (ti[INSTANCE_PID]) 401 pid = blobmsg_get_u32(ti[INSTANCE_PID]); 402 else 403 pid = -1; 404 405 if (ti[INSTANCE_EXITCODE]) 406 exitcode = blobmsg_get_u32(ti[INSTANCE_EXITCODE]); 407 else 408 exitcode = -1; 409 410 rs = runtime_alloc(container_name); 411 rs->instance_name = strdup(instance_name); 412 rs->jail_name = strdup(jail_name); 413 rs->runtime_pid = pid; 414 rs->exitcode = exitcode; 415 rs->running = running; 416 avl_insert(&runtime, &rs->avl); 417 } 418 } 419 420 return; 421 } 422 423 static int runtime_load(void) 424 { 425 struct runtime_state *item, *tmp; 426 uint32_t id; 427 428 avl_init(&runtime, avl_strcmp, false, NULL); 429 if (ubus_lookup_id(ctx, "container", &id) || 430 ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000)) 431 return -EIO; 432 433 avl_for_each_element_safe(&runtime, item, avl, tmp) 434 get_ocistate(&item->ocistate, item->jail_name); 435 436 return 0; 437 } 438 439 static void runtime_free(void) 440 { 441 struct runtime_state *item, *tmp; 442 443 avl_for_each_element_safe(&runtime, item, avl, tmp) { 444 avl_delete(&runtime, &item->avl); 445 free(item->instance_name); 446 free(item->jail_name); 447 free(item->ocistate); 448 free(item); 449 } 450 451 return; 452 } 453 454 static inline int setup_tios(int fd, struct termios *oldtios) 455 { 456 struct termios newtios; 457 458 if (!isatty(fd)) { 459 return -EIO; 460 } 461 462 /* Get current termios */ 463 if (tcgetattr(fd, oldtios) < 0) 464 return -errno; 465 466 newtios = *oldtios; 467 468 /* We use the same settings that ssh does. */ 469 newtios.c_iflag |= IGNPAR; 470 newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 471 newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 472 newtios.c_oflag &= ~ONLCR; 473 newtios.c_oflag |= OPOST; 474 newtios.c_cc[VMIN] = 1; 475 newtios.c_cc[VTIME] = 0; 476 477 /* Set new attributes */ 478 if (tcsetattr(fd, TCSAFLUSH, &newtios) < 0) 479 return -errno; 480 481 return 0; 482 } 483 484 485 static void client_cb(struct ustream *s, int bytes) 486 { 487 char *buf; 488 int len, rv; 489 490 do { 491 buf = ustream_get_read_buf(s, &len); 492 if (!buf) 493 break; 494 495 rv = ustream_write(&lufd.stream, buf, len, false); 496 497 if (rv > 0) 498 ustream_consume(s, rv); 499 500 if (rv <= len) 501 break; 502 } while(1); 503 } 504 505 static void local_cb(struct ustream *s, int bytes) 506 { 507 char *buf; 508 int len, rv; 509 510 do { 511 buf = ustream_get_read_buf(s, &len); 512 if (!buf) 513 break; 514 515 if ((len > 0) && (buf[0] == 2)) 516 uloop_end(); 517 518 rv = ustream_write(&cufd.stream, buf, len, false); 519 520 if (rv > 0) 521 ustream_consume(s, rv); 522 523 if (rv <= len) 524 break; 525 } while(1); 526 } 527 528 static int uxc_attach(const char *container_name) 529 { 530 struct ubus_context *ctx; 531 uint32_t id; 532 static struct blob_buf req; 533 int client_fd, server_fd, tty_fd; 534 struct termios oldtermios; 535 536 ctx = ubus_connect(NULL); 537 if (!ctx) { 538 fprintf(stderr, "can't connect to ubus!\n"); 539 return -ECONNREFUSED; 540 } 541 542 /* open pseudo-terminal pair */ 543 client_fd = posix_openpt(O_RDWR | O_NOCTTY); 544 if (client_fd < 0) { 545 fprintf(stderr, "can't create virtual console!\n"); 546 ubus_free(ctx); 547 return -EIO; 548 } 549 setup_tios(client_fd, &oldtermios); 550 grantpt(client_fd); 551 unlockpt(client_fd); 552 server_fd = open(ptsname(client_fd), O_RDWR | O_NOCTTY); 553 if (server_fd < 0) { 554 fprintf(stderr, "can't open virtual console!\n"); 555 close(client_fd); 556 ubus_free(ctx); 557 return -EIO; 558 } 559 setup_tios(server_fd, &oldtermios); 560 561 tty_fd = open("/dev/tty", O_RDWR); 562 if (tty_fd < 0) { 563 fprintf(stderr, "can't open local console!\n"); 564 close(server_fd); 565 close(client_fd); 566 ubus_free(ctx); 567 return -EIO; 568 } 569 setup_tios(tty_fd, &oldtermios); 570 571 /* register server-side with procd */ 572 blob_buf_init(&req, 0); 573 blobmsg_add_string(&req, "name", container_name); 574 blobmsg_add_string(&req, "instance", container_name); 575 576 if (ubus_lookup_id(ctx, "container", &id) || 577 ubus_invoke_fd(ctx, id, "console_attach", req.head, NULL, NULL, 3000, server_fd)) { 578 fprintf(stderr, "ubus request failed\n"); 579 close(tty_fd); 580 close(server_fd); 581 close(client_fd); 582 blob_buf_free(&req); 583 ubus_free(ctx); 584 return -ENXIO; 585 } 586 587 close(server_fd); 588 blob_buf_free(&req); 589 ubus_free(ctx); 590 591 uloop_init(); 592 593 /* forward between stdio and client_fd until detach is requested */ 594 lufd.stream.notify_read = local_cb; 595 ustream_fd_init(&lufd, tty_fd); 596 597 cufd.stream.notify_read = client_cb; 598 /* ToDo: handle remote close and other events */ 599 // cufd.stream.notify_state = client_state_cb; 600 ustream_fd_init(&cufd, client_fd); 601 602 fprintf(stderr, "attaching to jail console. press [CTRL]+[B] to exit.\n"); 603 close(0); 604 close(1); 605 close(2); 606 uloop_run(); 607 608 tcsetattr(tty_fd, TCSAFLUSH, &oldtermios); 609 ustream_free(&lufd.stream); 610 ustream_free(&cufd.stream); 611 close(client_fd); 612 613 return 0; 614 } 615 616 static int uxc_state(char *name) 617 { 618 struct runtime_state *rsstate = avl_find_element(&runtime, name, rsstate, avl); 619 struct blob_attr *ocistate = NULL; 620 struct blob_attr *cur, *tb[__CONF_MAX]; 621 int rem; 622 char *bundle = NULL; 623 char *jail_name = NULL; 624 char *state = NULL; 625 char *tmp; 626 static struct blob_buf buf; 627 628 if (rsstate) 629 ocistate = rsstate->ocistate; 630 631 if (ocistate) { 632 state = blobmsg_format_json_indent(ocistate, true, 0); 633 if (!state) 634 return -ENOMEM; 635 636 printf("%s\n", state); 637 free(state); 638 return 0; 639 } 640 641 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 642 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 643 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 644 continue; 645 646 if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) { 647 if (tb[CONF_JAIL]) 648 jail_name = blobmsg_get_string(tb[CONF_JAIL]); 649 else 650 jail_name = name; 651 652 bundle = blobmsg_get_string(tb[CONF_PATH]); 653 break; 654 } 655 } 656 657 if (!bundle) 658 return -ENOENT; 659 660 blob_buf_init(&buf, 0); 661 blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING); 662 blobmsg_add_string(&buf, "id", jail_name); 663 blobmsg_add_string(&buf, "status", rsstate?"stopped":"uninitialized"); 664 blobmsg_add_string(&buf, "bundle", bundle); 665 666 tmp = blobmsg_format_json_indent(buf.head, true, 0); 667 if (!tmp) { 668 blob_buf_free(&buf); 669 return -ENOMEM; 670 } 671 672 printf("%s\n", tmp); 673 free(tmp); 674 675 blob_buf_free(&buf); 676 677 return 0; 678 } 679 680 static int uxc_list(void) 681 { 682 struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX]; 683 int rem; 684 struct runtime_state *rsstate = NULL; 685 struct settings *usettings = NULL; 686 char *name, *ocistatus, *status, *tmp; 687 int container_pid = -1; 688 bool autostart; 689 static struct blob_buf buf; 690 void *arr, *obj; 691 692 if (json_output) { 693 blob_buf_init(&buf, 0); 694 arr = blobmsg_open_array(&buf, ""); 695 } 696 697 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 698 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 699 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 700 continue; 701 702 autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]); 703 704 ocistatus = NULL; 705 container_pid = 0; 706 name = blobmsg_get_string(tb[CONF_NAME]); 707 rsstate = avl_find_element(&runtime, name, rsstate, avl); 708 709 if (rsstate && rsstate->ocistate) { 710 blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(rsstate->ocistate), blobmsg_len(rsstate->ocistate)); 711 ocistatus = blobmsg_get_string(ts[STATE_STATUS]); 712 container_pid = blobmsg_get_u32(ts[STATE_PID]); 713 } 714 715 status = ocistatus?:(rsstate && rsstate->running)?"creating":"stopped"; 716 717 usettings = avl_find_element(&settings, name, usettings, avl); 718 719 if (usettings && (usettings->autostart >= 0)) 720 autostart = !!(usettings->autostart); 721 722 if (json_output) { 723 obj = blobmsg_open_table(&buf, ""); 724 blobmsg_add_string(&buf, "name", name); 725 blobmsg_add_string(&buf, "status", status); 726 blobmsg_add_u8(&buf, "autostart", autostart); 727 } else { 728 printf("[%c] %s %s", autostart?'*':' ', name, status); 729 } 730 731 if (rsstate && !rsstate->running && (rsstate->exitcode >= 0)) { 732 if (json_output) 733 blobmsg_add_u32(&buf, "exitcode", rsstate->exitcode); 734 else 735 printf(" exitcode: %d (%s)", rsstate->exitcode, strerror(rsstate->exitcode)); 736 } 737 738 if (rsstate && rsstate->running && (rsstate->runtime_pid >= 0)) { 739 if (json_output) 740 blobmsg_add_u32(&buf, "runtime_pid", rsstate->runtime_pid); 741 else 742 printf(" runtime pid: %d", rsstate->runtime_pid); 743 } 744 745 if (rsstate && rsstate->running && (container_pid >= 0)) { 746 if (json_output) 747 blobmsg_add_u32(&buf, "container_pid", container_pid); 748 else 749 printf(" container pid: %d", container_pid); 750 } 751 752 if (!json_output) 753 printf("\n"); 754 else 755 blobmsg_close_table(&buf, obj); 756 } 757 758 if (json_output) { 759 blobmsg_close_array(&buf, arr); 760 tmp = blobmsg_format_json_indent(buf.head, true, 0); 761 if (!tmp) { 762 blob_buf_free(&buf); 763 return -ENOMEM; 764 } 765 printf("%s\n", tmp); 766 free(tmp); 767 blob_buf_free(&buf); 768 }; 769 770 return 0; 771 } 772 773 static int uxc_exists(char *name) 774 { 775 struct runtime_state *rsstate = NULL; 776 rsstate = avl_find_element(&runtime, name, rsstate, avl); 777 778 if (rsstate && (rsstate->running)) 779 return -EEXIST; 780 781 return 0; 782 } 783 784 static int uxc_create(char *name, bool immediately) 785 { 786 static struct blob_buf req; 787 struct blob_attr *cur, *tb[__CONF_MAX]; 788 int rem, ret = 0; 789 uint32_t id; 790 struct settings *usettings = NULL; 791 char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL; 792 793 void *in, *ins, *j; 794 bool found = false; 795 796 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 797 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 798 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 799 continue; 800 801 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) 802 continue; 803 804 found = true; 805 break; 806 } 807 808 if (!found) 809 return -ENOENT; 810 811 path = blobmsg_get_string(tb[CONF_PATH]); 812 813 if (tb[CONF_PIDFILE]) 814 pidfile = blobmsg_get_string(tb[CONF_PIDFILE]); 815 816 if (tb[CONF_TEMP_OVERLAY_SIZE]) 817 tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]); 818 819 if (tb[CONF_WRITE_OVERLAY_PATH]) 820 writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]); 821 822 if (tb[CONF_JAIL]) 823 jailname = blobmsg_get_string(tb[CONF_JAIL]); 824 825 usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl); 826 if (usettings) { 827 if (usettings->writepath) { 828 writepath = usettings->writepath; 829 tmprwsize = NULL; 830 } 831 if (usettings->tmprwsize) { 832 tmprwsize = usettings->tmprwsize; 833 writepath = NULL; 834 } 835 } 836 837 blob_buf_init(&req, 0); 838 blobmsg_add_string(&req, "name", name); 839 ins = blobmsg_open_table(&req, "instances"); 840 in = blobmsg_open_table(&req, name); 841 blobmsg_add_string(&req, "bundle", path); 842 j = blobmsg_open_table(&req, "jail"); 843 blobmsg_add_string(&req, "name", jailname?:name); 844 blobmsg_add_u8(&req, "immediately", immediately); 845 846 if (pidfile) 847 blobmsg_add_string(&req, "pidfile", pidfile); 848 849 blobmsg_close_table(&req, j); 850 851 if (writepath) 852 blobmsg_add_string(&req, "overlaydir", writepath); 853 854 if (tmprwsize) 855 blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize); 856 857 blobmsg_close_table(&req, in); 858 blobmsg_close_table(&req, ins); 859 860 if (verbose) { 861 char *tmp; 862 tmp = blobmsg_format_json_indent(req.head, true, 1); 863 if (!tmp) 864 return -ENOMEM; 865 866 fprintf(stderr, "adding container to procd:\n\t%s\n", tmp); 867 free(tmp); 868 } 869 870 if (ubus_lookup_id(ctx, "container", &id) || 871 ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) { 872 blob_buf_free(&req); 873 ret = -EIO; 874 } 875 876 return ret; 877 } 878 879 static int uxc_start(const char *name, bool console) 880 { 881 char *objname; 882 unsigned int id; 883 pid_t pid; 884 885 if (console) { 886 pid = fork(); 887 if (pid > 0) 888 exit(uxc_attach(name)); 889 } 890 891 if (asprintf(&objname, "container.%s", name) == -1) 892 return -ENOMEM; 893 894 if (ubus_lookup_id(ctx, objname, &id)) 895 return -ENOENT; 896 897 free(objname); 898 return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000); 899 } 900 901 static int uxc_kill(char *name, int signal) 902 { 903 static struct blob_buf req; 904 struct blob_attr *cur, *tb[__CONF_MAX]; 905 int rem, ret; 906 char *objname; 907 unsigned int id; 908 struct runtime_state *rsstate = NULL; 909 bool found = false; 910 911 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 912 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 913 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 914 continue; 915 916 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) 917 continue; 918 919 found = true; 920 break; 921 } 922 923 if (!found) 924 return -ENOENT; 925 926 rsstate = avl_find_element(&runtime, name, rsstate, avl); 927 928 if (!rsstate || !(rsstate->running)) 929 return -ENOENT; 930 931 blob_buf_init(&req, 0); 932 blobmsg_add_u32(&req, "signal", signal); 933 blobmsg_add_string(&req, "name", name); 934 935 if (asprintf(&objname, "container.%s", name) == -1) 936 return -ENOMEM; 937 938 ret = ubus_lookup_id(ctx, objname, &id); 939 free(objname); 940 if (ret) 941 return -ENOENT; 942 943 if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000)) 944 return -EIO; 945 946 return 0; 947 } 948 949 950 static int uxc_set(char *name, char *path, signed char autostart, char *pidfile, char *tmprwsize, char *writepath, char *requiredmounts) 951 { 952 static struct blob_buf req; 953 struct settings *usettings = NULL; 954 struct blob_attr *cur, *tb[__CONF_MAX]; 955 int rem, ret; 956 const char *cfname = NULL; 957 const char *sfname = NULL; 958 char *fname = NULL; 959 char *curvol, *tmp, *mnttok; 960 void *mntarr; 961 int f; 962 struct stat sb; 963 964 /* nothing to do */ 965 if (!path && (autostart<0) && !pidfile && !tmprwsize && !writepath && !requiredmounts) 966 return 0; 967 968 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 969 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 970 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 971 continue; 972 973 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) 974 continue; 975 976 cfname = blobmsg_name(cur); 977 break; 978 } 979 980 if (cfname && path) 981 return -EEXIST; 982 983 if (!cfname && !path) 984 return -ENOENT; 985 986 if (path) { 987 if (stat(path, &sb) == -1) 988 return -ENOENT; 989 990 if ((sb.st_mode & S_IFMT) != S_IFDIR) 991 return -ENOTDIR; 992 } 993 994 usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl); 995 if (path && usettings) 996 return -EIO; 997 998 if (usettings) { 999 sfname = usettings->fname; 1000 if (!tmprwsize && !writepath) { 1001 if (usettings->tmprwsize) { 1002 tmprwsize = usettings->tmprwsize; 1003 writepath = NULL; 1004 } 1005 if (usettings->writepath) { 1006 writepath = usettings->writepath; 1007 tmprwsize = NULL; 1008 } 1009 } 1010 if (usettings->autostart >= 0 && autostart < 0) 1011 autostart = !!(usettings->autostart); 1012 } 1013 1014 if (path) { 1015 ret = mkdir(confdir, 0755); 1016 1017 if (ret && errno != EEXIST) 1018 return -errno; 1019 1020 if (asprintf(&fname, "%s/%s.json", confdir, name) == -1) 1021 return -ENOMEM; 1022 1023 f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1024 if (f < 0) 1025 return -errno; 1026 1027 free(fname); 1028 } else { 1029 if (sfname) { 1030 f = open(sfname, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1031 } else { 1032 char *t1, *t2; 1033 t1 = strdup(cfname); 1034 t2 = strrchr(t1, '/'); 1035 if (!t2) 1036 return -EINVAL; 1037 1038 *t2 = '\0'; 1039 1040 if (asprintf(&t2, "%s/settings", t1) == -1) 1041 return -ENOMEM; 1042 1043 ret = mkdir(t2, 0755); 1044 if (ret && ret != EEXIST) 1045 return -ret; 1046 1047 free(t2); 1048 if (asprintf(&t2, "%s/settings/%s.json", t1, name) == -1) 1049 return -ENOMEM; 1050 1051 free(t1); 1052 f = open(t2, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1053 free(t2); 1054 } 1055 if (f < 0) 1056 return -errno; 1057 } 1058 1059 blob_buf_init(&req, 0); 1060 blobmsg_add_string(&req, "name", name); 1061 if (path) 1062 blobmsg_add_string(&req, "path", path); 1063 1064 if (autostart >= 0) 1065 blobmsg_add_u8(&req, "autostart", !!autostart); 1066 1067 if (pidfile) 1068 blobmsg_add_string(&req, "pidfile", pidfile); 1069 1070 if (tmprwsize) 1071 blobmsg_add_string(&req, "temp-overlay-size", tmprwsize); 1072 1073 if (writepath) 1074 blobmsg_add_string(&req, "write-overlay-path", writepath); 1075 1076 if (!requiredmounts && usettings && usettings->volumes) 1077 blobmsg_add_blob(&req, usettings->volumes); 1078 1079 if (requiredmounts) { 1080 mntarr = blobmsg_open_array(&req, "volumes"); 1081 for (mnttok = requiredmounts; ; mnttok = NULL) { 1082 curvol = strtok_r(mnttok, ",;", &tmp); 1083 if (!curvol) 1084 break; 1085 1086 blobmsg_add_string(&req, NULL, curvol); 1087 } 1088 blobmsg_close_array(&req, mntarr); 1089 } 1090 1091 tmp = blobmsg_format_json_indent(req.head, true, 0); 1092 if (tmp) { 1093 dprintf(f, "%s\n", tmp); 1094 free(tmp); 1095 } 1096 1097 blob_buf_free(&req); 1098 close(f); 1099 1100 return 1; 1101 } 1102 1103 enum { 1104 BLOCK_INFO_DEVICE, 1105 BLOCK_INFO_UUID, 1106 BLOCK_INFO_TARGET, 1107 BLOCK_INFO_TYPE, 1108 BLOCK_INFO_MOUNT, 1109 __BLOCK_INFO_MAX, 1110 }; 1111 1112 static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = { 1113 [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING }, 1114 [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING }, 1115 [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING }, 1116 [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }, 1117 [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING }, 1118 }; 1119 1120 1121 /* check if device 'devname' is mounted according to blockd */ 1122 static bool checkblock(const char *uuid) 1123 { 1124 struct blob_attr *tb[__BLOCK_INFO_MAX]; 1125 struct blob_attr *cur; 1126 int rem; 1127 1128 blobmsg_for_each_attr(cur, blockinfo, rem) { 1129 blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 1130 1131 if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT]) 1132 continue; 1133 1134 if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID]))) 1135 return false; 1136 } 1137 1138 return true; 1139 } 1140 1141 enum { 1142 UCI_FSTAB_UUID, 1143 UCI_FSTAB_ANONYMOUS, 1144 __UCI_FSTAB_MAX, 1145 }; 1146 1147 static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = { 1148 [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING }, 1149 [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL }, 1150 }; 1151 1152 static const char *resolveuuid(const char *volname) 1153 { 1154 struct blob_attr *tb[__UCI_FSTAB_MAX]; 1155 struct blob_attr *cur; 1156 const char *mntname; 1157 char *tmpvolname, *replc; 1158 int rem, res; 1159 1160 blobmsg_for_each_attr(cur, fstabinfo, rem) { 1161 blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 1162 1163 if (!tb[UCI_FSTAB_UUID]) 1164 continue; 1165 1166 if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS])) 1167 continue; 1168 1169 mntname = blobmsg_name(cur); 1170 if (!mntname) 1171 continue; 1172 1173 tmpvolname = strdup(volname); 1174 while ((replc = strchr(tmpvolname, '-'))) 1175 *replc = '_'; 1176 1177 res = strcmp(tmpvolname, mntname); 1178 free(tmpvolname); 1179 1180 if (!res) 1181 return blobmsg_get_string(tb[UCI_FSTAB_UUID]); 1182 }; 1183 1184 return volname; 1185 }; 1186 1187 /* check status of each required volume */ 1188 static bool checkvolumes(struct blob_attr *volumes) 1189 { 1190 struct blob_attr *cur; 1191 int rem; 1192 1193 blobmsg_for_each_attr(cur, volumes, rem) { 1194 if (checkblock(resolveuuid(blobmsg_get_string(cur)))) 1195 return true; 1196 } 1197 1198 return false; 1199 } 1200 1201 static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1202 { 1203 blockinfo = blob_memdup(blobmsg_data(msg)); 1204 } 1205 1206 static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1207 { 1208 fstabinfo = blob_memdup(blobmsg_data(msg)); 1209 } 1210 1211 static int uxc_boot(void) 1212 { 1213 struct blob_attr *cur, *tb[__CONF_MAX]; 1214 struct runtime_state *rsstate = NULL; 1215 struct settings *usettings = NULL; 1216 static struct blob_buf req; 1217 int rem, ret = 0; 1218 char *name; 1219 unsigned int id; 1220 bool autostart; 1221 1222 ret = ubus_lookup_id(ctx, "block", &id); 1223 if (ret) 1224 return -ENOENT; 1225 1226 ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000); 1227 if (ret) 1228 return -ENXIO; 1229 1230 ret = ubus_lookup_id(ctx, "uci", &id); 1231 if (ret) 1232 return -ENOENT; 1233 1234 blob_buf_init(&req, 0); 1235 blobmsg_add_string(&req, "config", "fstab"); 1236 blobmsg_add_string(&req, "type", "mount"); 1237 1238 ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000); 1239 if (ret) 1240 return ret; 1241 1242 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 1243 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 1244 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 1245 continue; 1246 1247 rsstate = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), rsstate, avl); 1248 if (rsstate) 1249 continue; 1250 1251 if (tb[CONF_AUTOSTART]) 1252 autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]); 1253 1254 usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl); 1255 if (usettings && (usettings->autostart >= 0)) 1256 autostart = !!(usettings->autostart); 1257 1258 if (!autostart) 1259 continue; 1260 1261 /* make sure all volumes are ready before starting */ 1262 if (tb[CONF_VOLUMES]) 1263 if (checkvolumes(tb[CONF_VOLUMES])) 1264 continue; 1265 1266 if (usettings && usettings->volumes) 1267 if (checkvolumes(usettings->volumes)) 1268 continue; 1269 1270 name = strdup(blobmsg_get_string(tb[CONF_NAME])); 1271 if (uxc_exists(name)) 1272 continue; 1273 1274 if (uxc_create(name, true)) 1275 ++ret; 1276 1277 free(name); 1278 } 1279 1280 return ret; 1281 } 1282 1283 static int uxc_delete(char *name, bool force) 1284 { 1285 struct blob_attr *cur, *tb[__CONF_MAX]; 1286 struct runtime_state *rsstate = NULL; 1287 struct settings *usettings = NULL; 1288 static struct blob_buf req; 1289 uint32_t id; 1290 int rem, ret = 0; 1291 const char *cfname = NULL; 1292 const char *sfname = NULL; 1293 struct stat sb; 1294 1295 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { 1296 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 1297 if (!tb[CONF_NAME] || !tb[CONF_PATH]) 1298 continue; 1299 1300 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) 1301 continue; 1302 1303 cfname = blobmsg_name(cur); 1304 break; 1305 } 1306 1307 if (!cfname) 1308 return -ENOENT; 1309 1310 rsstate = avl_find_element(&runtime, name, rsstate, avl); 1311 1312 if (rsstate && rsstate->running) { 1313 if (force) { 1314 ret = uxc_kill(name, SIGKILL); 1315 if (ret) 1316 goto errout; 1317 1318 } else { 1319 ret = -EWOULDBLOCK; 1320 goto errout; 1321 } 1322 } 1323 1324 if (rsstate) { 1325 ret = ubus_lookup_id(ctx, "container", &id); 1326 if (ret) 1327 goto errout; 1328 1329 blob_buf_init(&req, 0); 1330 blobmsg_add_string(&req, "name", rsstate->container_name); 1331 blobmsg_add_string(&req, "instance", rsstate->instance_name); 1332 1333 if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) { 1334 blob_buf_free(&req); 1335 ret = -EIO; 1336 goto errout; 1337 } 1338 } 1339 1340 usettings = avl_find_element(&settings, name, usettings, avl); 1341 if (usettings) 1342 sfname = usettings->fname; 1343 1344 if (sfname) { 1345 if (stat(sfname, &sb) == -1) { 1346 ret = -ENOENT; 1347 goto errout; 1348 } 1349 1350 if (unlink(sfname) == -1) { 1351 ret = -errno; 1352 goto errout; 1353 } 1354 } 1355 1356 if (stat(cfname, &sb) == -1) { 1357 ret = -ENOENT; 1358 goto errout; 1359 } 1360 1361 if (unlink(cfname) == -1) 1362 ret = -errno; 1363 1364 errout: 1365 return ret; 1366 } 1367 1368 static void reload_conf(void) 1369 { 1370 blob_buf_free(&conf); 1371 conf_load(false); 1372 settings_free(); 1373 blob_buf_free(&settingsbuf); 1374 conf_load(true); 1375 settings_add(); 1376 } 1377 1378 int main(int argc, char **argv) 1379 { 1380 enum uxc_cmd cmd = CMD_UNKNOWN; 1381 int ret = -EINVAL; 1382 char *bundle = NULL; 1383 char *pidfile = NULL; 1384 char *tmprwsize = NULL; 1385 char *writepath = NULL; 1386 char *requiredmounts = NULL; 1387 signed char autostart = -1; 1388 bool force = false; 1389 bool console = false; 1390 int signal = SIGTERM; 1391 int c; 1392 1393 if (argc < 2) 1394 return usage(); 1395 1396 ctx = ubus_connect(NULL); 1397 if (!ctx) 1398 return -ENODEV; 1399 1400 ret = conf_load(false); 1401 if (ret < 0) 1402 goto out; 1403 1404 ret = conf_load(true); 1405 if (ret < 0) 1406 goto conf_out; 1407 1408 ret = settings_add(); 1409 if (ret < 0) 1410 goto settings_out; 1411 1412 ret = runtime_load(); 1413 if (ret) 1414 goto settings_avl_out; 1415 1416 while (true) { 1417 int option_index = 0; 1418 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index); 1419 if (c == -1) 1420 break; 1421 1422 switch (c) { 1423 case 'a': 1424 autostart = 1; 1425 break; 1426 1427 case 'b': 1428 bundle = optarg; 1429 break; 1430 1431 case 'c': 1432 console = true; 1433 break; 1434 1435 case 'f': 1436 force = true; 1437 break; 1438 1439 case 'j': 1440 json_output = true; 1441 break; 1442 1443 case 'p': 1444 pidfile = optarg; 1445 break; 1446 1447 case 't': 1448 tmprwsize = optarg; 1449 break; 1450 1451 case 'v': 1452 verbose = true; 1453 break; 1454 1455 case 'V': 1456 printf("uxc %s\n", UXC_VERSION); 1457 exit(0); 1458 1459 case 'w': 1460 writepath = optarg; 1461 break; 1462 1463 case 'm': 1464 requiredmounts = optarg; 1465 break; 1466 } 1467 } 1468 1469 if (optind == argc) 1470 goto usage_out; 1471 1472 if (!strcmp("list", argv[optind])) 1473 cmd = CMD_LIST; 1474 else if (!strcmp("attach", argv[optind])) 1475 cmd = CMD_ATTACH; 1476 else if (!strcmp("boot", argv[optind])) 1477 cmd = CMD_BOOT; 1478 else if(!strcmp("start", argv[optind])) 1479 cmd = CMD_START; 1480 else if(!strcmp("state", argv[optind])) 1481 cmd = CMD_STATE; 1482 else if(!strcmp("kill", argv[optind])) 1483 cmd = CMD_KILL; 1484 else if(!strcmp("enable", argv[optind])) 1485 cmd = CMD_ENABLE; 1486 else if(!strcmp("disable", argv[optind])) 1487 cmd = CMD_DISABLE; 1488 else if(!strcmp("delete", argv[optind])) 1489 cmd = CMD_DELETE; 1490 else if(!strcmp("create", argv[optind])) 1491 cmd = CMD_CREATE; 1492 1493 switch (cmd) { 1494 case CMD_ATTACH: 1495 if (optind != argc - 2) 1496 goto usage_out; 1497 1498 ret = uxc_attach(argv[optind + 1]); 1499 break; 1500 1501 case CMD_LIST: 1502 ret = uxc_list(); 1503 break; 1504 1505 case CMD_BOOT: 1506 ret = uxc_boot(); 1507 break; 1508 1509 case CMD_START: 1510 if (optind != argc - 2) 1511 goto usage_out; 1512 1513 ret = uxc_start(argv[optind + 1], console); 1514 break; 1515 1516 case CMD_STATE: 1517 if (optind != argc - 2) 1518 goto usage_out; 1519 1520 ret = uxc_state(argv[optind + 1]); 1521 break; 1522 1523 case CMD_KILL: 1524 if (optind == (argc - 3)) 1525 signal = atoi(argv[optind + 2]); 1526 else if (optind > argc - 2) 1527 goto usage_out; 1528 1529 ret = uxc_kill(argv[optind + 1], signal); 1530 break; 1531 1532 case CMD_ENABLE: 1533 if (optind != argc - 2) 1534 goto usage_out; 1535 1536 ret = uxc_set(argv[optind + 1], NULL, 1, NULL, NULL, NULL, NULL); 1537 break; 1538 1539 case CMD_DISABLE: 1540 if (optind != argc - 2) 1541 goto usage_out; 1542 1543 ret = uxc_set(argv[optind + 1], NULL, 0, NULL, NULL, NULL, NULL); 1544 break; 1545 1546 case CMD_DELETE: 1547 if (optind != argc - 2) 1548 goto usage_out; 1549 1550 ret = uxc_delete(argv[optind + 1], force); 1551 break; 1552 1553 case CMD_CREATE: 1554 if (optind != argc - 2) 1555 goto usage_out; 1556 1557 ret = uxc_exists(argv[optind + 1]); 1558 if (ret) 1559 goto runtime_out; 1560 1561 ret = uxc_set(argv[optind + 1], bundle, autostart, pidfile, tmprwsize, writepath, requiredmounts); 1562 if (ret < 0) 1563 goto runtime_out; 1564 1565 if (ret > 0) 1566 reload_conf(); 1567 1568 ret = uxc_create(argv[optind + 1], false); 1569 break; 1570 1571 default: 1572 goto usage_out; 1573 } 1574 1575 goto runtime_out; 1576 1577 usage_out: 1578 ret = usage(); 1579 runtime_out: 1580 runtime_free(); 1581 settings_avl_out: 1582 settings_free(); 1583 settings_out: 1584 blob_buf_free(&settingsbuf); 1585 conf_out: 1586 blob_buf_free(&conf); 1587 out: 1588 ubus_free(ctx); 1589 1590 if (ret < 0) 1591 fprintf(stderr, "uxc error: %s\n", strerror(-ret)); 1592 1593 return ret; 1594 } 1595
This page was automatically generated by LXR 0.3.1. • OpenWrt