• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt