• 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 <errno.h>
 19 #include <fcntl.h>
 20 #include <getopt.h>
 21 #include <glob.h>
 22 #include <stdlib.h>
 23 #include <stdbool.h>
 24 #include <stdio.h>
 25 #include <signal.h>
 26 #include <termios.h>
 27 #include <unistd.h>
 28 #include <sys/stat.h>
 29 #include <sys/types.h>
 30 
 31 #include <libubus.h>
 32 #include <libubox/avl-cmp.h>
 33 #include <libubox/blobmsg.h>
 34 #include <libubox/blobmsg_json.h>
 35 #include <libubox/ustream.h>
 36 
 37 #include "log.h"
 38 
 39 #define UXC_VERSION "0.3"
 40 #define OCI_VERSION_STRING "1.0.2"
 41 #define UXC_ETC_CONFDIR "/etc/uxc"
 42 #define UXC_VOL_CONFDIR "/tmp/run/uvol/.meta/uxc"
 43 
 44 static bool verbose = false;
 45 static bool json_output = false;
 46 static char *confdir = UXC_ETC_CONFDIR;
 47 static struct ustream_fd cufd;
 48 static struct ustream_fd lufd;
 49 
 50 
 51 struct runtime_state {
 52         struct avl_node avl;
 53         char *container_name;
 54         char *instance_name;
 55         char *jail_name;
 56         bool running;
 57         int runtime_pid;
 58         int exitcode;
 59         struct blob_attr *ocistate;
 60 };
 61 
 62 struct settings {
 63         struct avl_node avl;
 64         char *container_name;
 65         const char *fname;
 66         char *tmprwsize;
 67         char *writepath;
 68         signed char autostart;
 69         struct blob_attr *volumes;
 70 };
 71 
 72 enum uxc_cmd {
 73         CMD_ATTACH,
 74         CMD_LIST,
 75         CMD_BOOT,
 76         CMD_START,
 77         CMD_STATE,
 78         CMD_KILL,
 79         CMD_ENABLE,
 80         CMD_DISABLE,
 81         CMD_DELETE,
 82         CMD_CREATE,
 83         CMD_UNKNOWN
 84 };
 85 
 86 #define OPT_ARGS "ab:fjm:p:t:vVw:"
 87 static struct option long_options[] = {
 88         {"autostart",           no_argument,            0,      'a'     },
 89         {"console",             no_argument,            0,      'c'     },
 90         {"bundle",              required_argument,      0,      'b'     },
 91         {"force",               no_argument,            0,      'f'     },
 92         {"json",                no_argument,            0,      'j'     },
 93         {"mounts",              required_argument,      0,      'm'     },
 94         {"pid-file",            required_argument,      0,      'p'     },
 95         {"temp-overlay-size",   required_argument,      0,      't'     },
 96         {"write-overlay-path",  required_argument,      0,      'w'     },
 97         {"verbose",             no_argument,            0,      'v'     },
 98         {"version",             no_argument,            0,      'V'     },
 99         {0,                     0,                      0,      0       }
100 };
101 
102 AVL_TREE(runtime, avl_strcmp, false, NULL);
103 AVL_TREE(settings, avl_strcmp, false, NULL);
104 static struct blob_buf conf;
105 static struct blob_buf settingsbuf;
106 static struct blob_attr *blockinfo;
107 static struct blob_attr *fstabinfo;
108 static struct ubus_context *ctx;
109 
110 static int usage(void) {
111         printf("syntax: uxc <command> [parameters ...]\n");
112         printf("commands:\n");
113         printf("\tlist [--json]\t\t\t\tlist all configured containers\n");
114         printf("\tattach <conf>\t\t\t\tattach to container console\n");
115         printf("\tcreate <conf>\t\t\t\t(re-)create <conf>\n");
116         printf("\t\t[--bundle <path>]\t\t\tOCI bundle at <path>\n");
117         printf("\t\t[--autostart]\t\t\t\tstart on boot\n");
118         printf("\t\t[--temp-overlay-size <size>]\t\tuse tmpfs overlay with {size}\n");
119         printf("\t\t[--write-overlay-path <path>]\t\tuse overlay on {path}\n");
120         printf("\t\t[--mounts <v1>,<v2>,...,<vN>]\t\trequire filesystems to be available\n");
121         printf("\tstart [--console] <conf>\t\tstart container <conf>\n");
122         printf("\tstate <conf>\t\t\t\tget state of container <conf>\n");
123         printf("\tkill <conf> [<signal>]\t\t\tsend signal to container <conf>\n");
124         printf("\tenable <conf>\t\t\t\tstart container <conf> on boot\n");
125         printf("\tdisable <conf>\t\t\t\tdon't start container <conf> on boot\n");
126         printf("\tdelete <conf> [--force]\t\t\tdelete <conf>\n");
127         return -EINVAL;
128 }
129 
130 enum {
131         CONF_NAME,
132         CONF_PATH,
133         CONF_JAIL,
134         CONF_AUTOSTART,
135         CONF_PIDFILE,
136         CONF_TEMP_OVERLAY_SIZE,
137         CONF_WRITE_OVERLAY_PATH,
138         CONF_VOLUMES,
139         __CONF_MAX,
140 };
141 
142 static const struct blobmsg_policy conf_policy[__CONF_MAX] = {
143         [CONF_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
144         [CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
145         [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
146         [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL },
147         [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING },
148         [CONF_TEMP_OVERLAY_SIZE] = { .name = "temp-overlay-size", .type = BLOBMSG_TYPE_STRING },
149         [CONF_WRITE_OVERLAY_PATH] = { .name = "write-overlay-path", .type = BLOBMSG_TYPE_STRING },
150         [CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY },
151 };
152 
153 static int conf_load(bool load_settings)
154 {
155         int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
156         int j, res;
157         glob_t gl;
158         char *globstr;
159         void *c, *o;
160         struct stat sb;
161         struct blob_buf *target;
162 
163         if (asprintf(&globstr, "%s/%s*.json", UXC_ETC_CONFDIR, load_settings?"settings/":"") == -1)
164                 return -ENOMEM;
165 
166         res = glob(globstr, gl_flags, NULL, &gl);
167         if (res == 0)
168                 gl_flags |= GLOB_APPEND;
169 
170         free(globstr);
171 
172         if (!stat(UXC_VOL_CONFDIR, &sb)) {
173                 if (sb.st_mode & S_IFDIR) {
174                         if (asprintf(&globstr, "%s/%s*.json",  UXC_VOL_CONFDIR, load_settings?"settings/":"") == -1)
175                                 return -ENOMEM;
176 
177                         res = glob(globstr, gl_flags, NULL, &gl);
178                         free(globstr);
179                 }
180         }
181 
182         target = load_settings ? &settingsbuf : &conf;
183         blob_buf_init(target, 0);
184         c = blobmsg_open_table(target, NULL);
185 
186         if (res < 0)
187                 return 0;
188 
189         for (j = 0; j < gl.gl_pathc; j++) {
190                 o = blobmsg_open_table(target, strdup(gl.gl_pathv[j]));
191                 if (!blobmsg_add_json_from_file(target, gl.gl_pathv[j])) {
192                         ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]);
193                         continue;
194                 }
195                 blobmsg_close_table(target, o);
196         }
197         blobmsg_close_table(target, c);
198         globfree(&gl);
199 
200         return 0;
201 }
202 
203 static struct settings *
204 settings_alloc(const char *container_name)
205 {
206         struct settings *s;
207         char *new_name;
208         s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
209         strcpy(new_name, container_name);
210         s->container_name = new_name;
211         s->avl.key = s->container_name;
212         s->autostart = -1;
213         s->tmprwsize = NULL;
214         s->writepath = NULL;
215         s->volumes = NULL;
216         return s;
217 }
218 
219 static int settings_add(void)
220 {
221         struct blob_attr *cur, *tb[__CONF_MAX];
222         struct settings *s;
223         int rem, err;
224 
225         avl_init(&settings, avl_strcmp, false, NULL);
226 
227         blobmsg_for_each_attr(cur, blob_data(settingsbuf.head), rem) {
228                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
229                 if (!tb[CONF_NAME])
230                         continue;
231 
232                 if (tb[CONF_TEMP_OVERLAY_SIZE] && tb[CONF_WRITE_OVERLAY_PATH])
233                         return -EINVAL;
234 
235                 s = settings_alloc(blobmsg_get_string(tb[CONF_NAME]));
236 
237                 if (tb[CONF_AUTOSTART])
238                         s->autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]);
239 
240                 if (tb[CONF_TEMP_OVERLAY_SIZE])
241                         s->tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
242 
243                 if (tb[CONF_WRITE_OVERLAY_PATH])
244                         s->writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
245 
246                 s->volumes = tb[CONF_VOLUMES];
247                 s->fname = blobmsg_name(cur);
248 
249                 err = avl_insert(&settings, &s->avl);
250                 if (err) {
251                         fprintf(stderr, "error adding settings for %s\n", blobmsg_get_string(tb[CONF_NAME]));
252                         free(s);
253                 }
254         }
255 
256         return 0;
257 }
258 
259 static void settings_free(void)
260 {
261         struct settings *item, *tmp;
262 
263         avl_for_each_element_safe(&settings, item, avl, tmp) {
264                 avl_delete(&settings, &item->avl);
265                 free(item);
266         }
267 
268         return;
269 }
270 
271 enum {
272         LIST_INSTANCES,
273         __LIST_MAX,
274 };
275 
276 static const struct blobmsg_policy list_policy[__LIST_MAX] = {
277         [LIST_INSTANCES] = { .name = "instances", .type = BLOBMSG_TYPE_TABLE },
278 };
279 
280 enum {
281         INSTANCE_RUNNING,
282         INSTANCE_PID,
283         INSTANCE_EXITCODE,
284         INSTANCE_JAIL,
285         __INSTANCE_MAX,
286 };
287 
288 static const struct blobmsg_policy instance_policy[__INSTANCE_MAX] = {
289         [INSTANCE_RUNNING] = { .name = "running", .type = BLOBMSG_TYPE_BOOL },
290         [INSTANCE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
291         [INSTANCE_EXITCODE] = { .name = "exit_code", .type = BLOBMSG_TYPE_INT32 },
292         [INSTANCE_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_TABLE },
293 };
294 
295 enum {
296         JAIL_NAME,
297         __JAIL_MAX,
298 };
299 
300 static const struct blobmsg_policy jail_policy[__JAIL_MAX] = {
301         [JAIL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
302 };
303 
304 static struct runtime_state *
305 runtime_alloc(const char *container_name)
306 {
307         struct runtime_state *s;
308         char *new_name;
309         s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
310         strcpy(new_name, container_name);
311         s->container_name = new_name;
312         s->avl.key = s->container_name;
313         return s;
314 }
315 
316 enum {
317         STATE_OCIVERSION,
318         STATE_ID,
319         STATE_STATUS,
320         STATE_PID,
321         STATE_BUNDLE,
322         STATE_ANNOTATIONS,
323         __STATE_MAX,
324 };
325 
326 static const struct blobmsg_policy state_policy[__STATE_MAX] = {
327         [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING },
328         [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING },
329         [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
330         [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
331         [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING },
332         [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE },
333 };
334 
335 
336 static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg)
337 {
338         struct blob_attr **ocistate = (struct blob_attr **)req->priv;
339         struct blob_attr *tb[__STATE_MAX];
340 
341         blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
342 
343         if (!tb[STATE_OCIVERSION] ||
344             !tb[STATE_ID] ||
345             !tb[STATE_STATUS] ||
346             !tb[STATE_BUNDLE])
347                 return;
348 
349         *ocistate = blob_memdup(msg);
350 }
351 
352 static void get_ocistate(struct blob_attr **ocistate, const char *name)
353 {
354         char *objname;
355         unsigned int id;
356         int ret;
357         *ocistate = NULL;
358 
359         if (asprintf(&objname, "container.%s", name) == -1)
360                 exit(ENOMEM);
361 
362         ret = ubus_lookup_id(ctx, objname, &id);
363         free(objname);
364         if (ret)
365                 return;
366 
367         ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000);
368 }
369 
370 static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg)
371 {
372         struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX];
373         int rem, remi;
374         const char *container_name, *instance_name, *jail_name;
375         bool running;
376         int pid, exitcode;
377         struct runtime_state *rs;
378 
379         blobmsg_for_each_attr(cur, msg, rem) {
380                 container_name = blobmsg_name(cur);
381                 blobmsg_parse(list_policy, __LIST_MAX, tl, blobmsg_data(cur), blobmsg_len(cur));
382                 if (!tl[LIST_INSTANCES])
383                         continue;
384 
385                 blobmsg_for_each_attr(curi, tl[LIST_INSTANCES], remi) {
386                         instance_name = blobmsg_name(curi);
387                         blobmsg_parse(instance_policy, __INSTANCE_MAX, ti, blobmsg_data(curi), blobmsg_len(curi));
388 
389                         if (!ti[INSTANCE_JAIL])
390                                 continue;
391 
392                         blobmsg_parse(jail_policy, __JAIL_MAX, tj, blobmsg_data(ti[INSTANCE_JAIL]), blobmsg_len(ti[INSTANCE_JAIL]));
393                         if (!tj[JAIL_NAME])
394                                 continue;
395 
396                         jail_name = blobmsg_get_string(tj[JAIL_NAME]);
397 
398                         running = ti[INSTANCE_RUNNING] && blobmsg_get_bool(ti[INSTANCE_RUNNING]);
399 
400                         if (ti[INSTANCE_PID])
401                                 pid = blobmsg_get_u32(ti[INSTANCE_PID]);
402                         else
403                                 pid = -1;
404 
405                         if (ti[INSTANCE_EXITCODE])
406                                 exitcode = blobmsg_get_u32(ti[INSTANCE_EXITCODE]);
407                         else
408                                 exitcode = -1;
409 
410                         rs = runtime_alloc(container_name);
411                         rs->instance_name = strdup(instance_name);
412                         rs->jail_name = strdup(jail_name);
413                         rs->runtime_pid = pid;
414                         rs->exitcode = exitcode;
415                         rs->running = running;
416                         avl_insert(&runtime, &rs->avl);
417                 }
418         }
419 
420         return;
421 }
422 
423 static int runtime_load(void)
424 {
425         struct runtime_state *item, *tmp;
426         uint32_t id;
427 
428         avl_init(&runtime, avl_strcmp, false, NULL);
429         if (ubus_lookup_id(ctx, "container", &id) ||
430                 ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000))
431                 return -EIO;
432 
433         avl_for_each_element_safe(&runtime, item, avl, tmp)
434                 get_ocistate(&item->ocistate, item->jail_name);
435 
436         return 0;
437 }
438 
439 static void runtime_free(void)
440 {
441         struct runtime_state *item, *tmp;
442 
443         avl_for_each_element_safe(&runtime, item, avl, tmp) {
444                 avl_delete(&runtime, &item->avl);
445                 free(item->instance_name);
446                 free(item->jail_name);
447                 free(item->ocistate);
448                 free(item);
449         }
450 
451         return;
452 }
453 
454 static inline int setup_tios(int fd, struct termios *oldtios)
455 {
456         struct termios newtios;
457 
458         if (!isatty(fd)) {
459                 return -EIO;
460         }
461 
462         /* Get current termios */
463         if (tcgetattr(fd, oldtios) < 0)
464                 return -errno;
465 
466         newtios = *oldtios;
467 
468         /* We use the same settings that ssh does. */
469         newtios.c_iflag |= IGNPAR;
470         newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
471         newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
472         newtios.c_oflag &= ~ONLCR;
473         newtios.c_oflag |= OPOST;
474         newtios.c_cc[VMIN] = 1;
475         newtios.c_cc[VTIME] = 0;
476 
477         /* Set new attributes */
478         if (tcsetattr(fd, TCSAFLUSH, &newtios) < 0)
479                 return -errno;
480 
481         return 0;
482 }
483 
484 
485 static void client_cb(struct ustream *s, int bytes)
486 {
487         char *buf;
488         int len, rv;
489 
490         do {
491                 buf = ustream_get_read_buf(s, &len);
492                 if (!buf)
493                         break;
494 
495                 rv = ustream_write(&lufd.stream, buf, len, false);
496 
497                 if (rv > 0)
498                         ustream_consume(s, rv);
499 
500                 if (rv <= len)
501                         break;
502         } while(1);
503 }
504 
505 static void local_cb(struct ustream *s, int bytes)
506 {
507         char *buf;
508         int len, rv;
509 
510         do {
511                 buf = ustream_get_read_buf(s, &len);
512                 if (!buf)
513                         break;
514 
515                 if ((len > 0) && (buf[0] == 2))
516                                 uloop_end();
517 
518                 rv = ustream_write(&cufd.stream, buf, len, false);
519 
520                 if (rv > 0)
521                         ustream_consume(s, rv);
522 
523                 if (rv <= len)
524                         break;
525         } while(1);
526 }
527 
528 static int uxc_attach(const char *container_name)
529 {
530         struct ubus_context *ctx;
531         uint32_t id;
532         static struct blob_buf req;
533         int client_fd, server_fd, tty_fd;
534         struct termios oldtermios;
535 
536         ctx = ubus_connect(NULL);
537         if (!ctx) {
538                 fprintf(stderr, "can't connect to ubus!\n");
539                 return -ECONNREFUSED;
540         }
541 
542         /* open pseudo-terminal pair */
543         client_fd = posix_openpt(O_RDWR | O_NOCTTY);
544         if (client_fd < 0) {
545                 fprintf(stderr, "can't create virtual console!\n");
546                 ubus_free(ctx);
547                 return -EIO;
548         }
549         setup_tios(client_fd, &oldtermios);
550         grantpt(client_fd);
551         unlockpt(client_fd);
552         server_fd = open(ptsname(client_fd), O_RDWR | O_NOCTTY);
553         if (server_fd < 0) {
554                 fprintf(stderr, "can't open virtual console!\n");
555                 close(client_fd);
556                 ubus_free(ctx);
557                 return -EIO;
558         }
559         setup_tios(server_fd, &oldtermios);
560 
561         tty_fd = open("/dev/tty", O_RDWR);
562         if (tty_fd < 0) {
563                 fprintf(stderr, "can't open local console!\n");
564                 close(server_fd);
565                 close(client_fd);
566                 ubus_free(ctx);
567                 return -EIO;
568         }
569         setup_tios(tty_fd, &oldtermios);
570 
571         /* register server-side with procd */
572         blob_buf_init(&req, 0);
573         blobmsg_add_string(&req, "name", container_name);
574         blobmsg_add_string(&req, "instance", container_name);
575 
576         if (ubus_lookup_id(ctx, "container", &id) ||
577             ubus_invoke_fd(ctx, id, "console_attach", req.head, NULL, NULL, 3000, server_fd)) {
578                 fprintf(stderr, "ubus request failed\n");
579                 close(tty_fd);
580                 close(server_fd);
581                 close(client_fd);
582                 blob_buf_free(&req);
583                 ubus_free(ctx);
584                 return -ENXIO;
585         }
586 
587         close(server_fd);
588         blob_buf_free(&req);
589         ubus_free(ctx);
590 
591         uloop_init();
592 
593         /* forward between stdio and client_fd until detach is requested */
594         lufd.stream.notify_read = local_cb;
595         ustream_fd_init(&lufd, tty_fd);
596 
597         cufd.stream.notify_read = client_cb;
598 /* ToDo: handle remote close and other events */
599 //      cufd.stream.notify_state = client_state_cb;
600         ustream_fd_init(&cufd, client_fd);
601 
602         fprintf(stderr, "attaching to jail console. press [CTRL]+[B] to exit.\n");
603         close(0);
604         close(1);
605         close(2);
606         uloop_run();
607 
608         tcsetattr(tty_fd, TCSAFLUSH, &oldtermios);
609         ustream_free(&lufd.stream);
610         ustream_free(&cufd.stream);
611         close(client_fd);
612 
613         return 0;
614 }
615 
616 static int uxc_state(char *name)
617 {
618         struct runtime_state *rsstate = avl_find_element(&runtime, name, rsstate, avl);
619         struct blob_attr *ocistate = NULL;
620         struct blob_attr *cur, *tb[__CONF_MAX];
621         int rem;
622         char *bundle = NULL;
623         char *jail_name = NULL;
624         char *state = NULL;
625         char *tmp;
626         static struct blob_buf buf;
627 
628         if (rsstate)
629                 ocistate = rsstate->ocistate;
630 
631         if (ocistate) {
632                 state = blobmsg_format_json_indent(ocistate, true, 0);
633                 if (!state)
634                         return -ENOMEM;
635 
636                 printf("%s\n", state);
637                 free(state);
638                 return 0;
639         }
640 
641         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
642                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
643                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
644                         continue;
645 
646                 if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) {
647                         if (tb[CONF_JAIL])
648                                 jail_name = blobmsg_get_string(tb[CONF_JAIL]);
649                         else
650                                 jail_name = name;
651 
652                         bundle = blobmsg_get_string(tb[CONF_PATH]);
653                         break;
654                 }
655         }
656 
657         if (!bundle)
658                 return -ENOENT;
659 
660         blob_buf_init(&buf, 0);
661         blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING);
662         blobmsg_add_string(&buf, "id", jail_name);
663         blobmsg_add_string(&buf, "status", rsstate?"stopped":"uninitialized");
664         blobmsg_add_string(&buf, "bundle", bundle);
665 
666         tmp = blobmsg_format_json_indent(buf.head, true, 0);
667         if (!tmp) {
668                 blob_buf_free(&buf);
669                 return -ENOMEM;
670         }
671 
672         printf("%s\n", tmp);
673         free(tmp);
674 
675         blob_buf_free(&buf);
676 
677         return 0;
678 }
679 
680 static int uxc_list(void)
681 {
682         struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
683         int rem;
684         struct runtime_state *rsstate = NULL;
685         struct settings *usettings = NULL;
686         char *name, *ocistatus, *status, *tmp;
687         int container_pid = -1;
688         bool autostart;
689         static struct blob_buf buf;
690         void *arr, *obj;
691 
692         if (json_output) {
693                 blob_buf_init(&buf, 0);
694                 arr = blobmsg_open_array(&buf, "");
695         }
696 
697         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
698                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
699                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
700                         continue;
701 
702                 autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]);
703 
704                 ocistatus = NULL;
705                 container_pid = 0;
706                 name = blobmsg_get_string(tb[CONF_NAME]);
707                 rsstate = avl_find_element(&runtime, name, rsstate, avl);
708 
709                 if (rsstate && rsstate->ocistate) {
710                         blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(rsstate->ocistate), blobmsg_len(rsstate->ocistate));
711                         ocistatus = blobmsg_get_string(ts[STATE_STATUS]);
712                         container_pid = blobmsg_get_u32(ts[STATE_PID]);
713                 }
714 
715                 status = ocistatus?:(rsstate && rsstate->running)?"creating":"stopped";
716 
717                 usettings = avl_find_element(&settings, name, usettings, avl);
718 
719                 if (usettings && (usettings->autostart >= 0))
720                         autostart = !!(usettings->autostart);
721 
722                 if (json_output) {
723                         obj = blobmsg_open_table(&buf, "");
724                         blobmsg_add_string(&buf, "name", name);
725                         blobmsg_add_string(&buf, "status", status);
726                         blobmsg_add_u8(&buf, "autostart", autostart);
727                 } else {
728                         printf("[%c] %s %s", autostart?'*':' ', name, status);
729                 }
730 
731                 if (rsstate && !rsstate->running && (rsstate->exitcode >= 0)) {
732                         if (json_output)
733                                 blobmsg_add_u32(&buf, "exitcode", rsstate->exitcode);
734                         else
735                                 printf(" exitcode: %d (%s)", rsstate->exitcode, strerror(rsstate->exitcode));
736                 }
737 
738                 if (rsstate && rsstate->running && (rsstate->runtime_pid >= 0)) {
739                         if (json_output)
740                                 blobmsg_add_u32(&buf, "runtime_pid", rsstate->runtime_pid);
741                         else
742                                 printf(" runtime pid: %d", rsstate->runtime_pid);
743                 }
744 
745                 if (rsstate && rsstate->running && (container_pid >= 0)) {
746                         if (json_output)
747                                 blobmsg_add_u32(&buf, "container_pid", container_pid);
748                         else
749                                 printf(" container pid: %d", container_pid);
750                 }
751 
752                 if (!json_output)
753                         printf("\n");
754                 else
755                         blobmsg_close_table(&buf, obj);
756         }
757 
758         if (json_output) {
759                 blobmsg_close_array(&buf, arr);
760                 tmp = blobmsg_format_json_indent(buf.head, true, 0);
761                 if (!tmp) {
762                         blob_buf_free(&buf);
763                         return -ENOMEM;
764                 }
765                 printf("%s\n", tmp);
766                 free(tmp);
767                 blob_buf_free(&buf);
768         };
769 
770         return 0;
771 }
772 
773 static int uxc_exists(char *name)
774 {
775         struct runtime_state *rsstate = NULL;
776         rsstate = avl_find_element(&runtime, name, rsstate, avl);
777 
778         if (rsstate && (rsstate->running))
779                 return -EEXIST;
780 
781         return 0;
782 }
783 
784 static int uxc_create(char *name, bool immediately)
785 {
786         static struct blob_buf req;
787         struct blob_attr *cur, *tb[__CONF_MAX];
788         int rem, ret = 0;
789         uint32_t id;
790         struct settings *usettings = NULL;
791         char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
792 
793         void *in, *ins, *j;
794         bool found = false;
795 
796         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
797                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
798                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
799                         continue;
800 
801                 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
802                         continue;
803 
804                 found = true;
805                 break;
806         }
807 
808         if (!found)
809                 return -ENOENT;
810 
811         path = blobmsg_get_string(tb[CONF_PATH]);
812 
813         if (tb[CONF_PIDFILE])
814                 pidfile = blobmsg_get_string(tb[CONF_PIDFILE]);
815 
816         if (tb[CONF_TEMP_OVERLAY_SIZE])
817                 tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
818 
819         if (tb[CONF_WRITE_OVERLAY_PATH])
820                 writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
821 
822         if (tb[CONF_JAIL])
823                 jailname = blobmsg_get_string(tb[CONF_JAIL]);
824 
825         usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
826         if (usettings) {
827                 if (usettings->writepath) {
828                         writepath = usettings->writepath;
829                         tmprwsize = NULL;
830                 }
831                 if (usettings->tmprwsize) {
832                         tmprwsize = usettings->tmprwsize;
833                         writepath = NULL;
834                 }
835         }
836 
837         blob_buf_init(&req, 0);
838         blobmsg_add_string(&req, "name", name);
839         ins = blobmsg_open_table(&req, "instances");
840         in = blobmsg_open_table(&req, name);
841         blobmsg_add_string(&req, "bundle", path);
842         j = blobmsg_open_table(&req, "jail");
843         blobmsg_add_string(&req, "name", jailname?:name);
844         blobmsg_add_u8(&req, "immediately", immediately);
845 
846         if (pidfile)
847                 blobmsg_add_string(&req, "pidfile", pidfile);
848 
849         blobmsg_close_table(&req, j);
850 
851         if (writepath)
852                 blobmsg_add_string(&req, "overlaydir", writepath);
853 
854         if (tmprwsize)
855                 blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize);
856 
857         blobmsg_close_table(&req, in);
858         blobmsg_close_table(&req, ins);
859 
860         if (verbose) {
861                 char *tmp;
862                 tmp = blobmsg_format_json_indent(req.head, true, 1);
863                 if (!tmp)
864                         return -ENOMEM;
865 
866                 fprintf(stderr, "adding container to procd:\n\t%s\n", tmp);
867                 free(tmp);
868         }
869 
870         if (ubus_lookup_id(ctx, "container", &id) ||
871                 ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
872                 blob_buf_free(&req);
873                 ret = -EIO;
874         }
875 
876         return ret;
877 }
878 
879 static int uxc_start(const char *name, bool console)
880 {
881         char *objname;
882         unsigned int id;
883         pid_t pid;
884 
885         if (console) {
886                 pid = fork();
887                 if (pid > 0)
888                         exit(uxc_attach(name));
889         }
890 
891         if (asprintf(&objname, "container.%s", name) == -1)
892                 return -ENOMEM;
893 
894         if (ubus_lookup_id(ctx, objname, &id))
895                 return -ENOENT;
896 
897         free(objname);
898         return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000);
899 }
900 
901 static int uxc_kill(char *name, int signal)
902 {
903         static struct blob_buf req;
904         struct blob_attr *cur, *tb[__CONF_MAX];
905         int rem, ret;
906         char *objname;
907         unsigned int id;
908         struct runtime_state *rsstate = NULL;
909         bool found = false;
910 
911         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
912                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
913                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
914                         continue;
915 
916                 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
917                         continue;
918 
919                 found = true;
920                 break;
921         }
922 
923         if (!found)
924                 return -ENOENT;
925 
926         rsstate = avl_find_element(&runtime, name, rsstate, avl);
927 
928         if (!rsstate || !(rsstate->running))
929                 return -ENOENT;
930 
931         blob_buf_init(&req, 0);
932         blobmsg_add_u32(&req, "signal", signal);
933         blobmsg_add_string(&req, "name", name);
934 
935         if (asprintf(&objname, "container.%s", name) == -1)
936                 return -ENOMEM;
937 
938         ret = ubus_lookup_id(ctx, objname, &id);
939         free(objname);
940         if (ret)
941                 return -ENOENT;
942 
943         if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
944                 return -EIO;
945 
946         return 0;
947 }
948 
949 
950 static int uxc_set(char *name, char *path, signed char autostart, char *pidfile, char *tmprwsize, char *writepath, char *requiredmounts)
951 {
952         static struct blob_buf req;
953         struct settings *usettings = NULL;
954         struct blob_attr *cur, *tb[__CONF_MAX];
955         int rem, ret;
956         const char *cfname = NULL;
957         const char *sfname = NULL;
958         char *fname = NULL;
959         char *curvol, *tmp, *mnttok;
960         void *mntarr;
961         int f;
962         struct stat sb;
963 
964         /* nothing to do */
965         if (!path && (autostart<0) && !pidfile && !tmprwsize && !writepath && !requiredmounts)
966                 return 0;
967 
968         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
969                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
970                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
971                         continue;
972 
973                 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
974                         continue;
975 
976                 cfname = blobmsg_name(cur);
977                 break;
978         }
979 
980         if (cfname && path)
981                 return -EEXIST;
982 
983         if (!cfname && !path)
984                 return -ENOENT;
985 
986         if (path) {
987                 if (stat(path, &sb) == -1)
988                         return -ENOENT;
989 
990                 if ((sb.st_mode & S_IFMT) != S_IFDIR)
991                         return -ENOTDIR;
992         }
993 
994         usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
995         if (path && usettings)
996                 return -EIO;
997 
998         if (usettings) {
999                 sfname = usettings->fname;
1000                 if (!tmprwsize && !writepath) {
1001                         if (usettings->tmprwsize) {
1002                                 tmprwsize = usettings->tmprwsize;
1003                                 writepath = NULL;
1004                         }
1005                         if (usettings->writepath) {
1006                                 writepath = usettings->writepath;
1007                                 tmprwsize = NULL;
1008                         }
1009                 }
1010                 if (usettings->autostart >= 0 && autostart < 0)
1011                         autostart = !!(usettings->autostart);
1012         }
1013 
1014         if (path) {
1015                 ret = mkdir(confdir, 0755);
1016 
1017                 if (ret && errno != EEXIST)
1018                         return -errno;
1019 
1020                 if (asprintf(&fname, "%s/%s.json", confdir, name) == -1)
1021                         return -ENOMEM;
1022 
1023                 f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1024                 if (f < 0)
1025                         return -errno;
1026 
1027                 free(fname);
1028         } else {
1029                 if (sfname) {
1030                         f = open(sfname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1031                 } else {
1032                         char *t1, *t2;
1033                         t1 = strdup(cfname);
1034                         t2 = strrchr(t1, '/');
1035                         if (!t2)
1036                                 return -EINVAL;
1037 
1038                         *t2 = '\0';
1039 
1040                         if (asprintf(&t2, "%s/settings", t1) == -1)
1041                                 return -ENOMEM;
1042 
1043                         ret = mkdir(t2, 0755);
1044                         if (ret && ret != EEXIST)
1045                                 return -ret;
1046 
1047                         free(t2);
1048                         if (asprintf(&t2, "%s/settings/%s.json", t1, name) == -1)
1049                                 return -ENOMEM;
1050 
1051                         free(t1);
1052                         f = open(t2, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1053                         free(t2);
1054                 }
1055                 if (f < 0)
1056                         return -errno;
1057         }
1058 
1059         blob_buf_init(&req, 0);
1060         blobmsg_add_string(&req, "name", name);
1061         if (path)
1062                 blobmsg_add_string(&req, "path", path);
1063 
1064         if (autostart >= 0)
1065                 blobmsg_add_u8(&req, "autostart", !!autostart);
1066 
1067         if (pidfile)
1068                 blobmsg_add_string(&req, "pidfile", pidfile);
1069 
1070         if (tmprwsize)
1071                 blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
1072 
1073         if (writepath)
1074                 blobmsg_add_string(&req, "write-overlay-path", writepath);
1075 
1076         if (!requiredmounts && usettings && usettings->volumes)
1077                 blobmsg_add_blob(&req, usettings->volumes);
1078 
1079         if (requiredmounts) {
1080                 mntarr = blobmsg_open_array(&req, "volumes");
1081                 for (mnttok = requiredmounts; ; mnttok = NULL) {
1082                         curvol = strtok_r(mnttok, ",;", &tmp);
1083                         if (!curvol)
1084                                 break;
1085 
1086                         blobmsg_add_string(&req, NULL, curvol);
1087                 }
1088                 blobmsg_close_array(&req, mntarr);
1089         }
1090 
1091         tmp = blobmsg_format_json_indent(req.head, true, 0);
1092         if (tmp) {
1093                 dprintf(f, "%s\n", tmp);
1094                 free(tmp);
1095         }
1096 
1097         blob_buf_free(&req);
1098         close(f);
1099 
1100         return 1;
1101 }
1102 
1103 enum {
1104         BLOCK_INFO_DEVICE,
1105         BLOCK_INFO_UUID,
1106         BLOCK_INFO_TARGET,
1107         BLOCK_INFO_TYPE,
1108         BLOCK_INFO_MOUNT,
1109         __BLOCK_INFO_MAX,
1110 };
1111 
1112 static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
1113         [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
1114         [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
1115         [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
1116         [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
1117         [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
1118 };
1119 
1120 
1121 /* check if device 'devname' is mounted according to blockd */
1122 static bool checkblock(const char *uuid)
1123 {
1124         struct blob_attr *tb[__BLOCK_INFO_MAX];
1125         struct blob_attr *cur;
1126         int rem;
1127 
1128         blobmsg_for_each_attr(cur, blockinfo, rem) {
1129                 blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1130 
1131                 if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
1132                         continue;
1133 
1134                 if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
1135                         return false;
1136         }
1137 
1138         return true;
1139 }
1140 
1141 enum {
1142         UCI_FSTAB_UUID,
1143         UCI_FSTAB_ANONYMOUS,
1144         __UCI_FSTAB_MAX,
1145 };
1146 
1147 static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
1148         [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
1149         [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
1150 };
1151 
1152 static const char *resolveuuid(const char *volname)
1153 {
1154         struct blob_attr *tb[__UCI_FSTAB_MAX];
1155         struct blob_attr *cur;
1156         const char *mntname;
1157         char *tmpvolname, *replc;
1158         int rem, res;
1159 
1160         blobmsg_for_each_attr(cur, fstabinfo, rem) {
1161                 blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1162 
1163                 if (!tb[UCI_FSTAB_UUID])
1164                         continue;
1165 
1166                 if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
1167                         continue;
1168 
1169                 mntname = blobmsg_name(cur);
1170                 if (!mntname)
1171                         continue;
1172 
1173                 tmpvolname = strdup(volname);
1174                 while ((replc = strchr(tmpvolname, '-')))
1175                         *replc = '_';
1176 
1177                 res = strcmp(tmpvolname, mntname);
1178                 free(tmpvolname);
1179 
1180                 if (!res)
1181                         return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
1182         };
1183 
1184         return volname;
1185 };
1186 
1187 /* check status of each required volume */
1188 static bool checkvolumes(struct blob_attr *volumes)
1189 {
1190         struct blob_attr *cur;
1191         int rem;
1192 
1193         blobmsg_for_each_attr(cur, volumes, rem) {
1194                 if (checkblock(resolveuuid(blobmsg_get_string(cur))))
1195                         return true;
1196         }
1197 
1198         return false;
1199 }
1200 
1201 static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
1202 {
1203         blockinfo = blob_memdup(blobmsg_data(msg));
1204 }
1205 
1206 static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
1207 {
1208         fstabinfo = blob_memdup(blobmsg_data(msg));
1209 }
1210 
1211 static int uxc_boot(void)
1212 {
1213         struct blob_attr *cur, *tb[__CONF_MAX];
1214         struct runtime_state *rsstate = NULL;
1215         struct settings *usettings = NULL;
1216         static struct blob_buf req;
1217         int rem, ret = 0;
1218         char *name;
1219         unsigned int id;
1220         bool autostart;
1221 
1222         ret = ubus_lookup_id(ctx, "block", &id);
1223         if (ret)
1224                 return -ENOENT;
1225 
1226         ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
1227         if (ret)
1228                 return -ENXIO;
1229 
1230         ret = ubus_lookup_id(ctx, "uci", &id);
1231         if (ret)
1232                 return -ENOENT;
1233 
1234         blob_buf_init(&req, 0);
1235         blobmsg_add_string(&req, "config", "fstab");
1236         blobmsg_add_string(&req, "type", "mount");
1237 
1238         ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
1239         if (ret)
1240                 return ret;
1241 
1242         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
1243                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1244                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
1245                         continue;
1246 
1247                 rsstate = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), rsstate, avl);
1248                 if (rsstate)
1249                         continue;
1250 
1251                 if (tb[CONF_AUTOSTART])
1252                         autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]);
1253 
1254                 usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
1255                 if (usettings && (usettings->autostart >= 0))
1256                         autostart = !!(usettings->autostart);
1257 
1258                 if (!autostart)
1259                         continue;
1260 
1261                 /* make sure all volumes are ready before starting */
1262                 if (tb[CONF_VOLUMES])
1263                         if (checkvolumes(tb[CONF_VOLUMES]))
1264                                 continue;
1265 
1266                 if (usettings && usettings->volumes)
1267                         if (checkvolumes(usettings->volumes))
1268                                 continue;
1269 
1270                 name = strdup(blobmsg_get_string(tb[CONF_NAME]));
1271                 if (uxc_exists(name))
1272                         continue;
1273 
1274                 if (uxc_create(name, true))
1275                         ++ret;
1276 
1277                 free(name);
1278         }
1279 
1280         return ret;
1281 }
1282 
1283 static int uxc_delete(char *name, bool force)
1284 {
1285         struct blob_attr *cur, *tb[__CONF_MAX];
1286         struct runtime_state *rsstate = NULL;
1287         struct settings *usettings = NULL;
1288         static struct blob_buf req;
1289         uint32_t id;
1290         int rem, ret = 0;
1291         const char *cfname = NULL;
1292         const char *sfname = NULL;
1293         struct stat sb;
1294 
1295         blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
1296                 blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1297                 if (!tb[CONF_NAME] || !tb[CONF_PATH])
1298                         continue;
1299 
1300                 if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
1301                         continue;
1302 
1303                 cfname = blobmsg_name(cur);
1304                 break;
1305         }
1306 
1307         if (!cfname)
1308                 return -ENOENT;
1309 
1310         rsstate = avl_find_element(&runtime, name, rsstate, avl);
1311 
1312         if (rsstate && rsstate->running) {
1313                 if (force) {
1314                         ret = uxc_kill(name, SIGKILL);
1315                         if (ret)
1316                                 goto errout;
1317 
1318                 } else {
1319                         ret = -EWOULDBLOCK;
1320                         goto errout;
1321                 }
1322         }
1323 
1324         if (rsstate) {
1325                 ret = ubus_lookup_id(ctx, "container", &id);
1326                 if (ret)
1327                         goto errout;
1328 
1329                 blob_buf_init(&req, 0);
1330                 blobmsg_add_string(&req, "name", rsstate->container_name);
1331                 blobmsg_add_string(&req, "instance", rsstate->instance_name);
1332 
1333                 if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
1334                         blob_buf_free(&req);
1335                         ret = -EIO;
1336                         goto errout;
1337                 }
1338         }
1339 
1340         usettings = avl_find_element(&settings, name, usettings, avl);
1341         if (usettings)
1342                 sfname = usettings->fname;
1343 
1344         if (sfname) {
1345                 if (stat(sfname, &sb) == -1) {
1346                         ret = -ENOENT;
1347                         goto errout;
1348                 }
1349 
1350                 if (unlink(sfname) == -1) {
1351                         ret = -errno;
1352                         goto errout;
1353                 }
1354         }
1355 
1356         if (stat(cfname, &sb) == -1) {
1357                 ret = -ENOENT;
1358                 goto errout;
1359         }
1360 
1361         if (unlink(cfname) == -1)
1362                 ret = -errno;
1363 
1364 errout:
1365         return ret;
1366 }
1367 
1368 static void reload_conf(void)
1369 {
1370         blob_buf_free(&conf);
1371         conf_load(false);
1372         settings_free();
1373         blob_buf_free(&settingsbuf);
1374         conf_load(true);
1375         settings_add();
1376 }
1377 
1378 int main(int argc, char **argv)
1379 {
1380         enum uxc_cmd cmd = CMD_UNKNOWN;
1381         int ret = -EINVAL;
1382         char *bundle = NULL;
1383         char *pidfile = NULL;
1384         char *tmprwsize = NULL;
1385         char *writepath = NULL;
1386         char *requiredmounts = NULL;
1387         signed char autostart = -1;
1388         bool force = false;
1389         bool console = false;
1390         int signal = SIGTERM;
1391         int c;
1392 
1393         if (argc < 2)
1394                 return usage();
1395 
1396         ctx = ubus_connect(NULL);
1397         if (!ctx)
1398                 return -ENODEV;
1399 
1400         ret = conf_load(false);
1401         if (ret < 0)
1402                 goto out;
1403 
1404         ret = conf_load(true);
1405         if (ret < 0)
1406                 goto conf_out;
1407 
1408         ret = settings_add();
1409         if (ret < 0)
1410                 goto settings_out;
1411 
1412         ret = runtime_load();
1413         if (ret)
1414                 goto settings_avl_out;
1415 
1416         while (true) {
1417                 int option_index = 0;
1418                 c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
1419                 if (c == -1)
1420                         break;
1421 
1422                 switch (c) {
1423                         case 'a':
1424                                 autostart = 1;
1425                                 break;
1426 
1427                         case 'b':
1428                                 bundle = optarg;
1429                                 break;
1430 
1431                         case 'c':
1432                                 console = true;
1433                                 break;
1434 
1435                         case 'f':
1436                                 force = true;
1437                                 break;
1438 
1439                         case 'j':
1440                                 json_output = true;
1441                                 break;
1442 
1443                         case 'p':
1444                                 pidfile = optarg;
1445                                 break;
1446 
1447                         case 't':
1448                                 tmprwsize = optarg;
1449                                 break;
1450 
1451                         case 'v':
1452                                 verbose = true;
1453                                 break;
1454 
1455                         case 'V':
1456                                 printf("uxc %s\n", UXC_VERSION);
1457                                 exit(0);
1458 
1459                         case 'w':
1460                                 writepath = optarg;
1461                                 break;
1462 
1463                         case 'm':
1464                                 requiredmounts = optarg;
1465                                 break;
1466                 }
1467         }
1468 
1469         if (optind == argc)
1470                 goto usage_out;
1471 
1472         if (!strcmp("list", argv[optind]))
1473                 cmd = CMD_LIST;
1474         else if (!strcmp("attach", argv[optind]))
1475                 cmd = CMD_ATTACH;
1476         else if (!strcmp("boot", argv[optind]))
1477                 cmd = CMD_BOOT;
1478         else if(!strcmp("start", argv[optind]))
1479                 cmd = CMD_START;
1480         else if(!strcmp("state", argv[optind]))
1481                 cmd = CMD_STATE;
1482         else if(!strcmp("kill", argv[optind]))
1483                 cmd = CMD_KILL;
1484         else if(!strcmp("enable", argv[optind]))
1485                 cmd = CMD_ENABLE;
1486         else if(!strcmp("disable", argv[optind]))
1487                 cmd = CMD_DISABLE;
1488         else if(!strcmp("delete", argv[optind]))
1489                 cmd = CMD_DELETE;
1490         else if(!strcmp("create", argv[optind]))
1491                 cmd = CMD_CREATE;
1492 
1493         switch (cmd) {
1494                 case CMD_ATTACH:
1495                         if (optind != argc - 2)
1496                                 goto usage_out;
1497 
1498                         ret = uxc_attach(argv[optind + 1]);
1499                         break;
1500 
1501                 case CMD_LIST:
1502                         ret = uxc_list();
1503                         break;
1504 
1505                 case CMD_BOOT:
1506                         ret = uxc_boot();
1507                         break;
1508 
1509                 case CMD_START:
1510                         if (optind != argc - 2)
1511                                 goto usage_out;
1512 
1513                         ret = uxc_start(argv[optind + 1], console);
1514                         break;
1515 
1516                 case CMD_STATE:
1517                         if (optind != argc - 2)
1518                                 goto usage_out;
1519 
1520                         ret = uxc_state(argv[optind + 1]);
1521                         break;
1522 
1523                 case CMD_KILL:
1524                         if (optind == (argc - 3))
1525                                 signal = atoi(argv[optind + 2]);
1526                         else if (optind > argc - 2)
1527                                 goto usage_out;
1528 
1529                         ret = uxc_kill(argv[optind + 1], signal);
1530                         break;
1531 
1532                 case CMD_ENABLE:
1533                         if (optind != argc - 2)
1534                                 goto usage_out;
1535 
1536                         ret = uxc_set(argv[optind + 1], NULL, 1, NULL, NULL, NULL, NULL);
1537                         break;
1538 
1539                 case CMD_DISABLE:
1540                         if (optind != argc - 2)
1541                                 goto usage_out;
1542 
1543                         ret = uxc_set(argv[optind + 1], NULL, 0, NULL, NULL, NULL, NULL);
1544                         break;
1545 
1546                 case CMD_DELETE:
1547                         if (optind != argc - 2)
1548                                 goto usage_out;
1549 
1550                         ret = uxc_delete(argv[optind + 1], force);
1551                         break;
1552 
1553                 case CMD_CREATE:
1554                         if (optind != argc - 2)
1555                                 goto usage_out;
1556 
1557                         ret = uxc_exists(argv[optind + 1]);
1558                         if (ret)
1559                                 goto runtime_out;
1560 
1561                         ret = uxc_set(argv[optind + 1], bundle, autostart, pidfile, tmprwsize, writepath, requiredmounts);
1562                         if (ret < 0)
1563                                 goto runtime_out;
1564 
1565                         if (ret > 0)
1566                                 reload_conf();
1567 
1568                         ret = uxc_create(argv[optind + 1], false);
1569                         break;
1570 
1571                 default:
1572                         goto usage_out;
1573         }
1574 
1575         goto runtime_out;
1576 
1577 usage_out:
1578         ret = usage();
1579 runtime_out:
1580         runtime_free();
1581 settings_avl_out:
1582         settings_free();
1583 settings_out:
1584         blob_buf_free(&settingsbuf);
1585 conf_out:
1586         blob_buf_free(&conf);
1587 out:
1588         ubus_free(ctx);
1589 
1590         if (ret < 0)
1591                 fprintf(stderr, "uxc error: %s\n", strerror(-ret));
1592 
1593         return ret;
1594 }
1595 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt