hotplug-dispatch: fix rare memory leaks in error paths
[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:fm:p: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 {"mounts", required_argument, 0, 'm' },
74 {"pid-file", required_argument, 0, 'p' },
75 {"temp-overlay-size", required_argument, 0, 't' },
76 {"write-overlay-path", required_argument, 0, 'w' },
77 {"verbose", no_argument, 0, 'v' },
78 {"version", no_argument, 0, 'V' },
79 {0, 0, 0, 0 }
80 };
81
82 AVL_TREE(runtime, avl_strcmp, false, NULL);
83 static struct blob_buf conf;
84 static struct blob_attr *blockinfo;
85 static struct blob_attr *fstabinfo;
86 static struct ubus_context *ctx;
87
88 static int usage(void) {
89 printf("syntax: uxc <command> [parameters ...]\n");
90 printf("commands:\n");
91 printf("\tlist\t\t\t\t\t\tlist all configured containers\n");
92 printf("\tcreate <conf>\t\t\t\t\t(re-)create <conf>\n");
93 printf(" [--bundle <path>]\t\t\tOCI bundle at <path>\n");
94 printf(" [--autostart]\t\t\t\tstart on boot\n");
95 printf(" [--temp-overlay-size size]\t\tuse tmpfs overlay with {size}\n");
96 printf(" [--write-overlay-path path]\t\tuse overlay on {path}\n");
97 printf(" [--volumes v1,v2,...,vN]\t\trequire volumes to be available\n");
98 printf("\tstart <conf>\t\t\t\t\tstart container <conf>\n");
99 printf("\tstate <conf>\t\t\t\t\tget state of container <conf>\n");
100 printf("\tkill <conf> [<signal>]\t\t\t\tsend signal to container <conf>\n");
101 printf("\tenable <conf>\t\t\t\t\tstart container <conf> on boot\n");
102 printf("\tdisable <conf>\t\t\t\t\tdon't start container <conf> on boot\n");
103 printf("\tdelete <conf> [--force]\t\t\t\tdelete <conf>\n");
104 return EINVAL;
105 }
106
107 enum {
108 CONF_NAME,
109 CONF_PATH,
110 CONF_JAIL,
111 CONF_AUTOSTART,
112 CONF_PIDFILE,
113 CONF_TEMP_OVERLAY_SIZE,
114 CONF_WRITE_OVERLAY_PATH,
115 CONF_VOLUMES,
116 __CONF_MAX,
117 };
118
119 static const struct blobmsg_policy conf_policy[__CONF_MAX] = {
120 [CONF_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
121 [CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
122 [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
123 [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL },
124 [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING },
125 [CONF_TEMP_OVERLAY_SIZE] = { .name = "temp-overlay-size", .type = BLOBMSG_TYPE_STRING },
126 [CONF_WRITE_OVERLAY_PATH] = { .name = "write-overlay-path", .type = BLOBMSG_TYPE_STRING },
127 [CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY },
128 };
129
130 static int conf_load(void)
131 {
132 int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
133 int j, res;
134 glob_t gl;
135 char *globstr;
136 void *c, *o;
137
138 if (asprintf(&globstr, "%s/*.json", UXC_CONFDIR) == -1)
139 return ENOMEM;
140
141 blob_buf_init(&conf, 0);
142 c = blobmsg_open_table(&conf, NULL);
143
144 res = glob(globstr, gl_flags, NULL, &gl);
145 free(globstr);
146 if (res < 0)
147 return 0;
148
149 for (j = 0; j < gl.gl_pathc; j++) {
150 o = blobmsg_open_table(&conf, strdup(gl.gl_pathv[j]));
151 if (!blobmsg_add_json_from_file(&conf, gl.gl_pathv[j])) {
152 ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]);
153 continue;
154 }
155 blobmsg_close_table(&conf, o);
156 }
157 blobmsg_close_table(&conf, c);
158 globfree(&gl);
159
160 return 0;
161 }
162
163 enum {
164 LIST_INSTANCES,
165 __LIST_MAX,
166 };
167
168 static const struct blobmsg_policy list_policy[__LIST_MAX] = {
169 [LIST_INSTANCES] = { .name = "instances", .type = BLOBMSG_TYPE_TABLE },
170 };
171
172 enum {
173 INSTANCE_RUNNING,
174 INSTANCE_PID,
175 INSTANCE_EXITCODE,
176 INSTANCE_JAIL,
177 __INSTANCE_MAX,
178 };
179
180 static const struct blobmsg_policy instance_policy[__INSTANCE_MAX] = {
181 [INSTANCE_RUNNING] = { .name = "running", .type = BLOBMSG_TYPE_BOOL },
182 [INSTANCE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
183 [INSTANCE_EXITCODE] = { .name = "exit_code", .type = BLOBMSG_TYPE_INT32 },
184 [INSTANCE_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_TABLE },
185 };
186
187 enum {
188 JAIL_NAME,
189 __JAIL_MAX,
190 };
191
192 static const struct blobmsg_policy jail_policy[__JAIL_MAX] = {
193 [JAIL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
194 };
195
196 static struct runtime_state *
197 runtime_alloc(const char *container_name)
198 {
199 struct runtime_state *s;
200 char *new_name;
201 s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
202 strcpy(new_name, container_name);
203 s->container_name = new_name;
204 s->avl.key = s->container_name;
205 return s;
206 }
207
208 enum {
209 STATE_OCIVERSION,
210 STATE_ID,
211 STATE_STATUS,
212 STATE_PID,
213 STATE_BUNDLE,
214 STATE_ANNOTATIONS,
215 __STATE_MAX,
216 };
217
218 static const struct blobmsg_policy state_policy[__STATE_MAX] = {
219 [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING },
220 [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING },
221 [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
222 [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
223 [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING },
224 [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE },
225 };
226
227
228 static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg)
229 {
230 struct blob_attr **ocistate = (struct blob_attr **)req->priv;
231 struct blob_attr *tb[__STATE_MAX];
232
233 blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
234
235 if (!tb[STATE_OCIVERSION] ||
236 !tb[STATE_ID] ||
237 !tb[STATE_STATUS] ||
238 !tb[STATE_BUNDLE])
239 return;
240
241 *ocistate = blob_memdup(msg);
242 }
243
244 static void get_ocistate(struct blob_attr **ocistate, const char *name)
245 {
246 char *objname;
247 unsigned int id;
248 int ret;
249 *ocistate = NULL;
250
251 if (asprintf(&objname, "container.%s", name) == -1)
252 exit(ENOMEM);
253
254 ret = ubus_lookup_id(ctx, objname, &id);
255 free(objname);
256 if (ret)
257 return;
258
259 ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000);
260 }
261
262 static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg)
263 {
264 struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX];
265 int rem, remi;
266 const char *container_name, *instance_name, *jail_name;
267 bool running;
268 int pid, exitcode;
269 struct runtime_state *rs;
270
271 blobmsg_for_each_attr(cur, msg, rem) {
272 container_name = blobmsg_name(cur);
273 blobmsg_parse(list_policy, __LIST_MAX, tl, blobmsg_data(cur), blobmsg_len(cur));
274 if (!tl[LIST_INSTANCES])
275 continue;
276
277 blobmsg_for_each_attr(curi, tl[LIST_INSTANCES], remi) {
278 instance_name = blobmsg_name(curi);
279 blobmsg_parse(instance_policy, __INSTANCE_MAX, ti, blobmsg_data(curi), blobmsg_len(curi));
280
281 if (!ti[INSTANCE_JAIL])
282 continue;
283
284 blobmsg_parse(jail_policy, __JAIL_MAX, tj, blobmsg_data(ti[INSTANCE_JAIL]), blobmsg_len(ti[INSTANCE_JAIL]));
285 if (!tj[JAIL_NAME])
286 continue;
287
288 jail_name = blobmsg_get_string(tj[JAIL_NAME]);
289
290 running = ti[INSTANCE_RUNNING] && blobmsg_get_bool(ti[INSTANCE_RUNNING]);
291
292 if (ti[INSTANCE_PID])
293 pid = blobmsg_get_u32(ti[INSTANCE_PID]);
294 else
295 pid = -1;
296
297 if (ti[INSTANCE_EXITCODE])
298 exitcode = blobmsg_get_u32(ti[INSTANCE_EXITCODE]);
299 else
300 exitcode = -1;
301
302 rs = runtime_alloc(container_name);
303 rs->instance_name = strdup(instance_name);
304 rs->jail_name = strdup(jail_name);
305 rs->runtime_pid = pid;
306 rs->exitcode = exitcode;
307 rs->running = running;
308 avl_insert(&runtime, &rs->avl);
309 }
310 }
311
312 return;
313 }
314
315 static int runtime_load(void)
316 {
317 struct runtime_state *item, *tmp;
318 uint32_t id;
319
320 avl_init(&runtime, avl_strcmp, false, NULL);
321 if (ubus_lookup_id(ctx, "container", &id) ||
322 ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000))
323 return EIO;
324
325
326 avl_for_each_element_safe(&runtime, item, avl, tmp)
327 get_ocistate(&item->ocistate, item->jail_name);
328
329 return 0;
330 }
331
332 static void runtime_free(void)
333 {
334 struct runtime_state *item, *tmp;
335
336 avl_for_each_element_safe(&runtime, item, avl, tmp) {
337 avl_delete(&runtime, &item->avl);
338 free(item->instance_name);
339 free(item->jail_name);
340 free(item->ocistate);
341 free(item);
342 }
343
344 return;
345 }
346
347 static int uxc_state(char *name)
348 {
349 struct runtime_state *s = avl_find_element(&runtime, name, s, avl);
350 struct blob_attr *ocistate = NULL;
351 struct blob_attr *cur, *tb[__CONF_MAX];
352 int rem;
353 char *bundle = NULL;
354 char *jail_name = NULL;
355 static struct blob_buf buf;
356
357 if (s)
358 ocistate = s->ocistate;
359
360 if (ocistate) {
361 printf("%s\n", blobmsg_format_json_indent(ocistate, true, 0));
362 return 0;
363 }
364
365 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
366 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
367 if (!tb[CONF_NAME] || !tb[CONF_PATH])
368 continue;
369
370 if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) {
371 if (tb[CONF_JAIL])
372 jail_name = blobmsg_get_string(tb[CONF_JAIL]);
373 else
374 jail_name = name;
375
376 bundle = blobmsg_get_string(tb[CONF_PATH]);
377 break;
378 }
379 }
380
381 if (!bundle)
382 return ENOENT;
383
384 blob_buf_init(&buf, 0);
385 blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING);
386 blobmsg_add_string(&buf, "id", jail_name);
387 blobmsg_add_string(&buf, "status", s?"stopped":"uninitialized");
388 blobmsg_add_string(&buf, "bundle", bundle);
389
390 printf("%s\n", blobmsg_format_json_indent(buf.head, true, 0));
391 blob_buf_free(&buf);
392
393 return 0;
394 }
395
396 static int uxc_list(void)
397 {
398 struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
399 int rem;
400 struct runtime_state *s = NULL;
401 char *name;
402 char *ocistatus;
403 int container_pid = -1;
404 bool autostart;
405
406 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
407 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
408 if (!tb[CONF_NAME] || !tb[CONF_PATH])
409 continue;
410
411 autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]);
412 ocistatus = NULL;
413 container_pid = 0;
414 name = blobmsg_get_string(tb[CONF_NAME]);
415 s = avl_find_element(&runtime, name, s, avl);
416
417 if (s && s->ocistate) {
418 blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(s->ocistate), blobmsg_len(s->ocistate));
419 ocistatus = blobmsg_get_string(ts[STATE_STATUS]);
420 container_pid = blobmsg_get_u32(ts[STATE_PID]);
421 }
422
423 printf("[%c] %s %s", autostart?'*':' ', name, ocistatus?:(s && s->running)?"creating":"stopped");
424
425 if (s && !s->running && (s->exitcode >= 0))
426 printf(" exitcode: %d (%s)", s->exitcode, strerror(s->exitcode));
427
428 if (s && s->running && (s->runtime_pid >= 0))
429 printf(" runtime pid: %d", s->runtime_pid);
430
431 if (s && s->running && (container_pid >= 0))
432 printf(" container pid: %d", container_pid);
433
434 printf("\n");
435 }
436
437 return 0;
438 }
439
440 static int uxc_create(char *name, bool immediately)
441 {
442 static struct blob_buf req;
443 struct blob_attr *cur, *tb[__CONF_MAX];
444 int rem, ret;
445 uint32_t id;
446 struct runtime_state *s = NULL;
447 char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
448
449 void *in, *ins, *j;
450 bool found = false;
451
452 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
453 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
454 if (!tb[CONF_NAME] || !tb[CONF_PATH])
455 continue;
456
457 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
458 continue;
459
460 found = true;
461 path = strdup(blobmsg_get_string(tb[CONF_PATH]));
462
463 if (tb[CONF_PIDFILE])
464 pidfile = strdup(blobmsg_get_string(tb[CONF_PIDFILE]));
465
466 if (tb[CONF_TEMP_OVERLAY_SIZE])
467 tmprwsize = strdup(blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]));
468
469 if (tb[CONF_WRITE_OVERLAY_PATH])
470 writepath = strdup(blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]));
471
472 break;
473 }
474
475 if (!found)
476 return ENOENT;
477
478 s = avl_find_element(&runtime, name, s, avl);
479
480 if (s && (s->running))
481 return EEXIST;
482
483 if (tb[CONF_JAIL])
484 jailname = strdup(blobmsg_get_string(tb[CONF_JAIL]));
485
486 blob_buf_init(&req, 0);
487 blobmsg_add_string(&req, "name", name);
488 ins = blobmsg_open_table(&req, "instances");
489 in = blobmsg_open_table(&req, name);
490 blobmsg_add_string(&req, "bundle", path);
491 j = blobmsg_open_table(&req, "jail");
492 blobmsg_add_string(&req, "name", jailname?:name);
493 blobmsg_add_u8(&req, "immediately", immediately);
494
495 if (pidfile)
496 blobmsg_add_string(&req, "pidfile", pidfile);
497
498 blobmsg_close_table(&req, j);
499
500 if (writepath)
501 blobmsg_add_string(&req, "overlaydir", writepath);
502
503 if (tmprwsize)
504 blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize);
505
506 blobmsg_close_table(&req, in);
507 blobmsg_close_table(&req, ins);
508
509 if (verbose)
510 fprintf(stderr, "adding container to procd:\n\t%s\n",
511 blobmsg_format_json_indent(req.head, true, 1));
512
513 ret = 0;
514 if (ubus_lookup_id(ctx, "container", &id) ||
515 ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
516 ret = EIO;
517 }
518
519 free(jailname);
520 free(path);
521 blob_buf_free(&req);
522
523 return ret;
524 }
525
526 static int uxc_start(const char *name)
527 {
528 char *objname;
529 unsigned int id;
530
531 if (asprintf(&objname, "container.%s", name) == -1)
532 return ENOMEM;
533
534 if (ubus_lookup_id(ctx, objname, &id))
535 return ENOENT;
536
537 return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000);
538 }
539
540 static int uxc_kill(char *name, int signal)
541 {
542 static struct blob_buf req;
543 struct blob_attr *cur, *tb[__CONF_MAX];
544 int rem, ret;
545 char *objname;
546 unsigned int id;
547 struct runtime_state *s = NULL;
548 bool found = false;
549
550 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
551 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
552 if (!tb[CONF_NAME] || !tb[CONF_PATH])
553 continue;
554
555 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
556 continue;
557
558 found = true;
559 break;
560 }
561
562 if (!found)
563 return ENOENT;
564
565 s = avl_find_element(&runtime, name, s, avl);
566
567 if (!s || !(s->running))
568 return ENOENT;
569
570 blob_buf_init(&req, 0);
571 blobmsg_add_u32(&req, "signal", signal);
572 blobmsg_add_string(&req, "name", name);
573
574 if (asprintf(&objname, "container.%s", name) == -1)
575 return ENOMEM;
576
577 ret = ubus_lookup_id(ctx, objname, &id);
578 free(objname);
579 if (ret)
580 return ENOENT;
581
582 if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
583 return EIO;
584
585 return 0;
586 }
587
588
589 static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile, char *_tmprwsize, char *_writepath, char *requiredmounts)
590 {
591 static struct blob_buf req;
592 struct blob_attr *cur, *tb[__CONF_MAX];
593 int rem, ret;
594 bool found = false;
595 char *fname = NULL;
596 char *keeppath = NULL;
597 char *tmprwsize = _tmprwsize;
598 char *writepath = _writepath;
599 char *curvol, *tmp, *mnttok;
600 void *mntarr;
601 int f;
602 struct stat sb;
603
604 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
605 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
606 if (!tb[CONF_NAME] || !tb[CONF_PATH])
607 continue;
608
609 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
610 continue;
611
612 found = true;
613 break;
614 }
615
616 if (found && add)
617 return EEXIST;
618
619 if (!found && !add)
620 return ENOENT;
621
622 if (add && !path)
623 return EINVAL;
624
625 if (path) {
626 if (stat(path, &sb) == -1)
627 return ENOENT;
628
629 if ((sb.st_mode & S_IFMT) != S_IFDIR)
630 return ENOTDIR;
631 }
632
633 ret = mkdir(UXC_CONFDIR, 0755);
634
635 if (ret && errno != EEXIST)
636 return ret;
637
638 if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) == -1)
639 return ENOMEM;
640
641 f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
642 if (f < 0)
643 return errno;
644
645 if (!add) {
646 keeppath = strdup(blobmsg_get_string(tb[CONF_PATH]));
647 if (tb[CONF_WRITE_OVERLAY_PATH])
648 writepath = strdup(blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]));
649
650 if (tb[CONF_TEMP_OVERLAY_SIZE])
651 tmprwsize = strdup(blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]));
652 }
653
654 blob_buf_init(&req, 0);
655 blobmsg_add_string(&req, "name", name);
656 blobmsg_add_string(&req, "path", path?:keeppath);
657 blobmsg_add_u8(&req, "autostart", autostart);
658 if (pidfile)
659 blobmsg_add_string(&req, "pidfile", pidfile);
660
661 if (tmprwsize)
662 blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
663
664 if (writepath)
665 blobmsg_add_string(&req, "write-overlay-path", writepath);
666
667 if (!add && tb[CONF_VOLUMES])
668 blobmsg_add_blob(&req, tb[CONF_VOLUMES]);
669
670 if (add && requiredmounts) {
671 mntarr = blobmsg_open_array(&req, "volumes");
672 for (mnttok = requiredmounts; ; mnttok = NULL) {
673 curvol = strtok_r(mnttok, ",;", &tmp);
674 if (!curvol)
675 break;
676
677 blobmsg_add_string(&req, NULL, curvol);
678 }
679 blobmsg_close_array(&req, mntarr);
680 }
681
682 dprintf(f, "%s\n", blobmsg_format_json_indent(req.head, true, 0));
683
684 if (!add)
685 free(keeppath);
686
687 blob_buf_free(&req);
688
689 return 0;
690 }
691
692 enum {
693 BLOCK_INFO_DEVICE,
694 BLOCK_INFO_UUID,
695 BLOCK_INFO_TARGET,
696 BLOCK_INFO_TYPE,
697 BLOCK_INFO_MOUNT,
698 __BLOCK_INFO_MAX,
699 };
700
701 static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
702 [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
703 [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
704 [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
705 [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
706 [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
707 };
708
709
710 /* check if device 'devname' is mounted according to blockd */
711 static int checkblock(const char *uuid)
712 {
713 struct blob_attr *tb[__BLOCK_INFO_MAX];
714 struct blob_attr *cur;
715 int rem;
716
717 blobmsg_for_each_attr(cur, blockinfo, rem) {
718 blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
719
720 if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
721 continue;
722
723 if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
724 return 0;
725 }
726
727 return 1;
728 }
729
730 enum {
731 UCI_FSTAB_UUID,
732 UCI_FSTAB_ANONYMOUS,
733 __UCI_FSTAB_MAX,
734 };
735
736 static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
737 [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
738 [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
739 };
740
741 static const char *resolveuuid(const char *volname)
742 {
743 struct blob_attr *tb[__UCI_FSTAB_MAX];
744 struct blob_attr *cur;
745 const char *mntname;
746 char *tmpvolname, *replc;
747 int rem, res;
748
749 blobmsg_for_each_attr(cur, fstabinfo, rem) {
750 blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
751
752 if (!tb[UCI_FSTAB_UUID])
753 continue;
754
755 if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
756 continue;
757
758 mntname = blobmsg_name(cur);
759 if (!mntname)
760 continue;
761
762 tmpvolname = strdup(volname);
763 while ((replc = strchr(tmpvolname, '-')))
764 *replc = '_';
765
766 res = strcmp(tmpvolname, mntname);
767 free(tmpvolname);
768
769 if (!res)
770 return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
771 };
772
773 return volname;
774 };
775
776 /* check status of each required volume */
777 static int checkvolumes(struct blob_attr *volumes)
778 {
779 struct blob_attr *cur;
780 int rem;
781
782 blobmsg_for_each_attr(cur, volumes, rem) {
783 if (checkblock(resolveuuid(blobmsg_get_string(cur))))
784 return 1;
785 }
786
787 return 0;
788 }
789
790 static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
791 {
792 blockinfo = blob_memdup(blobmsg_data(msg));
793 }
794
795 static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
796 {
797 fstabinfo = blob_memdup(blobmsg_data(msg));
798 }
799
800 static int uxc_boot(void)
801 {
802 struct blob_attr *cur, *tb[__CONF_MAX];
803 struct runtime_state *s;
804 static struct blob_buf req;
805 int rem, ret = 0;
806 char *name;
807 unsigned int id;
808
809 ret = ubus_lookup_id(ctx, "block", &id);
810 if (ret)
811 return ENOENT;
812
813 ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
814 if (ret)
815 return ENXIO;
816
817 ret = ubus_lookup_id(ctx, "uci", &id);
818 if (ret)
819 return ENOENT;
820
821 blob_buf_init(&req, 0);
822 blobmsg_add_string(&req, "config", "fstab");
823 blobmsg_add_string(&req, "type", "mount");
824
825 ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
826 if (ret)
827 return ENXIO;
828
829 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
830 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
831 if (!tb[CONF_NAME] || !tb[CONF_PATH] || !tb[CONF_AUTOSTART] || !blobmsg_get_bool(tb[CONF_AUTOSTART]))
832 continue;
833
834 s = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), s, avl);
835 if (s)
836 continue;
837
838 /* make sure all volumes are ready before starting */
839 if (tb[CONF_VOLUMES])
840 if (checkvolumes(tb[CONF_VOLUMES]))
841 continue;
842
843 name = strdup(blobmsg_get_string(tb[CONF_NAME]));
844 ret += uxc_create(name, true);
845 free(name);
846 }
847
848 return ret;
849 }
850
851 static int uxc_delete(char *name, bool force)
852 {
853 struct blob_attr *cur, *tb[__CONF_MAX];
854 struct runtime_state *s = NULL;
855 static struct blob_buf req;
856 uint32_t id;
857 int rem, ret = 0;
858 bool found = false;
859 char *fname;
860 struct stat sb;
861
862 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
863 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
864 if (!tb[CONF_NAME] || !tb[CONF_PATH])
865 continue;
866
867 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
868 continue;
869
870 fname = strdup(blobmsg_name(cur));
871 if (!fname)
872 return errno;
873
874 found = true;
875 break;
876 }
877
878 if (!found)
879 return ENOENT;
880
881 s = avl_find_element(&runtime, name, s, avl);
882
883 if (s && s->running) {
884 if (force) {
885 ret = uxc_kill(name, SIGKILL);
886 if (ret)
887 goto errout;
888
889 } else {
890 ret = EWOULDBLOCK;
891 goto errout;
892 }
893 }
894
895 if (s) {
896 ret = ubus_lookup_id(ctx, "container", &id);
897 if (ret)
898 goto errout;
899
900 blob_buf_init(&req, 0);
901 blobmsg_add_string(&req, "name", s->container_name);
902 blobmsg_add_string(&req, "instance", s->instance_name);
903
904 if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
905 blob_buf_free(&req);
906 ret=EIO;
907 goto errout;
908 }
909 }
910
911 if (stat(fname, &sb) == -1) {
912 ret=ENOENT;
913 goto errout;
914 }
915
916 if (unlink(fname) == -1)
917 ret=errno;
918
919 errout:
920 free(fname);
921 return ret;
922 }
923
924 static void reload_conf(void)
925 {
926 blob_buf_free(&conf);
927 conf_load();
928 }
929
930 int main(int argc, char **argv)
931 {
932 enum uxc_cmd cmd = CMD_UNKNOWN;
933 int ret = EINVAL;
934 char *bundle = NULL;
935 char *pidfile = NULL;
936 char *tmprwsize = NULL;
937 char *writepath = NULL;
938 char *requiredmounts = NULL;
939 bool autostart = false;
940 bool force = false;
941 int signal = SIGTERM;
942 int c;
943
944 if (argc < 2)
945 return usage();
946
947 ctx = ubus_connect(NULL);
948 if (!ctx)
949 return ENODEV;
950
951 ret = conf_load();
952 if (ret)
953 goto out;
954
955 ret = runtime_load();
956 if (ret)
957 goto conf_out;
958
959 while (true) {
960 int option_index = 0;
961 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
962 if (c == -1)
963 break;
964
965 switch (c) {
966 case 'a':
967 autostart = true;
968 break;
969
970 case 'b':
971 bundle = optarg;
972 break;
973
974 case 'f':
975 force = true;
976 break;
977
978 case 'p':
979 pidfile = optarg;
980 break;
981
982 case 't':
983 tmprwsize = optarg;
984 break;
985
986 case 'v':
987 verbose = true;
988 break;
989
990 case 'V':
991 printf("uxc %s\n", UXC_VERSION);
992 exit(0);
993
994 case 'w':
995 writepath = optarg;
996 break;
997
998 case 'm':
999 requiredmounts = optarg;
1000 break;
1001 }
1002 }
1003
1004 if (optind == argc)
1005 goto usage_out;
1006
1007 if (!strcmp("list", argv[optind]))
1008 cmd = CMD_LIST;
1009 else if (!strcmp("boot", argv[optind]))
1010 cmd = CMD_BOOT;
1011 else if(!strcmp("start", argv[optind]))
1012 cmd = CMD_START;
1013 else if(!strcmp("state", argv[optind]))
1014 cmd = CMD_STATE;
1015 else if(!strcmp("kill", argv[optind]))
1016 cmd = CMD_KILL;
1017 else if(!strcmp("enable", argv[optind]))
1018 cmd = CMD_ENABLE;
1019 else if(!strcmp("disable", argv[optind]))
1020 cmd = CMD_DISABLE;
1021 else if(!strcmp("delete", argv[optind]))
1022 cmd = CMD_DELETE;
1023 else if(!strcmp("create", argv[optind]))
1024 cmd = CMD_CREATE;
1025
1026 switch (cmd) {
1027 case CMD_LIST:
1028 ret = uxc_list();
1029 break;
1030
1031 case CMD_BOOT:
1032 ret = uxc_boot();
1033 break;
1034
1035 case CMD_START:
1036 if (optind != argc - 2)
1037 goto usage_out;
1038
1039 ret = uxc_start(argv[optind + 1]);
1040 break;
1041
1042 case CMD_STATE:
1043 if (optind != argc - 2)
1044 goto usage_out;
1045
1046 ret = uxc_state(argv[optind + 1]);
1047 break;
1048
1049 case CMD_KILL:
1050 if (optind == (argc - 3))
1051 signal = atoi(argv[optind + 2]);
1052 else if (optind > argc - 2)
1053 goto usage_out;
1054
1055 ret = uxc_kill(argv[optind + 1], signal);
1056 break;
1057
1058 case CMD_ENABLE:
1059 if (optind != argc - 2)
1060 goto usage_out;
1061
1062 ret = uxc_set(argv[optind + 1], NULL, true, false, NULL, NULL, NULL, NULL);
1063 break;
1064
1065 case CMD_DISABLE:
1066 if (optind != argc - 2)
1067 goto usage_out;
1068
1069 ret = uxc_set(argv[optind + 1], NULL, false, false, NULL, NULL, NULL, NULL);
1070 break;
1071
1072 case CMD_DELETE:
1073 if (optind != argc - 2)
1074 goto usage_out;
1075
1076 ret = uxc_delete(argv[optind + 1], force);
1077 break;
1078
1079 case CMD_CREATE:
1080 if (optind != argc - 2)
1081 goto usage_out;
1082
1083 if (bundle) {
1084 ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile, tmprwsize, writepath, requiredmounts);
1085 if (ret)
1086 goto runtime_out;
1087
1088 reload_conf();
1089 }
1090
1091 ret = uxc_create(argv[optind + 1], false);
1092 break;
1093
1094 default:
1095 goto usage_out;
1096 }
1097
1098 goto runtime_out;
1099
1100 usage_out:
1101 usage();
1102 runtime_out:
1103 runtime_free();
1104 conf_out:
1105 blob_buf_free(&conf);
1106 out:
1107 ubus_free(ctx);
1108
1109 if (ret != 0)
1110 fprintf(stderr, "uxc error: %s\n", strerror(ret));
1111
1112 return ret;
1113 }