#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <glob.h>
#include "log.h"
+#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;
struct blob_attr *ocistate;
};
+enum uxc_cmd {
+ CMD_LIST,
+ CMD_BOOT,
+ CMD_START,
+ CMD_STATE,
+ CMD_KILL,
+ CMD_ENABLE,
+ CMD_DISABLE,
+ CMD_DELETE,
+ CMD_CREATE,
+ CMD_UNKNOWN
+};
+
+#define OPT_ARGS "ab:fm:p:t:vVw:"
+static struct option long_options[] = {
+ {"autostart", no_argument, 0, 'a' },
+ {"bundle", required_argument, 0, 'b' },
+ {"force", no_argument, 0, 'f' },
+ {"mounts", required_argument, 0, 'm' },
+ {"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 blob_attr *blockinfo;
+static struct blob_attr *fstabinfo;
static struct ubus_context *ctx;
static int usage(void) {
- printf("syntax: uxc {command} [parameters ...]\n");
+ printf("syntax: uxc <command> [parameters ...]\n");
printf("commands:\n");
- printf("\tlist\t\t\t\tlist all configured containers\n");
- printf("\tcreate {conf} [path] [enabled]\tcreate {conf} for OCI bundle at {path}\n");
- printf("\tstart {conf}\t\t\tstart container {conf}\n");
- printf("\tstate {conf}\t\t\tget state of container {conf}\n");
- printf("\tkill {conf} {signal}\t\tsend signal to container {conf}\n");
- printf("\tenable {conf}\t\t\tstart container {conf} on boot\n");
- printf("\tdisable {conf}\t\t\tdon't start container {conf} on boot\n");
- printf("\tdelete {conf}\t\t\tdelete {conf}\n");
+ printf("\tlist\t\t\t\t\t\tlist all configured containers\n");
+ printf("\tcreate <conf>\t\t\t\t\t(re-)create <conf>\n");
+ printf(" [--bundle <path>]\t\t\tOCI bundle at <path>\n");
+ printf(" [--autostart]\t\t\t\tstart on boot\n");
+ printf(" [--temp-overlay-size size]\t\tuse tmpfs overlay with {size}\n");
+ printf(" [--write-overlay-path path]\t\tuse overlay on {path}\n");
+ printf(" [--volumes v1,v2,...,vN]\t\trequire volumes to be available\n");
+ printf("\tstart <conf>\t\t\t\t\tstart container <conf>\n");
+ printf("\tstate <conf>\t\t\t\t\tget state of container <conf>\n");
+ printf("\tkill <conf> [<signal>]\t\t\t\tsend signal to container <conf>\n");
+ printf("\tenable <conf>\t\t\t\t\tstart container <conf> on boot\n");
+ printf("\tdisable <conf>\t\t\t\t\tdon't start container <conf> on boot\n");
+ printf("\tdelete <conf> [--force]\t\t\t\tdelete <conf>\n");
return EINVAL;
}
CONF_PATH,
CONF_JAIL,
CONF_AUTOSTART,
+ CONF_PIDFILE,
+ CONF_TEMP_OVERLAY_SIZE,
+ CONF_WRITE_OVERLAY_PATH,
+ CONF_VOLUMES,
__CONF_MAX,
};
[CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
[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 },
+ [CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY },
};
-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);
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;
{
char *objname;
unsigned int id;
-
+ int ret;
*ocistate = NULL;
- asprintf(&objname, "container.%s", name);
- if (ubus_lookup_id(ctx, objname, &id))
+ if (asprintf(&objname, "container.%s", name) == -1)
+ exit(ENOMEM);
+
+ ret = ubus_lookup_id(ctx, objname, &id);
+ free(objname);
+ if (ret)
return;
ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000);
int rem, ret;
uint32_t id;
struct runtime_state *s = NULL;
- char *path = NULL, *jailname = NULL;
+ char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
+
void *in, *ins, *j;
bool found = false;
found = true;
path = strdup(blobmsg_get_string(tb[CONF_PATH]));
+ 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;
}
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)) {
char *objname;
unsigned int id;
- asprintf(&objname, "container.%s", name);
+ if (asprintf(&objname, "container.%s", name) == -1)
+ return ENOMEM;
+
if (ubus_lookup_id(ctx, objname, &id))
return ENOENT;
static struct blob_buf req;
struct blob_attr *cur, *tb[__CONF_MAX];
int rem, ret;
- uint32_t id;
+ char *objname;
+ unsigned int id;
struct runtime_state *s = NULL;
bool found = false;
return ENOENT;
blob_buf_init(&req, 0);
- blobmsg_add_string(&req, "name", name);
- blobmsg_add_string(&req, "instance", s->instance_name);
blobmsg_add_u32(&req, "signal", signal);
+ blobmsg_add_string(&req, "name", name);
- ret = 0;
- if (ubus_lookup_id(ctx, "container", &id) ||
- ubus_invoke(ctx, id, "signal", req.head, NULL, NULL, 3000)) {
- ret = EIO;
- }
+ if (asprintf(&objname, "container.%s", name) == -1)
+ return ENOMEM;
- return ret;
+ ret = ubus_lookup_id(ctx, objname, &id);
+ free(objname);
+ if (ret)
+ return ENOENT;
+
+ if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
+ return EIO;
+
+ return 0;
}
-static int uxc_set(char *name, char *path, bool autostart, bool add)
+static int uxc_set(char *name, char *path, bool autostart, bool add, char *pidfile, char *_tmprwsize, char *_writepath, char *requiredmounts)
{
static struct blob_buf req;
struct blob_attr *cur, *tb[__CONF_MAX];
bool found = false;
char *fname = NULL;
char *keeppath = NULL;
+ char *tmprwsize = _tmprwsize;
+ char *writepath = _writepath;
+ char *curvol, *tmp, *mnttok;
+ void *mntarr;
int f;
struct stat sb;
if (ret && errno != EEXIST)
return ret;
- if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) < 1)
+ if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) == -1)
return ENOMEM;
f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
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);
blobmsg_add_string(&req, "path", path?:keeppath);
blobmsg_add_u8(&req, "autostart", autostart);
+ 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);
+
+ if (!add && tb[CONF_VOLUMES])
+ blobmsg_add_blob(&req, tb[CONF_VOLUMES]);
+
+ if (add && requiredmounts) {
+ mntarr = blobmsg_open_array(&req, "volumes");
+ for (mnttok = requiredmounts; ; mnttok = NULL) {
+ curvol = strtok_r(mnttok, ",;", &tmp);
+ if (!curvol)
+ break;
+
+ blobmsg_add_string(&req, NULL, curvol);
+ }
+ blobmsg_close_array(&req, mntarr);
+ }
dprintf(f, "%s\n", blobmsg_format_json_indent(req.head, true, 0));
return 0;
}
+enum {
+ BLOCK_INFO_DEVICE,
+ BLOCK_INFO_UUID,
+ BLOCK_INFO_TARGET,
+ BLOCK_INFO_TYPE,
+ BLOCK_INFO_MOUNT,
+ __BLOCK_INFO_MAX,
+};
+
+static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
+ [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
+ [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
+ [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
+ [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
+ [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
+};
+
+
+/* check if device 'devname' is mounted according to blockd */
+static int checkblock(const char *uuid)
+{
+ struct blob_attr *tb[__BLOCK_INFO_MAX];
+ struct blob_attr *cur;
+ int rem;
+
+ blobmsg_for_each_attr(cur, blockinfo, rem) {
+ blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
+
+ if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
+ continue;
+
+ if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
+ return 0;
+ }
+
+ return 1;
+}
+
+enum {
+ UCI_FSTAB_UUID,
+ UCI_FSTAB_ANONYMOUS,
+ __UCI_FSTAB_MAX,
+};
+
+static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
+ [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
+ [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
+};
+
+static const char *resolveuuid(const char *volname)
+{
+ struct blob_attr *tb[__UCI_FSTAB_MAX];
+ struct blob_attr *cur;
+ const char *mntname;
+ char *tmpvolname, *replc;
+ int rem, res;
+
+ blobmsg_for_each_attr(cur, fstabinfo, rem) {
+ blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
+
+ if (!tb[UCI_FSTAB_UUID])
+ continue;
+
+ if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
+ continue;
+
+ mntname = blobmsg_name(cur);
+ if (!mntname)
+ continue;
+
+ tmpvolname = strdup(volname);
+ while ((replc = strchr(tmpvolname, '-')))
+ *replc = '_';
+
+ res = strcmp(tmpvolname, mntname);
+ free(tmpvolname);
+
+ if (!res)
+ return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
+ };
+
+ return volname;
+};
+
+/* check status of each required volume */
+static int checkvolumes(struct blob_attr *volumes)
+{
+ struct blob_attr *cur;
+ int rem;
+
+ blobmsg_for_each_attr(cur, volumes, rem) {
+ if (checkblock(resolveuuid(blobmsg_get_string(cur))))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+ blockinfo = blob_memdup(blobmsg_data(msg));
+}
+
+static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+ fstabinfo = blob_memdup(blobmsg_data(msg));
+}
+
static int uxc_boot(void)
{
struct blob_attr *cur, *tb[__CONF_MAX];
+ struct runtime_state *s;
+ static struct blob_buf req;
int rem, ret = 0;
char *name;
+ unsigned int id;
+
+ ret = ubus_lookup_id(ctx, "block", &id);
+ if (ret)
+ return ENOENT;
+
+ ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
+ if (ret)
+ return ENXIO;
+
+ ret = ubus_lookup_id(ctx, "uci", &id);
+ if (ret)
+ return ENOENT;
+
+ blob_buf_init(&req, 0);
+ blobmsg_add_string(&req, "config", "fstab");
+ blobmsg_add_string(&req, "type", "mount");
+
+ ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
+ if (ret)
+ return ENXIO;
blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
if (!tb[CONF_NAME] || !tb[CONF_PATH] || !tb[CONF_AUTOSTART] || !blobmsg_get_bool(tb[CONF_AUTOSTART]))
continue;
+ s = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), s, avl);
+ if (s)
+ continue;
+
+ /* make sure all volumes are ready before starting */
+ if (tb[CONF_VOLUMES])
+ if (checkvolumes(tb[CONF_VOLUMES]))
+ continue;
+
name = strdup(blobmsg_get_string(tb[CONF_NAME]));
ret += uxc_create(name, true);
free(name);
return ret;
}
-static int uxc_delete(char *name)
+static int uxc_delete(char *name, bool force)
{
struct blob_attr *cur, *tb[__CONF_MAX];
+ struct runtime_state *s = NULL;
+ static struct blob_buf req;
+ uint32_t id;
int rem, ret = 0;
bool found = false;
char *fname;
if (!found)
return ENOENT;
+ s = avl_find_element(&runtime, name, s, avl);
+
+ if (s && s->running) {
+ if (force) {
+ ret = uxc_kill(name, SIGKILL);
+ if (ret)
+ goto errout;
+
+ } else {
+ ret = EWOULDBLOCK;
+ goto errout;
+ }
+ }
+
+ if (s) {
+ ret = ubus_lookup_id(ctx, "container", &id);
+ if (ret)
+ goto errout;
+
+ blob_buf_init(&req, 0);
+ blobmsg_add_string(&req, "name", s->container_name);
+ blobmsg_add_string(&req, "instance", s->instance_name);
+
+ if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
+ blob_buf_free(&req);
+ ret=EIO;
+ goto errout;
+ }
+ }
+
if (stat(fname, &sb) == -1) {
ret=ENOENT;
goto 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;
+ char *requiredmounts = NULL;
+ bool autostart = false;
+ bool force = false;
+ int signal = SIGTERM;
+ int c;
if (argc < 2)
return usage();
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;
-
- if (!strcmp("list", argv[1]))
- ret = uxc_list();
- else if (!strcmp("boot", argv[1]))
- ret = uxc_boot();
- else if(!strcmp("start", argv[1])) {
- if (argc < 3)
- goto usage_out;
+ goto conf_out;
- ret = uxc_start(argv[2]);
- } else if(!strcmp("state", argv[1])) {
- if (argc < 3)
- goto usage_out;
+ while (true) {
+ int option_index = 0;
+ c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
+ if (c == -1)
+ break;
- ret = uxc_state(argv[2]);
- } else if(!strcmp("kill", argv[1])) {
- int signal = SIGTERM;
- if (argc < 3)
- goto usage_out;
+ switch (c) {
+ case 'a':
+ autostart = true;
+ break;
- if (argc == 4)
- signal = atoi(argv[3]);
+ case 'b':
+ bundle = optarg;
+ break;
- ret = uxc_kill(argv[2], signal);
- } else if(!strcmp("enable", argv[1])) {
- if (argc < 3)
- goto usage_out;
+ case 'f':
+ force = true;
+ break;
- ret = uxc_set(argv[2], NULL, true, false);
- } else if(!strcmp("disable", argv[1])) {
- if (argc < 3)
- goto usage_out;
+ case 'p':
+ pidfile = optarg;
+ break;
- ret = uxc_set(argv[2], NULL, false, false);
- } else if(!strcmp("delete", argv[1])) {
- if (argc < 3)
- goto usage_out;
+ case 't':
+ tmprwsize = optarg;
+ break;
- ret = uxc_delete(argv[2]);
- } else if(!strcmp("create", argv[1])) {
- bool autostart = false;
- if (argc < 3)
- goto usage_out;
+ case 'v':
+ verbose = true;
+ break;
- if (argc == 5) {
- if (!strncmp("true", argv[4], 5))
- autostart = true;
- else
- autostart = atoi(argv[4]);
- }
+ case 'V':
+ printf("uxc %s\n", UXC_VERSION);
+ exit(0);
- if (argc >= 4) {
- ret = uxc_set(argv[2], argv[3], autostart, true);
- if (ret)
- goto runtime_out;
+ case 'w':
+ writepath = optarg;
+ break;
- reload_conf();
+ case 'm':
+ requiredmounts = optarg;
+ break;
}
- ret = uxc_create(argv[2], false);
- } else
+ }
+
+ if (optind == argc)
goto usage_out;
+ if (!strcmp("list", argv[optind]))
+ cmd = CMD_LIST;
+ else if (!strcmp("boot", argv[optind]))
+ cmd = CMD_BOOT;
+ else if(!strcmp("start", argv[optind]))
+ cmd = CMD_START;
+ else if(!strcmp("state", argv[optind]))
+ cmd = CMD_STATE;
+ else if(!strcmp("kill", argv[optind]))
+ cmd = CMD_KILL;
+ else if(!strcmp("enable", argv[optind]))
+ cmd = CMD_ENABLE;
+ else if(!strcmp("disable", argv[optind]))
+ cmd = CMD_DISABLE;
+ else if(!strcmp("delete", argv[optind]))
+ cmd = CMD_DELETE;
+ else if(!strcmp("create", argv[optind]))
+ cmd = CMD_CREATE;
+
+ switch (cmd) {
+ case CMD_LIST:
+ ret = uxc_list();
+ break;
+
+ case CMD_BOOT:
+ ret = uxc_boot();
+ break;
+
+ case CMD_START:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ ret = uxc_start(argv[optind + 1]);
+ break;
+
+ case CMD_STATE:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ ret = uxc_state(argv[optind + 1]);
+ break;
+
+ case CMD_KILL:
+ if (optind == (argc - 3))
+ signal = atoi(argv[optind + 2]);
+ else if (optind > argc - 2)
+ goto usage_out;
+
+ ret = uxc_kill(argv[optind + 1], signal);
+ break;
+
+ case CMD_ENABLE:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ ret = uxc_set(argv[optind + 1], NULL, true, false, NULL, NULL, NULL, NULL);
+ break;
+
+ case CMD_DISABLE:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ ret = uxc_set(argv[optind + 1], NULL, false, false, NULL, NULL, NULL, NULL);
+ break;
+
+ case CMD_DELETE:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ ret = uxc_delete(argv[optind + 1], force);
+ break;
+
+ case CMD_CREATE:
+ if (optind != argc - 2)
+ goto usage_out;
+
+ if (bundle) {
+ ret = uxc_set(argv[optind + 1], bundle, autostart, true, pidfile, tmprwsize, writepath, requiredmounts);
+ if (ret)
+ goto runtime_out;
+
+ reload_conf();
+ }
+
+ ret = uxc_create(argv[optind + 1], false);
+ break;
+
+ default:
+ goto usage_out;
+ }
+
goto runtime_out;
usage_out:
usage();
runtime_out:
runtime_free();
-state_out:
- blob_buf_free(&state);
conf_out:
blob_buf_free(&conf);
out: