uxc: fix a bunch of issues discovered by Coverity
[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 char *state = NULL;
356 static struct blob_buf buf;
357
358 if (s)
359 ocistate = s->ocistate;
360
361 if (ocistate) {
362 state = blobmsg_format_json_indent(ocistate, true, 0);
363 if (!state)
364 return 1;
365
366 printf("%s\n", state);
367 free(state);
368 return 0;
369 }
370
371 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
372 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
373 if (!tb[CONF_NAME] || !tb[CONF_PATH])
374 continue;
375
376 if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) {
377 if (tb[CONF_JAIL])
378 jail_name = blobmsg_get_string(tb[CONF_JAIL]);
379 else
380 jail_name = name;
381
382 bundle = blobmsg_get_string(tb[CONF_PATH]);
383 break;
384 }
385 }
386
387 if (!bundle)
388 return ENOENT;
389
390 blob_buf_init(&buf, 0);
391 blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING);
392 blobmsg_add_string(&buf, "id", jail_name);
393 blobmsg_add_string(&buf, "status", s?"stopped":"uninitialized");
394 blobmsg_add_string(&buf, "bundle", bundle);
395
396 printf("%s\n", blobmsg_format_json_indent(buf.head, true, 0));
397 blob_buf_free(&buf);
398
399 return 0;
400 }
401
402 static int uxc_list(void)
403 {
404 struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
405 int rem;
406 struct runtime_state *s = NULL;
407 char *name;
408 char *ocistatus;
409 int container_pid = -1;
410 bool autostart;
411
412 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
413 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
414 if (!tb[CONF_NAME] || !tb[CONF_PATH])
415 continue;
416
417 autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]);
418 ocistatus = NULL;
419 container_pid = 0;
420 name = blobmsg_get_string(tb[CONF_NAME]);
421 s = avl_find_element(&runtime, name, s, avl);
422
423 if (s && s->ocistate) {
424 blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(s->ocistate), blobmsg_len(s->ocistate));
425 ocistatus = blobmsg_get_string(ts[STATE_STATUS]);
426 container_pid = blobmsg_get_u32(ts[STATE_PID]);
427 }
428
429 printf("[%c] %s %s", autostart?'*':' ', name, ocistatus?:(s && s->running)?"creating":"stopped");
430
431 if (s && !s->running && (s->exitcode >= 0))
432 printf(" exitcode: %d (%s)", s->exitcode, strerror(s->exitcode));
433
434 if (s && s->running && (s->runtime_pid >= 0))
435 printf(" runtime pid: %d", s->runtime_pid);
436
437 if (s && s->running && (container_pid >= 0))
438 printf(" container pid: %d", container_pid);
439
440 printf("\n");
441 }
442
443 return 0;
444 }
445
446 static int uxc_create(char *name, bool immediately)
447 {
448 static struct blob_buf req;
449 struct blob_attr *cur, *tb[__CONF_MAX];
450 int rem, ret;
451 uint32_t id;
452 struct runtime_state *s = NULL;
453 char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
454
455 void *in, *ins, *j;
456 bool found = false;
457
458 s = avl_find_element(&runtime, name, s, avl);
459
460 if (s && (s->running))
461 return EEXIST;
462
463 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
464 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
465 if (!tb[CONF_NAME] || !tb[CONF_PATH])
466 continue;
467
468 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
469 continue;
470
471 found = true;
472 }
473
474 if (!found)
475 return ENOENT;
476
477 path = blobmsg_get_string(tb[CONF_PATH]);
478
479 if (tb[CONF_PIDFILE])
480 pidfile = blobmsg_get_string(tb[CONF_PIDFILE]);
481
482 if (tb[CONF_TEMP_OVERLAY_SIZE])
483 tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
484
485 if (tb[CONF_WRITE_OVERLAY_PATH])
486 writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
487
488
489 if (tb[CONF_JAIL])
490 jailname = blobmsg_get_string(tb[CONF_JAIL]);
491
492 blob_buf_init(&req, 0);
493 blobmsg_add_string(&req, "name", name);
494 ins = blobmsg_open_table(&req, "instances");
495 in = blobmsg_open_table(&req, name);
496 blobmsg_add_string(&req, "bundle", path);
497 j = blobmsg_open_table(&req, "jail");
498 blobmsg_add_string(&req, "name", jailname?:name);
499 blobmsg_add_u8(&req, "immediately", immediately);
500
501 if (pidfile)
502 blobmsg_add_string(&req, "pidfile", pidfile);
503
504 blobmsg_close_table(&req, j);
505
506 if (writepath)
507 blobmsg_add_string(&req, "overlaydir", writepath);
508
509 if (tmprwsize)
510 blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize);
511
512 blobmsg_close_table(&req, in);
513 blobmsg_close_table(&req, ins);
514
515 if (verbose) {
516 char *tmp;
517 tmp = blobmsg_format_json_indent(req.head, true, 1);
518 if (!tmp)
519 return ENOMEM;
520
521 fprintf(stderr, "adding container to procd:\n\t%s\n", tmp);
522 free(tmp);
523 }
524
525 ret = 0;
526 if (ubus_lookup_id(ctx, "container", &id) ||
527 ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
528 ret = EIO;
529 }
530
531 free(jailname);
532 free(path);
533 blob_buf_free(&req);
534
535 return ret;
536 }
537
538 static int uxc_start(const char *name)
539 {
540 char *objname;
541 unsigned int id;
542
543 if (asprintf(&objname, "container.%s", name) == -1)
544 return ENOMEM;
545
546 if (ubus_lookup_id(ctx, objname, &id))
547 return ENOENT;
548
549 return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000);
550 }
551
552 static int uxc_kill(char *name, int signal)
553 {
554 static struct blob_buf req;
555 struct blob_attr *cur, *tb[__CONF_MAX];
556 int rem, ret;
557 char *objname;
558 unsigned int id;
559 struct runtime_state *s = NULL;
560 bool found = false;
561
562 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
563 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
564 if (!tb[CONF_NAME] || !tb[CONF_PATH])
565 continue;
566
567 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
568 continue;
569
570 found = true;
571 break;
572 }
573
574 if (!found)
575 return ENOENT;
576
577 s = avl_find_element(&runtime, name, s, avl);
578
579 if (!s || !(s->running))
580 return ENOENT;
581
582 blob_buf_init(&req, 0);
583 blobmsg_add_u32(&req, "signal", signal);
584 blobmsg_add_string(&req, "name", name);
585
586 if (asprintf(&objname, "container.%s", name) == -1)
587 return ENOMEM;
588
589 ret = ubus_lookup_id(ctx, objname, &id);
590 free(objname);
591 if (ret)
592 return ENOENT;
593
594 if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
595 return EIO;
596
597 return 0;
598 }
599
600
601 static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile, char *_tmprwsize, char *_writepath, char *requiredmounts)
602 {
603 static struct blob_buf req;
604 struct blob_attr *cur, *tb[__CONF_MAX];
605 int rem, ret;
606 bool found = false;
607 char *fname = NULL;
608 char *keeppath = NULL;
609 char *tmprwsize = _tmprwsize;
610 char *writepath = _writepath;
611 char *curvol, *tmp, *mnttok;
612 void *mntarr;
613 int f;
614 struct stat sb;
615
616 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
617 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
618 if (!tb[CONF_NAME] || !tb[CONF_PATH])
619 continue;
620
621 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
622 continue;
623
624 found = true;
625 break;
626 }
627
628 if (found && add)
629 return EEXIST;
630
631 if (!found && !add)
632 return ENOENT;
633
634 if (add && !path)
635 return EINVAL;
636
637 if (path) {
638 if (stat(path, &sb) == -1)
639 return ENOENT;
640
641 if ((sb.st_mode & S_IFMT) != S_IFDIR)
642 return ENOTDIR;
643 }
644
645 ret = mkdir(UXC_CONFDIR, 0755);
646
647 if (ret && errno != EEXIST)
648 return ret;
649
650 if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) == -1)
651 return ENOMEM;
652
653 f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
654 if (f < 0)
655 return errno;
656
657 if (!add) {
658 keeppath = blobmsg_get_string(tb[CONF_PATH]);
659 if (tb[CONF_WRITE_OVERLAY_PATH])
660 writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
661
662 if (tb[CONF_TEMP_OVERLAY_SIZE])
663 tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
664 }
665
666 blob_buf_init(&req, 0);
667 blobmsg_add_string(&req, "name", name);
668 blobmsg_add_string(&req, "path", path?:keeppath);
669 blobmsg_add_u8(&req, "autostart", autostart);
670 if (pidfile)
671 blobmsg_add_string(&req, "pidfile", pidfile);
672
673 if (tmprwsize)
674 blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
675
676 if (writepath)
677 blobmsg_add_string(&req, "write-overlay-path", writepath);
678
679 if (!add && tb[CONF_VOLUMES])
680 blobmsg_add_blob(&req, tb[CONF_VOLUMES]);
681
682 if (add && requiredmounts) {
683 mntarr = blobmsg_open_array(&req, "volumes");
684 for (mnttok = requiredmounts; ; mnttok = NULL) {
685 curvol = strtok_r(mnttok, ",;", &tmp);
686 if (!curvol)
687 break;
688
689 blobmsg_add_string(&req, NULL, curvol);
690 }
691 blobmsg_close_array(&req, mntarr);
692 }
693 tmp = blobmsg_format_json_indent(req.head, true, 0);
694 if (tmp) {
695 dprintf(f, "%s\n", tmp);
696 free(tmp);
697 }
698
699 blob_buf_free(&req);
700
701 return 0;
702 }
703
704 enum {
705 BLOCK_INFO_DEVICE,
706 BLOCK_INFO_UUID,
707 BLOCK_INFO_TARGET,
708 BLOCK_INFO_TYPE,
709 BLOCK_INFO_MOUNT,
710 __BLOCK_INFO_MAX,
711 };
712
713 static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
714 [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
715 [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
716 [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
717 [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
718 [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
719 };
720
721
722 /* check if device 'devname' is mounted according to blockd */
723 static int checkblock(const char *uuid)
724 {
725 struct blob_attr *tb[__BLOCK_INFO_MAX];
726 struct blob_attr *cur;
727 int rem;
728
729 blobmsg_for_each_attr(cur, blockinfo, rem) {
730 blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
731
732 if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
733 continue;
734
735 if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
736 return 0;
737 }
738
739 return 1;
740 }
741
742 enum {
743 UCI_FSTAB_UUID,
744 UCI_FSTAB_ANONYMOUS,
745 __UCI_FSTAB_MAX,
746 };
747
748 static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
749 [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
750 [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
751 };
752
753 static const char *resolveuuid(const char *volname)
754 {
755 struct blob_attr *tb[__UCI_FSTAB_MAX];
756 struct blob_attr *cur;
757 const char *mntname;
758 char *tmpvolname, *replc;
759 int rem, res;
760
761 blobmsg_for_each_attr(cur, fstabinfo, rem) {
762 blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
763
764 if (!tb[UCI_FSTAB_UUID])
765 continue;
766
767 if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
768 continue;
769
770 mntname = blobmsg_name(cur);
771 if (!mntname)
772 continue;
773
774 tmpvolname = strdup(volname);
775 while ((replc = strchr(tmpvolname, '-')))
776 *replc = '_';
777
778 res = strcmp(tmpvolname, mntname);
779 free(tmpvolname);
780
781 if (!res)
782 return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
783 };
784
785 return volname;
786 };
787
788 /* check status of each required volume */
789 static int checkvolumes(struct blob_attr *volumes)
790 {
791 struct blob_attr *cur;
792 int rem;
793
794 blobmsg_for_each_attr(cur, volumes, rem) {
795 if (checkblock(resolveuuid(blobmsg_get_string(cur))))
796 return 1;
797 }
798
799 return 0;
800 }
801
802 static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
803 {
804 blockinfo = blob_memdup(blobmsg_data(msg));
805 }
806
807 static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
808 {
809 fstabinfo = blob_memdup(blobmsg_data(msg));
810 }
811
812 static int uxc_boot(void)
813 {
814 struct blob_attr *cur, *tb[__CONF_MAX];
815 struct runtime_state *s;
816 static struct blob_buf req;
817 int rem, ret = 0;
818 char *name;
819 unsigned int id;
820
821 ret = ubus_lookup_id(ctx, "block", &id);
822 if (ret)
823 return ENOENT;
824
825 ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
826 if (ret)
827 return ENXIO;
828
829 ret = ubus_lookup_id(ctx, "uci", &id);
830 if (ret)
831 return ENOENT;
832
833 blob_buf_init(&req, 0);
834 blobmsg_add_string(&req, "config", "fstab");
835 blobmsg_add_string(&req, "type", "mount");
836
837 ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
838 if (ret)
839 return ENXIO;
840
841 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
842 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
843 if (!tb[CONF_NAME] || !tb[CONF_PATH] || !tb[CONF_AUTOSTART] || !blobmsg_get_bool(tb[CONF_AUTOSTART]))
844 continue;
845
846 s = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), s, avl);
847 if (s)
848 continue;
849
850 /* make sure all volumes are ready before starting */
851 if (tb[CONF_VOLUMES])
852 if (checkvolumes(tb[CONF_VOLUMES]))
853 continue;
854
855 name = strdup(blobmsg_get_string(tb[CONF_NAME]));
856 ret += uxc_create(name, true);
857 free(name);
858 }
859
860 return ret;
861 }
862
863 static int uxc_delete(char *name, bool force)
864 {
865 struct blob_attr *cur, *tb[__CONF_MAX];
866 struct runtime_state *s = NULL;
867 static struct blob_buf req;
868 uint32_t id;
869 int rem, ret = 0;
870 bool found = false;
871 char *fname;
872 struct stat sb;
873
874 blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
875 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
876 if (!tb[CONF_NAME] || !tb[CONF_PATH])
877 continue;
878
879 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
880 continue;
881
882 fname = strdup(blobmsg_name(cur));
883 if (!fname)
884 return errno;
885
886 found = true;
887 break;
888 }
889
890 if (!found)
891 return ENOENT;
892
893 s = avl_find_element(&runtime, name, s, avl);
894
895 if (s && s->running) {
896 if (force) {
897 ret = uxc_kill(name, SIGKILL);
898 if (ret)
899 goto errout;
900
901 } else {
902 ret = EWOULDBLOCK;
903 goto errout;
904 }
905 }
906
907 if (s) {
908 ret = ubus_lookup_id(ctx, "container", &id);
909 if (ret)
910 goto errout;
911
912 blob_buf_init(&req, 0);
913 blobmsg_add_string(&req, "name", s->container_name);
914 blobmsg_add_string(&req, "instance", s->instance_name);
915
916 if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
917 blob_buf_free(&req);
918 ret=EIO;
919 goto errout;
920 }
921 }
922
923 if (stat(fname, &sb) == -1) {
924 ret=ENOENT;
925 goto errout;
926 }
927
928 if (unlink(fname) == -1)
929 ret=errno;
930
931 errout:
932 free(fname);
933 return ret;
934 }
935
936 static void reload_conf(void)
937 {
938 blob_buf_free(&conf);
939 conf_load();
940 }
941
942 int main(int argc, char **argv)
943 {
944 enum uxc_cmd cmd = CMD_UNKNOWN;
945 int ret = EINVAL;
946 char *bundle = NULL;
947 char *pidfile = NULL;
948 char *tmprwsize = NULL;
949 char *writepath = NULL;
950 char *requiredmounts = NULL;
951 bool autostart = false;
952 bool force = false;
953 int signal = SIGTERM;
954 int c;
955
956 if (argc < 2)
957 return usage();
958
959 ctx = ubus_connect(NULL);
960 if (!ctx)
961 return ENODEV;
962
963 ret = conf_load();
964 if (ret)
965 goto out;
966
967 ret = runtime_load();
968 if (ret)
969 goto conf_out;
970
971 while (true) {
972 int option_index = 0;
973 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
974 if (c == -1)
975 break;
976
977 switch (c) {
978 case 'a':
979 autostart = true;
980 break;
981
982 case 'b':
983 bundle = optarg;
984 break;
985
986 case 'f':
987 force = true;
988 break;
989
990 case 'p':
991 pidfile = optarg;
992 break;
993
994 case 't':
995 tmprwsize = optarg;
996 break;
997
998 case 'v':
999 verbose = true;
1000 break;
1001
1002 case 'V':
1003 printf("uxc %s\n", UXC_VERSION);
1004 exit(0);
1005
1006 case 'w':
1007 writepath = optarg;
1008 break;
1009
1010 case 'm':
1011 requiredmounts = optarg;
1012 break;
1013 }
1014 }
1015
1016 if (optind == argc)
1017 goto usage_out;
1018
1019 if (!strcmp("list", argv[optind]))
1020 cmd = CMD_LIST;
1021 else if (!strcmp("boot", argv[optind]))
1022 cmd = CMD_BOOT;
1023 else if(!strcmp("start", argv[optind]))
1024 cmd = CMD_START;
1025 else if(!strcmp("state", argv[optind]))
1026 cmd = CMD_STATE;
1027 else if(!strcmp("kill", argv[optind]))
1028 cmd = CMD_KILL;
1029 else if(!strcmp("enable", argv[optind]))
1030 cmd = CMD_ENABLE;
1031 else if(!strcmp("disable", argv[optind]))
1032 cmd = CMD_DISABLE;
1033 else if(!strcmp("delete", argv[optind]))
1034 cmd = CMD_DELETE;
1035 else if(!strcmp("create", argv[optind]))
1036 cmd = CMD_CREATE;
1037
1038 switch (cmd) {
1039 case CMD_LIST:
1040 ret = uxc_list();
1041 break;
1042
1043 case CMD_BOOT:
1044 ret = uxc_boot();
1045 break;
1046
1047 case CMD_START:
1048 if (optind != argc - 2)
1049 goto usage_out;
1050
1051 ret = uxc_start(argv[optind + 1]);
1052 break;
1053
1054 case CMD_STATE:
1055 if (optind != argc - 2)
1056 goto usage_out;
1057
1058 ret = uxc_state(argv[optind + 1]);
1059 break;
1060
1061 case CMD_KILL:
1062 if (optind == (argc - 3))
1063 signal = atoi(argv[optind + 2]);
1064 else if (optind > argc - 2)
1065 goto usage_out;
1066
1067 ret = uxc_kill(argv[optind + 1], signal);
1068 break;
1069
1070 case CMD_ENABLE:
1071 if (optind != argc - 2)
1072 goto usage_out;
1073
1074 ret = uxc_set(argv[optind + 1], NULL, true, false, NULL, NULL, NULL, NULL);
1075 break;
1076
1077 case CMD_DISABLE:
1078 if (optind != argc - 2)
1079 goto usage_out;
1080
1081 ret = uxc_set(argv[optind + 1], NULL, false, false, NULL, NULL, NULL, NULL);
1082 break;
1083
1084 case CMD_DELETE:
1085 if (optind != argc - 2)
1086 goto usage_out;
1087
1088 ret = uxc_delete(argv[optind + 1], force);
1089 break;
1090
1091 case CMD_CREATE:
1092 if (optind != argc - 2)
1093 goto usage_out;
1094
1095 if (bundle) {
1096 ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile, tmprwsize, writepath, requiredmounts);
1097 if (ret)
1098 goto runtime_out;
1099
1100 reload_conf();
1101 }
1102
1103 ret = uxc_create(argv[optind + 1], false);
1104 break;
1105
1106 default:
1107 goto usage_out;
1108 }
1109
1110 goto runtime_out;
1111
1112 usage_out:
1113 usage();
1114 runtime_out:
1115 runtime_free();
1116 conf_out:
1117 blob_buf_free(&conf);
1118 out:
1119 ubus_free(ctx);
1120
1121 if (ret != 0)
1122 fprintf(stderr, "uxc error: %s\n", strerror(ret));
1123
1124 return ret;
1125 }