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