uxc: fix incomplete commit
[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 <stdlib.h>
19 #include <stdbool.h>
20 #include <fcntl.h>
21 #include <libubus.h>
22 #include <libubox/avl-cmp.h>
23 #include <libubox/blobmsg.h>
24 #include <libubox/blobmsg_json.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <getopt.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <glob.h>
34 #include <signal.h>
35
36 #include "log.h"
37
38 #define UXC_VERSION "0.1"
39 #define OCI_VERSION_STRING "1.0.2"
40 #define UXC_CONFDIR "/etc/uxc"
41 #define UXC_RUNDIR "/var/run/uxc"
42
43 struct runtime_state {
44 struct avl_node avl;
45 char *container_name;
46 char *instance_name;
47 char *jail_name;
48 bool running;
49 int runtime_pid;
50 int exitcode;
51 struct blob_attr *ocistate;
52 };
53
54 enum uxc_cmd {
55 CMD_LIST,
56 CMD_BOOT,
57 CMD_START,
58 CMD_STATE,
59 CMD_KILL,
60 CMD_ENABLE,
61 CMD_DISABLE,
62 CMD_DELETE,
63 CMD_CREATE,
64 CMD_UNKNOWN
65 };
66
67 #define OPT_ARGS "ab:fp:vV"
68 static struct option long_options[] = {
69 {"autostart", no_argument, 0, 'a' },
70 {"bundle", required_argument, 0, 'b' },
71 {"force", no_argument, 0, 'f' },
72 {"pid-file", required_argument, 0, 'p' },
73 {"verbose", no_argument, 0, 'v' },
74 {"version", no_argument, 0, 'V' },
75 {0, 0, 0, 0 }
76 };
77
78 AVL_TREE(runtime, avl_strcmp, false, NULL);
79 static struct blob_buf conf;
80 static struct blob_buf state;
81 static struct ubus_context *ctx;
82
83 static int usage(void) {
84 printf("syntax: uxc <command> [parameters ...]\n");
85 printf("commands:\n");
86 printf("\tlist\t\t\t\t\t\tlist all configured containers\n");
87 printf("\tcreate <conf> [--bundle <path>] [--autostart]\tcreate <conf> for OCI bundle at <path>\n");
88 printf("\tstart <conf>\t\t\t\t\tstart container <conf>\n");
89 printf("\tstate <conf>\t\t\t\t\tget state of container <conf>\n");
90 printf("\tkill <conf> [<signal>]\t\t\t\tsend signal to container <conf>\n");
91 printf("\tenable <conf>\t\t\t\t\tstart container <conf> on boot\n");
92 printf("\tdisable <conf>\t\t\t\t\tdon't start container <conf> on boot\n");
93 printf("\tdelete <conf> [--force]\t\t\t\tdelete <conf>\n");
94 return EINVAL;
95 }
96
97 enum {
98 CONF_NAME,
99 CONF_PATH,
100 CONF_JAIL,
101 CONF_AUTOSTART,
102 CONF_PIDFILE,
103 __CONF_MAX,
104 };
105
106 static const struct blobmsg_policy conf_policy[__CONF_MAX] = {
107 [CONF_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
108 [CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
109 [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
110 [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL },
111 [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING },
112 };
113
114 static int conf_load(bool load_state)
115 {
116 int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
117 int j, res;
118 glob_t gl;
119 char *globstr;
120 struct blob_buf *target = load_state?&state:&conf;
121 void *c, *o;
122
123 if (asprintf(&globstr, "%s/*.json", load_state?UXC_RUNDIR:UXC_CONFDIR) == -1)
124 return ENOMEM;
125
126 blob_buf_init(target, 0);
127 c = blobmsg_open_table(target, NULL);
128
129 res = glob(globstr, gl_flags, NULL, &gl);
130 free(globstr);
131 if (res < 0)
132 return 0;
133
134 for (j = 0; j < gl.gl_pathc; j++) {
135 o = blobmsg_open_table(target, strdup(gl.gl_pathv[j]));
136 if (!blobmsg_add_json_from_file(target, gl.gl_pathv[j])) {
137 ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]);
138 continue;
139 }
140 blobmsg_close_table(target, o);
141 }
142 blobmsg_close_table(target, c);
143 globfree(&gl);
144
145 return 0;
146 }
147
148 enum {
149 LIST_INSTANCES,
150 __LIST_MAX,
151 };
152
153 static const struct blobmsg_policy list_policy[__LIST_MAX] = {
154 [LIST_INSTANCES] = { .name = "instances", .type = BLOBMSG_TYPE_TABLE },
155 };
156
157 enum {
158 INSTANCE_RUNNING,
159 INSTANCE_PID,
160 INSTANCE_EXITCODE,
161 INSTANCE_JAIL,
162 __INSTANCE_MAX,
163 };
164
165 static const struct blobmsg_policy instance_policy[__INSTANCE_MAX] = {
166 [INSTANCE_RUNNING] = { .name = "running", .type = BLOBMSG_TYPE_BOOL },
167 [INSTANCE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
168 [INSTANCE_EXITCODE] = { .name = "exit_code", .type = BLOBMSG_TYPE_INT32 },
169 [INSTANCE_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_TABLE },
170 };
171
172 enum {
173 JAIL_NAME,
174 __JAIL_MAX,
175 };
176
177 static const struct blobmsg_policy jail_policy[__JAIL_MAX] = {
178 [JAIL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
179 };
180
181 static struct runtime_state *
182 runtime_alloc(const char *container_name)
183 {
184 struct runtime_state *s;
185 char *new_name;
186 s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
187 strcpy(new_name, container_name);
188 s->container_name = new_name;
189 s->avl.key = s->container_name;
190 return s;
191 }
192
193 enum {
194 STATE_OCIVERSION,
195 STATE_ID,
196 STATE_STATUS,
197 STATE_PID,
198 STATE_BUNDLE,
199 STATE_ANNOTATIONS,
200 __STATE_MAX,
201 };
202
203 static const struct blobmsg_policy state_policy[__STATE_MAX] = {
204 [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING },
205 [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING },
206 [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
207 [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
208 [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING },
209 [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE },
210 };
211
212
213 static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg)
214 {
215 struct blob_attr **ocistate = (struct blob_attr **)req->priv;
216 struct blob_attr *tb[__STATE_MAX];
217
218 blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
219
220 if (!tb[STATE_OCIVERSION] ||
221 !tb[STATE_ID] ||
222 !tb[STATE_STATUS] ||
223 !tb[STATE_BUNDLE])
224 return;
225
226 *ocistate = blob_memdup(msg);
227 }
228
229 static void get_ocistate(struct blob_attr **ocistate, const char *name)
230 {
231 char *objname;
232 unsigned int id;
233 int ret;
234 *ocistate = NULL;
235
236 asprintf(&objname, "container.%s", name);
237 ret = ubus_lookup_id(ctx, objname, &id);
238 free(objname);
239 if (ret)
240 return;
241
242 ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000);
243 }
244
245 static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg)
246 {
247 struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX];
248 int rem, remi;
249 const char *container_name, *instance_name, *jail_name;
250 bool running;
251 int pid, exitcode;
252 struct runtime_state *rs;
253
254 blobmsg_for_each_attr(cur, msg, rem) {
255 container_name = blobmsg_name(cur);
256 blobmsg_parse(list_policy, __LIST_MAX, tl, blobmsg_data(cur), blobmsg_len(cur));
257 if (!tl[LIST_INSTANCES])
258 continue;
259
260 blobmsg_for_each_attr(curi, tl[LIST_INSTANCES], remi) {
261 instance_name = blobmsg_name(curi);
262 blobmsg_parse(instance_policy, __INSTANCE_MAX, ti, blobmsg_data(curi), blobmsg_len(curi));
263
264 if (!ti[INSTANCE_JAIL])
265 continue;
266
267 blobmsg_parse(jail_policy, __JAIL_MAX, tj, blobmsg_data(ti[INSTANCE_JAIL]), blobmsg_len(ti[INSTANCE_JAIL]));
268 if (!tj[JAIL_NAME])
269 continue;
270
271 jail_name = blobmsg_get_string(tj[JAIL_NAME]);
272
273 running = ti[INSTANCE_RUNNING] && blobmsg_get_bool(ti[INSTANCE_RUNNING]);
274
275 if (ti[INSTANCE_PID])
276 pid = blobmsg_get_u32(ti[INSTANCE_PID]);
277 else
278 pid = -1;
279
280 if (ti[INSTANCE_EXITCODE])
281 exitcode = blobmsg_get_u32(ti[INSTANCE_EXITCODE]);
282 else
283 exitcode = -1;
284
285 rs = runtime_alloc(container_name);
286 rs->instance_name = strdup(instance_name);
287 rs->jail_name = strdup(jail_name);
288 rs->runtime_pid = pid;
289 rs->exitcode = exitcode;
290 rs->running = running;
291 avl_insert(&runtime, &rs->avl);
292 }
293 }
294
295 return;
296 }
297
298 static int runtime_load(void)
299 {
300 struct runtime_state *item, *tmp;
301 uint32_t id;
302
303 avl_init(&runtime, avl_strcmp, false, NULL);
304 if (ubus_lookup_id(ctx, "container", &id) ||
305 ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000))
306 return EIO;
307
308
309 avl_for_each_element_safe(&runtime, item, avl, tmp)
310 get_ocistate(&item->ocistate, item->jail_name);
311
312 return 0;
313 }
314
315 static void runtime_free(void)
316 {
317 struct runtime_state *item, *tmp;
318
319 avl_for_each_element_safe(&runtime, item, avl, tmp) {
320 avl_delete(&runtime, &item->avl);
321 free(item->instance_name);
322 free(item->jail_name);
323 free(item->ocistate);
324 free(item);
325 }
326
327 return;
328 }
329
330 static int uxc_state(char *name)
331 {
332 struct runtime_state *s = avl_find_element(&runtime, name, s, avl);
333 struct blob_attr *ocistate = NULL;
334 struct blob_attr *cur, *tb[__CONF_MAX];
335 int rem;
336 char *bundle = NULL;
337 char *jail_name = NULL;
338 static struct blob_buf buf;
339
340 if (s)
341 ocistate = s->ocistate;
342
343 if (ocistate) {
344 printf("%s\n", blobmsg_format_json_indent(ocistate, true, 0));
345 return 0;
346 }
347
348 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
349 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
350 if (!tb[CONF_NAME] || !tb[CONF_PATH])
351 continue;
352
353 if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) {
354 if (tb[CONF_JAIL])
355 jail_name = blobmsg_get_string(tb[CONF_JAIL]);
356 else
357 jail_name = name;
358
359 bundle = blobmsg_get_string(tb[CONF_PATH]);
360 break;
361 }
362 }
363
364 if (!bundle)
365 return ENOENT;
366
367 blob_buf_init(&buf, 0);
368 blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING);
369 blobmsg_add_string(&buf, "id", jail_name);
370 blobmsg_add_string(&buf, "status", s?"stopped":"uninitialized");
371 blobmsg_add_string(&buf, "bundle", bundle);
372
373 printf("%s\n", blobmsg_format_json_indent(buf.head, true, 0));
374 blob_buf_free(&buf);
375
376 return 0;
377 }
378
379 static int uxc_list(void)
380 {
381 struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
382 int rem;
383 struct runtime_state *s = NULL;
384 char *name;
385 char *ocistatus;
386 int container_pid = -1;
387 bool autostart;
388
389 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
390 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
391 if (!tb[CONF_NAME] || !tb[CONF_PATH])
392 continue;
393
394 autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]);
395 ocistatus = NULL;
396 container_pid = 0;
397 name = blobmsg_get_string(tb[CONF_NAME]);
398 s = avl_find_element(&runtime, name, s, avl);
399
400 if (s && s->ocistate) {
401 blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(s->ocistate), blobmsg_len(s->ocistate));
402 ocistatus = blobmsg_get_string(ts[STATE_STATUS]);
403 container_pid = blobmsg_get_u32(ts[STATE_PID]);
404 }
405
406 printf("[%c] %s %s", autostart?'*':' ', name, ocistatus?:(s && s->running)?"creating":"stopped");
407
408 if (s && !s->running && (s->exitcode >= 0))
409 printf(" exitcode: %d (%s)", s->exitcode, strerror(s->exitcode));
410
411 if (s && s->running && (s->runtime_pid >= 0))
412 printf(" runtime pid: %d", s->runtime_pid);
413
414 if (s && s->running && (container_pid >= 0))
415 printf(" container pid: %d", container_pid);
416
417 printf("\n");
418 }
419
420 return 0;
421 }
422
423 static int uxc_create(char *name, bool immediately)
424 {
425 static struct blob_buf req;
426 struct blob_attr *cur, *tb[__CONF_MAX];
427 int rem, ret;
428 uint32_t id;
429 struct runtime_state *s = NULL;
430 char *path = NULL, *jailname = NULL, *pidfile = NULL;
431 void *in, *ins, *j;
432 bool found = false;
433
434 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
435 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
436 if (!tb[CONF_NAME] || !tb[CONF_PATH])
437 continue;
438
439 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
440 continue;
441
442 found = true;
443 path = strdup(blobmsg_get_string(tb[CONF_PATH]));
444
445 if (tb[CONF_PIDFILE])
446 pidfile = strdup(blobmsg_get_string(tb[CONF_PIDFILE]));
447 break;
448 }
449
450 if (!found)
451 return ENOENT;
452
453 s = avl_find_element(&runtime, name, s, avl);
454
455 if (s && (s->running))
456 return EEXIST;
457
458 if (tb[CONF_JAIL])
459 jailname = strdup(blobmsg_get_string(tb[CONF_JAIL]));
460
461 blob_buf_init(&req, 0);
462 blobmsg_add_string(&req, "name", name);
463 ins = blobmsg_open_table(&req, "instances");
464 in = blobmsg_open_table(&req, name);
465 blobmsg_add_string(&req, "bundle", path);
466 j = blobmsg_open_table(&req, "jail");
467 blobmsg_add_string(&req, "name", jailname?:name);
468 blobmsg_add_u8(&req, "immediately", immediately);
469 if (pidfile)
470 blobmsg_add_string(&req, "pidfile", pidfile);
471
472 blobmsg_close_table(&req, j);
473 blobmsg_close_table(&req, in);
474 blobmsg_close_table(&req, ins);
475
476 ret = 0;
477 if (ubus_lookup_id(ctx, "container", &id) ||
478 ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
479 ret = EIO;
480 }
481
482 free(jailname);
483 free(path);
484 blob_buf_free(&req);
485
486 return ret;
487 }
488
489 static int uxc_start(const char *name)
490 {
491 char *objname;
492 unsigned int id;
493
494 asprintf(&objname, "container.%s", name);
495 if (ubus_lookup_id(ctx, objname, &id))
496 return ENOENT;
497
498 return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000);
499 }
500
501 static int uxc_kill(char *name, int signal)
502 {
503 static struct blob_buf req;
504 struct blob_attr *cur, *tb[__CONF_MAX];
505 int rem, ret;
506 char *objname;
507 unsigned int id;
508 struct runtime_state *s = NULL;
509 bool found = false;
510
511 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
512 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
513 if (!tb[CONF_NAME] || !tb[CONF_PATH])
514 continue;
515
516 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
517 continue;
518
519 found = true;
520 break;
521 }
522
523 if (!found)
524 return ENOENT;
525
526 s = avl_find_element(&runtime, name, s, avl);
527
528 if (!s || !(s->running))
529 return ENOENT;
530
531 blob_buf_init(&req, 0);
532 blobmsg_add_u32(&req, "signal", signal);
533 blobmsg_add_string(&req, "name", name);
534
535 asprintf(&objname, "container.%s", name);
536 ret = ubus_lookup_id(ctx, objname, &id);
537 free(objname);
538 if (ret)
539 return ENOENT;
540
541 if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
542 return EIO;
543
544 return 0;
545 }
546
547
548 static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile)
549 {
550 static struct blob_buf req;
551 struct blob_attr *cur, *tb[__CONF_MAX];
552 int rem, ret;
553 bool found = false;
554 char *fname = NULL;
555 char *keeppath = NULL;
556 int f;
557 struct stat sb;
558
559 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
560 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
561 if (!tb[CONF_NAME] || !tb[CONF_PATH])
562 continue;
563
564 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
565 continue;
566
567 found = true;
568 break;
569 }
570
571 if (found && add)
572 return EEXIST;
573
574 if (!found && !add)
575 return ENOENT;
576
577 if (add && !path)
578 return EINVAL;
579
580 if (path) {
581 if (stat(path, &sb) == -1)
582 return ENOENT;
583
584 if ((sb.st_mode & S_IFMT) != S_IFDIR)
585 return ENOTDIR;
586 }
587
588 ret = mkdir(UXC_CONFDIR, 0755);
589
590 if (ret && errno != EEXIST)
591 return ret;
592
593 if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) < 1)
594 return ENOMEM;
595
596 f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
597 if (f < 0)
598 return errno;
599
600 if (!add)
601 keeppath = strdup(blobmsg_get_string(tb[CONF_PATH]));
602
603 blob_buf_init(&req, 0);
604 blobmsg_add_string(&req, "name", name);
605 blobmsg_add_string(&req, "path", path?:keeppath);
606 blobmsg_add_u8(&req, "autostart", autostart);
607 if (pidfile)
608 blobmsg_add_string(&req, "pidfile", pidfile);
609
610 dprintf(f, "%s\n", blobmsg_format_json_indent(req.head, true, 0));
611
612 if (!add)
613 free(keeppath);
614
615 blob_buf_free(&req);
616
617 return 0;
618 }
619
620 static int uxc_boot(void)
621 {
622 struct blob_attr *cur, *tb[__CONF_MAX];
623 int rem, ret = 0;
624 char *name;
625
626 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
627 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
628 if (!tb[CONF_NAME] || !tb[CONF_PATH] || !tb[CONF_AUTOSTART] || !blobmsg_get_bool(tb[CONF_AUTOSTART]))
629 continue;
630
631 name = strdup(blobmsg_get_string(tb[CONF_NAME]));
632 ret += uxc_create(name, true);
633 free(name);
634 }
635
636 return ret;
637 }
638
639 static int uxc_delete(char *name, bool force)
640 {
641 struct blob_attr *cur, *tb[__CONF_MAX];
642 struct runtime_state *s = NULL;
643 int rem, ret = 0;
644 bool found = false;
645 char *fname;
646 struct stat sb;
647
648 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
649 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
650 if (!tb[CONF_NAME] || !tb[CONF_PATH])
651 continue;
652
653 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
654 continue;
655
656 fname = strdup(blobmsg_name(cur));
657 if (!fname)
658 return errno;
659
660 found = true;
661 break;
662 }
663
664 if (!found)
665 return ENOENT;
666
667 s = avl_find_element(&runtime, name, s, avl);
668
669 if (s && s->running) {
670 if (force) {
671 ret = uxc_kill(name, SIGKILL);
672 if (ret)
673 goto errout;
674
675 } else {
676 ret = EWOULDBLOCK;
677 goto errout;
678 }
679 }
680
681 if (stat(fname, &sb) == -1) {
682 ret=ENOENT;
683 goto errout;
684 }
685
686 if (unlink(fname) == -1)
687 ret=errno;
688
689 errout:
690 free(fname);
691 return ret;
692 }
693
694 static void reload_conf(void)
695 {
696 blob_buf_free(&conf);
697 conf_load(false);
698 }
699
700
701 int main(int argc, char **argv)
702 {
703 enum uxc_cmd cmd = CMD_UNKNOWN;
704 int ret = EINVAL;
705 char *bundle = NULL;
706 char *pidfile = NULL;
707 bool autostart = false;
708 bool force = false;
709 bool verbose = false;
710 int signal = SIGTERM;
711 int c;
712
713 if (argc < 2)
714 return usage();
715
716 ctx = ubus_connect(NULL);
717 if (!ctx)
718 return ENODEV;
719
720 ret = conf_load(false);
721 if (ret)
722 goto out;
723
724 ret = mkdir(UXC_RUNDIR, 0755);
725 if (ret && errno != EEXIST)
726 goto conf_out;
727
728 ret = conf_load(true);
729 if (ret)
730 goto conf_out;
731
732 ret = runtime_load();
733 if (ret)
734 goto state_out;
735
736 while (true) {
737 int option_index = 0;
738 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
739 if (c == -1)
740 break;
741
742 switch (c) {
743 case 'a':
744 autostart = true;
745 break;
746
747 case 'b':
748 bundle = optarg;
749 break;
750
751 case 'f':
752 force = true;
753 break;
754
755 case 'p':
756 pidfile = optarg;
757 break;
758
759 case 'v':
760 verbose = true;
761 break;
762
763 case 'V':
764 printf("uxc %s\n", UXC_VERSION);
765 exit(0);
766 }
767 }
768
769 if (optind == argc)
770 goto usage_out;
771
772 if (!strcmp("list", argv[optind]))
773 cmd = CMD_LIST;
774 else if (!strcmp("boot", argv[optind]))
775 cmd = CMD_BOOT;
776 else if(!strcmp("start", argv[optind]))
777 cmd = CMD_START;
778 else if(!strcmp("state", argv[optind]))
779 cmd = CMD_STATE;
780 else if(!strcmp("kill", argv[optind]))
781 cmd = CMD_KILL;
782 else if(!strcmp("enable", argv[optind]))
783 cmd = CMD_ENABLE;
784 else if(!strcmp("disable", argv[optind]))
785 cmd = CMD_DISABLE;
786 else if(!strcmp("delete", argv[optind]))
787 cmd = CMD_DELETE;
788 else if(!strcmp("create", argv[optind]))
789 cmd = CMD_CREATE;
790
791 switch (cmd) {
792 case CMD_LIST:
793 ret = uxc_list();
794 break;
795
796 case CMD_BOOT:
797 ret = uxc_boot();
798 break;
799
800 case CMD_START:
801 if (optind != argc - 2)
802 goto usage_out;
803
804 ret = uxc_start(argv[optind + 1]);
805 break;
806
807 case CMD_STATE:
808 if (optind != argc - 2)
809 goto usage_out;
810
811 ret = uxc_state(argv[optind + 1]);
812 break;
813
814 case CMD_KILL:
815 if (optind == (argc - 3))
816 signal = atoi(argv[optind + 2]);
817 else if (optind > argc - 2)
818 goto usage_out;
819
820 ret = uxc_kill(argv[optind + 1], signal);
821 break;
822
823 case CMD_ENABLE:
824 if (optind != argc - 2)
825 goto usage_out;
826
827 ret = uxc_set(argv[optind + 1], NULL, true, false, NULL);
828 break;
829
830 case CMD_DISABLE:
831 if (optind != argc - 2)
832 goto usage_out;
833
834 ret = uxc_set(argv[optind + 1], NULL, false, false, NULL);
835 break;
836
837 case CMD_DELETE:
838 if (optind != argc - 2)
839 goto usage_out;
840
841 ret = uxc_delete(argv[optind + 1], force);
842 break;
843
844 case CMD_CREATE:
845 if (optind != argc - 2)
846 goto usage_out;
847
848 if (bundle) {
849 ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile);
850 if (ret)
851 goto runtime_out;
852
853 reload_conf();
854 }
855
856 ret = uxc_create(argv[optind + 1], false);
857 break;
858
859 default:
860 goto usage_out;
861 }
862
863 goto runtime_out;
864
865 usage_out:
866 usage();
867 runtime_out:
868 runtime_free();
869 state_out:
870 blob_buf_free(&state);
871 conf_out:
872 blob_buf_free(&conf);
873 out:
874 ubus_free(ctx);
875
876 if (ret != 0)
877 fprintf(stderr, "uxc error: %s\n", strerror(ret));
878
879 return ret;
880 }