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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt