uxc: remove unused printf parameter
[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 *t2 = '\0';
1036
1037 if (asprintf(&t2, "%s/settings", t1) == -1)
1038 return -ENOMEM;
1039
1040 ret = mkdir(t2, 0755);
1041 if (ret && ret != EEXIST)
1042 return -ret;
1043
1044 free(t2);
1045 if (asprintf(&t2, "%s/settings/%s.json", t1, name) == -1)
1046 return -ENOMEM;
1047
1048 free(t1);
1049 f = open(t2, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1050 free(t2);
1051 }
1052 if (f < 0)
1053 return -errno;
1054 }
1055
1056 blob_buf_init(&req, 0);
1057 blobmsg_add_string(&req, "name", name);
1058 if (path)
1059 blobmsg_add_string(&req, "path", path);
1060
1061 if (autostart >= 0)
1062 blobmsg_add_u8(&req, "autostart", !!autostart);
1063
1064 if (pidfile)
1065 blobmsg_add_string(&req, "pidfile", pidfile);
1066
1067 if (tmprwsize)
1068 blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
1069
1070 if (writepath)
1071 blobmsg_add_string(&req, "write-overlay-path", writepath);
1072
1073 if (!requiredmounts && usettings && usettings->volumes)
1074 blobmsg_add_blob(&req, usettings->volumes);
1075
1076 if (requiredmounts) {
1077 mntarr = blobmsg_open_array(&req, "volumes");
1078 for (mnttok = requiredmounts; ; mnttok = NULL) {
1079 curvol = strtok_r(mnttok, ",;", &tmp);
1080 if (!curvol)
1081 break;
1082
1083 blobmsg_add_string(&req, NULL, curvol);
1084 }
1085 blobmsg_close_array(&req, mntarr);
1086 }
1087
1088 tmp = blobmsg_format_json_indent(req.head, true, 0);
1089 if (tmp) {
1090 dprintf(f, "%s\n", tmp);
1091 free(tmp);
1092 }
1093
1094 blob_buf_free(&req);
1095 close(f);
1096
1097 return 1;
1098 }
1099
1100 enum {
1101 BLOCK_INFO_DEVICE,
1102 BLOCK_INFO_UUID,
1103 BLOCK_INFO_TARGET,
1104 BLOCK_INFO_TYPE,
1105 BLOCK_INFO_MOUNT,
1106 __BLOCK_INFO_MAX,
1107 };
1108
1109 static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
1110 [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
1111 [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
1112 [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
1113 [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
1114 [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
1115 };
1116
1117
1118 /* check if device 'devname' is mounted according to blockd */
1119 static bool checkblock(const char *uuid)
1120 {
1121 struct blob_attr *tb[__BLOCK_INFO_MAX];
1122 struct blob_attr *cur;
1123 int rem;
1124
1125 blobmsg_for_each_attr(cur, blockinfo, rem) {
1126 blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1127
1128 if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
1129 continue;
1130
1131 if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
1132 return false;
1133 }
1134
1135 return true;
1136 }
1137
1138 enum {
1139 UCI_FSTAB_UUID,
1140 UCI_FSTAB_ANONYMOUS,
1141 __UCI_FSTAB_MAX,
1142 };
1143
1144 static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
1145 [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
1146 [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
1147 };
1148
1149 static const char *resolveuuid(const char *volname)
1150 {
1151 struct blob_attr *tb[__UCI_FSTAB_MAX];
1152 struct blob_attr *cur;
1153 const char *mntname;
1154 char *tmpvolname, *replc;
1155 int rem, res;
1156
1157 blobmsg_for_each_attr(cur, fstabinfo, rem) {
1158 blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1159
1160 if (!tb[UCI_FSTAB_UUID])
1161 continue;
1162
1163 if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
1164 continue;
1165
1166 mntname = blobmsg_name(cur);
1167 if (!mntname)
1168 continue;
1169
1170 tmpvolname = strdup(volname);
1171 while ((replc = strchr(tmpvolname, '-')))
1172 *replc = '_';
1173
1174 res = strcmp(tmpvolname, mntname);
1175 free(tmpvolname);
1176
1177 if (!res)
1178 return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
1179 };
1180
1181 return volname;
1182 };
1183
1184 /* check status of each required volume */
1185 static bool checkvolumes(struct blob_attr *volumes)
1186 {
1187 struct blob_attr *cur;
1188 int rem;
1189
1190 blobmsg_for_each_attr(cur, volumes, rem) {
1191 if (checkblock(resolveuuid(blobmsg_get_string(cur))))
1192 return true;
1193 }
1194
1195 return false;
1196 }
1197
1198 static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
1199 {
1200 blockinfo = blob_memdup(blobmsg_data(msg));
1201 }
1202
1203 static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
1204 {
1205 fstabinfo = blob_memdup(blobmsg_data(msg));
1206 }
1207
1208 static int uxc_boot(void)
1209 {
1210 struct blob_attr *cur, *tb[__CONF_MAX];
1211 struct runtime_state *rsstate = NULL;
1212 struct settings *usettings = NULL;
1213 static struct blob_buf req;
1214 int rem, ret = 0;
1215 char *name;
1216 unsigned int id;
1217 bool autostart;
1218
1219 ret = ubus_lookup_id(ctx, "block", &id);
1220 if (ret)
1221 return -ENOENT;
1222
1223 ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
1224 if (ret)
1225 return -ENXIO;
1226
1227 ret = ubus_lookup_id(ctx, "uci", &id);
1228 if (ret)
1229 return -ENOENT;
1230
1231 blob_buf_init(&req, 0);
1232 blobmsg_add_string(&req, "config", "fstab");
1233 blobmsg_add_string(&req, "type", "mount");
1234
1235 ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
1236 if (ret)
1237 return ret;
1238
1239 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
1240 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1241 if (!tb[CONF_NAME] || !tb[CONF_PATH])
1242 continue;
1243
1244 rsstate = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), rsstate, avl);
1245 if (rsstate)
1246 continue;
1247
1248 if (tb[CONF_AUTOSTART])
1249 autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]);
1250
1251 usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
1252 if (usettings && (usettings->autostart >= 0))
1253 autostart = !!(usettings->autostart);
1254
1255 if (!autostart)
1256 continue;
1257
1258 /* make sure all volumes are ready before starting */
1259 if (tb[CONF_VOLUMES])
1260 if (checkvolumes(tb[CONF_VOLUMES]))
1261 continue;
1262
1263 if (usettings && usettings->volumes)
1264 if (checkvolumes(usettings->volumes))
1265 continue;
1266
1267 name = strdup(blobmsg_get_string(tb[CONF_NAME]));
1268 if (uxc_exists(name))
1269 continue;
1270
1271 if (uxc_create(name, true))
1272 ++ret;
1273
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 blob_buf_free(&settingsbuf);
1371 conf_load(true);
1372 settings_add();
1373 }
1374
1375 int main(int argc, char **argv)
1376 {
1377 enum uxc_cmd cmd = CMD_UNKNOWN;
1378 int ret = -EINVAL;
1379 char *bundle = NULL;
1380 char *pidfile = NULL;
1381 char *tmprwsize = NULL;
1382 char *writepath = NULL;
1383 char *requiredmounts = NULL;
1384 signed char autostart = -1;
1385 bool force = false;
1386 bool console = false;
1387 int signal = SIGTERM;
1388 int c;
1389
1390 if (argc < 2)
1391 return usage();
1392
1393 ctx = ubus_connect(NULL);
1394 if (!ctx)
1395 return -ENODEV;
1396
1397 ret = conf_load(false);
1398 if (ret < 0)
1399 goto out;
1400
1401 ret = conf_load(true);
1402 if (ret < 0)
1403 goto conf_out;
1404
1405 ret = settings_add();
1406 if (ret < 0)
1407 goto settings_out;
1408
1409 ret = runtime_load();
1410 if (ret)
1411 goto settings_avl_out;
1412
1413 while (true) {
1414 int option_index = 0;
1415 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
1416 if (c == -1)
1417 break;
1418
1419 switch (c) {
1420 case 'a':
1421 autostart = 1;
1422 break;
1423
1424 case 'b':
1425 bundle = optarg;
1426 break;
1427
1428 case 'c':
1429 console = true;
1430 break;
1431
1432 case 'f':
1433 force = true;
1434 break;
1435
1436 case 'j':
1437 json_output = true;
1438 break;
1439
1440 case 'p':
1441 pidfile = optarg;
1442 break;
1443
1444 case 't':
1445 tmprwsize = optarg;
1446 break;
1447
1448 case 'v':
1449 verbose = true;
1450 break;
1451
1452 case 'V':
1453 printf("uxc %s\n", UXC_VERSION);
1454 exit(0);
1455
1456 case 'w':
1457 writepath = optarg;
1458 break;
1459
1460 case 'm':
1461 requiredmounts = optarg;
1462 break;
1463 }
1464 }
1465
1466 if (optind == argc)
1467 goto usage_out;
1468
1469 if (!strcmp("list", argv[optind]))
1470 cmd = CMD_LIST;
1471 else if (!strcmp("attach", argv[optind]))
1472 cmd = CMD_ATTACH;
1473 else if (!strcmp("boot", argv[optind]))
1474 cmd = CMD_BOOT;
1475 else if(!strcmp("start", argv[optind]))
1476 cmd = CMD_START;
1477 else if(!strcmp("state", argv[optind]))
1478 cmd = CMD_STATE;
1479 else if(!strcmp("kill", argv[optind]))
1480 cmd = CMD_KILL;
1481 else if(!strcmp("enable", argv[optind]))
1482 cmd = CMD_ENABLE;
1483 else if(!strcmp("disable", argv[optind]))
1484 cmd = CMD_DISABLE;
1485 else if(!strcmp("delete", argv[optind]))
1486 cmd = CMD_DELETE;
1487 else if(!strcmp("create", argv[optind]))
1488 cmd = CMD_CREATE;
1489
1490 switch (cmd) {
1491 case CMD_ATTACH:
1492 if (optind != argc - 2)
1493 goto usage_out;
1494
1495 ret = uxc_attach(argv[optind + 1]);
1496 break;
1497
1498 case CMD_LIST:
1499 ret = uxc_list();
1500 break;
1501
1502 case CMD_BOOT:
1503 ret = uxc_boot();
1504 break;
1505
1506 case CMD_START:
1507 if (optind != argc - 2)
1508 goto usage_out;
1509
1510 ret = uxc_start(argv[optind + 1], console);
1511 break;
1512
1513 case CMD_STATE:
1514 if (optind != argc - 2)
1515 goto usage_out;
1516
1517 ret = uxc_state(argv[optind + 1]);
1518 break;
1519
1520 case CMD_KILL:
1521 if (optind == (argc - 3))
1522 signal = atoi(argv[optind + 2]);
1523 else if (optind > argc - 2)
1524 goto usage_out;
1525
1526 ret = uxc_kill(argv[optind + 1], signal);
1527 break;
1528
1529 case CMD_ENABLE:
1530 if (optind != argc - 2)
1531 goto usage_out;
1532
1533 ret = uxc_set(argv[optind + 1], NULL, 1, NULL, NULL, NULL, NULL);
1534 break;
1535
1536 case CMD_DISABLE:
1537 if (optind != argc - 2)
1538 goto usage_out;
1539
1540 ret = uxc_set(argv[optind + 1], NULL, 0, NULL, NULL, NULL, NULL);
1541 break;
1542
1543 case CMD_DELETE:
1544 if (optind != argc - 2)
1545 goto usage_out;
1546
1547 ret = uxc_delete(argv[optind + 1], force);
1548 break;
1549
1550 case CMD_CREATE:
1551 if (optind != argc - 2)
1552 goto usage_out;
1553
1554 ret = uxc_exists(argv[optind + 1]);
1555 if (ret)
1556 goto runtime_out;
1557
1558 ret = uxc_set(argv[optind + 1], bundle, autostart, pidfile, tmprwsize, writepath, requiredmounts);
1559 if (ret < 0)
1560 goto runtime_out;
1561
1562 if (ret > 0)
1563 reload_conf();
1564
1565 ret = uxc_create(argv[optind + 1], false);
1566 break;
1567
1568 default:
1569 goto usage_out;
1570 }
1571
1572 goto runtime_out;
1573
1574 usage_out:
1575 ret = usage();
1576 runtime_out:
1577 runtime_free();
1578 settings_avl_out:
1579 settings_free();
1580 settings_out:
1581 blob_buf_free(&settingsbuf);
1582 conf_out:
1583 blob_buf_free(&conf);
1584 out:
1585 ubus_free(ctx);
1586
1587 if (ret < 0)
1588 fprintf(stderr, "uxc error: %s\n", strerror(-ret));
1589
1590 return ret;
1591 }