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