trace: use standard POSIX header for basename()
[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 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 }