uxc: implement support for rootfs overlay in containers
authorDaniel Golle <daniel@makrotopia.org>
Mon, 12 Jul 2021 20:22:04 +0000 (21:22 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Thu, 15 Jul 2021 17:07:44 +0000 (18:07 +0100)
ujail already supports having a (temporary) overlayfs on top of a
containers rootfs. This is very useful for "dirty" containers which
assume / is writable.
Support this in uxc at the time a container is created and keep the
settings on subsequent re-creates (or reboots).

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
uxc.c

diff --git a/uxc.c b/uxc.c
index eb40eb2e6a5bc54345f80c649e33d8822a4fe040..210084dcd8fc1855484560f8b164cad4dedd87ac 100644 (file)
--- a/uxc.c
+++ b/uxc.c
 
 #include "log.h"
 
-#define UXC_VERSION "0.1"
+#define UXC_VERSION "0.2"
 #define OCI_VERSION_STRING "1.0.2"
 #define UXC_CONFDIR "/etc/uxc"
-#define UXC_RUNDIR "/var/run/uxc"
+
+static bool verbose = false;
 
 struct runtime_state {
        struct avl_node avl;
@@ -64,20 +65,21 @@ enum uxc_cmd {
        CMD_UNKNOWN
 };
 
-#define OPT_ARGS "ab:fp:vV"
+#define OPT_ARGS "ab:fp:t:vVw:"
 static struct option long_options[] = {
-       {"autostart",   no_argument,            0,      'a'     },
-       {"bundle",      required_argument,      0,      'b'     },
-       {"force",       no_argument,            0,      'f'     },
-       {"pid-file",    required_argument,      0,      'p'     },
-       {"verbose",     no_argument,            0,      'v'     },
-       {"version",     no_argument,            0,      'V'     },
-       {0,             0,                      0,      0       }
+       {"autostart",           no_argument,            0,      'a'     },
+       {"bundle",              required_argument,      0,      'b'     },
+       {"force",               no_argument,            0,      'f'     },
+       {"pid-file",            required_argument,      0,      'p'     },
+       {"temp-overlay-size",   required_argument,      0,      't'     },
+       {"write-overlay-path",  required_argument,      0,      'w'     },
+       {"verbose",             no_argument,            0,      'v'     },
+       {"version",             no_argument,            0,      'V'     },
+       {0,                     0,                      0,      0       }
 };
 
 AVL_TREE(runtime, avl_strcmp, false, NULL);
 static struct blob_buf conf;
-static struct blob_buf state;
 static struct ubus_context *ctx;
 
 static int usage(void) {
@@ -100,6 +102,8 @@ enum {
        CONF_JAIL,
        CONF_AUTOSTART,
        CONF_PIDFILE,
+       CONF_TEMP_OVERLAY_SIZE,
+       CONF_WRITE_OVERLAY_PATH,
        __CONF_MAX,
 };
 
@@ -109,22 +113,23 @@ static const struct blobmsg_policy conf_policy[__CONF_MAX] = {
        [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
        [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL },
        [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING },
+       [CONF_TEMP_OVERLAY_SIZE] = { .name = "temp-overlay-size", .type = BLOBMSG_TYPE_STRING },
+       [CONF_WRITE_OVERLAY_PATH] = { .name = "write-overlay-path", .type = BLOBMSG_TYPE_STRING },
 };
 
-static int conf_load(bool load_state)
+static int conf_load(void)
 {
        int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
        int j, res;
        glob_t gl;
        char *globstr;
-       struct blob_buf *target = load_state?&state:&conf;
        void *c, *o;
 
-       if (asprintf(&globstr, "%s/*.json", load_state?UXC_RUNDIR:UXC_CONFDIR) == -1)
+       if (asprintf(&globstr, "%s/*.json", UXC_CONFDIR) == -1)
                return ENOMEM;
 
-       blob_buf_init(target, 0);
-       c = blobmsg_open_table(target, NULL);
+       blob_buf_init(&conf, 0);
+       c = blobmsg_open_table(&conf, NULL);
 
        res = glob(globstr, gl_flags, NULL, &gl);
        free(globstr);
@@ -132,14 +137,14 @@ static int conf_load(bool load_state)
                return 0;
 
        for (j = 0; j < gl.gl_pathc; j++) {
-               o = blobmsg_open_table(target, strdup(gl.gl_pathv[j]));
-               if (!blobmsg_add_json_from_file(target, gl.gl_pathv[j])) {
+               o = blobmsg_open_table(&conf, strdup(gl.gl_pathv[j]));
+               if (!blobmsg_add_json_from_file(&conf, gl.gl_pathv[j])) {
                        ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]);
                        continue;
                }
-               blobmsg_close_table(target, o);
+               blobmsg_close_table(&conf, o);
        }
-       blobmsg_close_table(target, c);
+       blobmsg_close_table(&conf, c);
        globfree(&gl);
 
        return 0;
@@ -427,7 +432,8 @@ static int uxc_create(char *name, bool immediately)
        int rem, ret;
        uint32_t id;
        struct runtime_state *s = NULL;
-       char *path = NULL, *jailname = NULL, *pidfile = NULL;
+       char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
+
        void *in, *ins, *j;
        bool found = false;
 
@@ -444,6 +450,13 @@ static int uxc_create(char *name, bool immediately)
 
                if (tb[CONF_PIDFILE])
                        pidfile = strdup(blobmsg_get_string(tb[CONF_PIDFILE]));
+
+               if (tb[CONF_TEMP_OVERLAY_SIZE])
+                       tmprwsize = strdup(blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]));
+
+               if (tb[CONF_WRITE_OVERLAY_PATH])
+                       writepath = strdup(blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]));
+
                break;
        }
 
@@ -466,13 +479,25 @@ static int uxc_create(char *name, bool immediately)
        j = blobmsg_open_table(&req, "jail");
        blobmsg_add_string(&req, "name", jailname?:name);
        blobmsg_add_u8(&req, "immediately", immediately);
+
        if (pidfile)
                blobmsg_add_string(&req, "pidfile", pidfile);
 
        blobmsg_close_table(&req, j);
+
+       if (writepath)
+               blobmsg_add_string(&req, "overlaydir", writepath);
+
+       if (tmprwsize)
+               blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize);
+
        blobmsg_close_table(&req, in);
        blobmsg_close_table(&req, ins);
 
+       if (verbose)
+               fprintf(stderr, "adding container to procd:\n\t%s\n",
+                       blobmsg_format_json_indent(req.head, true, 1));
+
        ret = 0;
        if (ubus_lookup_id(ctx, "container", &id) ||
                ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
@@ -545,7 +570,7 @@ static int uxc_kill(char *name, int signal)
 }
 
 
-static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile)
+static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile, char *_tmprwsize, char *_writepath)
 {
        static struct blob_buf req;
        struct blob_attr *cur, *tb[__CONF_MAX];
@@ -553,6 +578,9 @@ static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfi
        bool found = false;
        char *fname = NULL;
        char *keeppath = NULL;
+       char *tmprwsize = _tmprwsize;
+       char *writepath = _writepath;
+
        int f;
        struct stat sb;
 
@@ -597,8 +625,14 @@ static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfi
        if (f < 0)
                return errno;
 
-       if (!add)
+       if (!add) {
                keeppath = strdup(blobmsg_get_string(tb[CONF_PATH]));
+               if (tb[CONF_WRITE_OVERLAY_PATH])
+                       writepath = strdup(blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]));
+
+               if (tb[CONF_TEMP_OVERLAY_SIZE])
+                       tmprwsize = strdup(blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]));
+       }
 
        blob_buf_init(&req, 0);
        blobmsg_add_string(&req, "name", name);
@@ -607,6 +641,12 @@ static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfi
        if (pidfile)
                blobmsg_add_string(&req, "pidfile", pidfile);
 
+       if (tmprwsize)
+               blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
+
+       if (writepath)
+               blobmsg_add_string(&req, "write-overlay-path", writepath);
+
        dprintf(f, "%s\n", blobmsg_format_json_indent(req.head, true, 0));
 
        if (!add)
@@ -712,19 +752,19 @@ errout:
 static void reload_conf(void)
 {
        blob_buf_free(&conf);
-       conf_load(false);
+       conf_load();
 }
 
-
 int main(int argc, char **argv)
 {
        enum uxc_cmd cmd = CMD_UNKNOWN;
        int ret = EINVAL;
        char *bundle = NULL;
        char *pidfile = NULL;
+       char *tmprwsize = NULL;
+       char *writepath = NULL;
        bool autostart = false;
        bool force = false;
-       bool verbose = false;
        int signal = SIGTERM;
        int c;
 
@@ -735,21 +775,13 @@ int main(int argc, char **argv)
        if (!ctx)
                return ENODEV;
 
-       ret = conf_load(false);
+       ret = conf_load();
        if (ret)
                goto out;
 
-       ret = mkdir(UXC_RUNDIR, 0755);
-       if (ret && errno != EEXIST)
-               goto conf_out;
-
-       ret = conf_load(true);
-       if (ret)
-               goto conf_out;
-
        ret = runtime_load();
        if (ret)
-               goto state_out;
+               goto conf_out;
 
        while (true) {
                int option_index = 0;
@@ -774,6 +806,10 @@ int main(int argc, char **argv)
                                pidfile = optarg;
                                break;
 
+                       case 't':
+                               tmprwsize = optarg;
+                               break;
+
                        case 'v':
                                verbose = true;
                                break;
@@ -781,6 +817,10 @@ int main(int argc, char **argv)
                        case 'V':
                                printf("uxc %s\n", UXC_VERSION);
                                exit(0);
+
+                       case 'w':
+                               writepath = optarg;
+                               break;
                }
        }
 
@@ -842,14 +882,14 @@ int main(int argc, char **argv)
                        if (optind != argc - 2)
                                goto usage_out;
 
-                       ret = uxc_set(argv[optind + 1], NULL, true, false, NULL);
+                       ret = uxc_set(argv[optind + 1], NULL, true, false, NULL, NULL, NULL);
                        break;
 
                case CMD_DISABLE:
                        if (optind != argc - 2)
                                goto usage_out;
 
-                       ret = uxc_set(argv[optind + 1], NULL, false, false, NULL);
+                       ret = uxc_set(argv[optind + 1], NULL, false, false, NULL, NULL, NULL);
                        break;
 
                case CMD_DELETE:
@@ -864,7 +904,7 @@ int main(int argc, char **argv)
                                goto usage_out;
 
                        if (bundle) {
-                               ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile);
+                               ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile, tmprwsize, writepath);
                                if (ret)
                                        goto runtime_out;
 
@@ -884,8 +924,6 @@ usage_out:
        usage();
 runtime_out:
        runtime_free();
-state_out:
-       blob_buf_free(&state);
 conf_out:
        blob_buf_free(&conf);
 out: