jail: fix and simplify userns uid/gid maps from OCI
[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 static struct blob_buf req;
644 uint32_t id;
645 int rem, ret = 0;
646 bool found = false;
647 char *fname;
648 struct stat sb;
649
650 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
651 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
652 if (!tb[CONF_NAME] || !tb[CONF_PATH])
653 continue;
654
655 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
656 continue;
657
658 fname = strdup(blobmsg_name(cur));
659 if (!fname)
660 return errno;
661
662 found = true;
663 break;
664 }
665
666 if (!found)
667 return ENOENT;
668
669 s = avl_find_element(&runtime, name, s, avl);
670
671 if (s && s->running) {
672 if (force) {
673 ret = uxc_kill(name, SIGKILL);
674 if (ret)
675 goto errout;
676
677 } else {
678 ret = EWOULDBLOCK;
679 goto errout;
680 }
681 }
682
683 if (s) {
684 ret = ubus_lookup_id(ctx, "container", &id);
685 if (ret)
686 goto errout;
687
688 blob_buf_init(&req, 0);
689 blobmsg_add_string(&req, "name", s->container_name);
690 blobmsg_add_string(&req, "instance", s->instance_name);
691
692 if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
693 blob_buf_free(&req);
694 ret=EIO;
695 goto errout;
696 }
697 }
698
699 if (stat(fname, &sb) == -1) {
700 ret=ENOENT;
701 goto errout;
702 }
703
704 if (unlink(fname) == -1)
705 ret=errno;
706
707 errout:
708 free(fname);
709 return ret;
710 }
711
712 static void reload_conf(void)
713 {
714 blob_buf_free(&conf);
715 conf_load(false);
716 }
717
718
719 int main(int argc, char **argv)
720 {
721 enum uxc_cmd cmd = CMD_UNKNOWN;
722 int ret = EINVAL;
723 char *bundle = NULL;
724 char *pidfile = NULL;
725 bool autostart = false;
726 bool force = false;
727 bool verbose = false;
728 int signal = SIGTERM;
729 int c;
730
731 if (argc < 2)
732 return usage();
733
734 ctx = ubus_connect(NULL);
735 if (!ctx)
736 return ENODEV;
737
738 ret = conf_load(false);
739 if (ret)
740 goto out;
741
742 ret = mkdir(UXC_RUNDIR, 0755);
743 if (ret && errno != EEXIST)
744 goto conf_out;
745
746 ret = conf_load(true);
747 if (ret)
748 goto conf_out;
749
750 ret = runtime_load();
751 if (ret)
752 goto state_out;
753
754 while (true) {
755 int option_index = 0;
756 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
757 if (c == -1)
758 break;
759
760 switch (c) {
761 case 'a':
762 autostart = true;
763 break;
764
765 case 'b':
766 bundle = optarg;
767 break;
768
769 case 'f':
770 force = true;
771 break;
772
773 case 'p':
774 pidfile = optarg;
775 break;
776
777 case 'v':
778 verbose = true;
779 break;
780
781 case 'V':
782 printf("uxc %s\n", UXC_VERSION);
783 exit(0);
784 }
785 }
786
787 if (optind == argc)
788 goto usage_out;
789
790 if (!strcmp("list", argv[optind]))
791 cmd = CMD_LIST;
792 else if (!strcmp("boot", argv[optind]))
793 cmd = CMD_BOOT;
794 else if(!strcmp("start", argv[optind]))
795 cmd = CMD_START;
796 else if(!strcmp("state", argv[optind]))
797 cmd = CMD_STATE;
798 else if(!strcmp("kill", argv[optind]))
799 cmd = CMD_KILL;
800 else if(!strcmp("enable", argv[optind]))
801 cmd = CMD_ENABLE;
802 else if(!strcmp("disable", argv[optind]))
803 cmd = CMD_DISABLE;
804 else if(!strcmp("delete", argv[optind]))
805 cmd = CMD_DELETE;
806 else if(!strcmp("create", argv[optind]))
807 cmd = CMD_CREATE;
808
809 switch (cmd) {
810 case CMD_LIST:
811 ret = uxc_list();
812 break;
813
814 case CMD_BOOT:
815 ret = uxc_boot();
816 break;
817
818 case CMD_START:
819 if (optind != argc - 2)
820 goto usage_out;
821
822 ret = uxc_start(argv[optind + 1]);
823 break;
824
825 case CMD_STATE:
826 if (optind != argc - 2)
827 goto usage_out;
828
829 ret = uxc_state(argv[optind + 1]);
830 break;
831
832 case CMD_KILL:
833 if (optind == (argc - 3))
834 signal = atoi(argv[optind + 2]);
835 else if (optind > argc - 2)
836 goto usage_out;
837
838 ret = uxc_kill(argv[optind + 1], signal);
839 break;
840
841 case CMD_ENABLE:
842 if (optind != argc - 2)
843 goto usage_out;
844
845 ret = uxc_set(argv[optind + 1], NULL, true, false, NULL);
846 break;
847
848 case CMD_DISABLE:
849 if (optind != argc - 2)
850 goto usage_out;
851
852 ret = uxc_set(argv[optind + 1], NULL, false, false, NULL);
853 break;
854
855 case CMD_DELETE:
856 if (optind != argc - 2)
857 goto usage_out;
858
859 ret = uxc_delete(argv[optind + 1], force);
860 break;
861
862 case CMD_CREATE:
863 if (optind != argc - 2)
864 goto usage_out;
865
866 if (bundle) {
867 ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile);
868 if (ret)
869 goto runtime_out;
870
871 reload_conf();
872 }
873
874 ret = uxc_create(argv[optind + 1], false);
875 break;
876
877 default:
878 goto usage_out;
879 }
880
881 goto runtime_out;
882
883 usage_out:
884 usage();
885 runtime_out:
886 runtime_free();
887 state_out:
888 blob_buf_free(&state);
889 conf_out:
890 blob_buf_free(&conf);
891 out:
892 ubus_free(ctx);
893
894 if (ret != 0)
895 fprintf(stderr, "uxc error: %s\n", strerror(ret));
896
897 return ret;
898 }