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