X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=uxc.c;h=d8db49d104a4be2c7ee804711c7e99bbaffdf6ea;hb=59f769920276229a3ee2dcbe75ea54095cf14ffe;hp=6013637da666b8f35699f33d164a1a5638f675cb;hpb=bb4a4467b1d5ccbc84d89822235a38b63134e5a4;p=project%2Fprocd.git diff --git a/uxc.c b/uxc.c index 6013637..d8db49d 100644 --- a/uxc.c +++ b/uxc.c @@ -11,6 +11,10 @@ * GNU General Public License for more details. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -23,41 +27,84 @@ #include #include #include +#include #include #include #include +#include #include "log.h" -#define UXC_CONFDIR "/etc/uxc" -#define UXC_RUNDIR "/var/run/uxc" +#define UXC_VERSION "0.2" +#define OCI_VERSION_STRING "1.0.2" +#define UXC_ETC_CONFDIR "/etc/uxc" +#define UXC_VOL_CONFDIR "/var/run/uxc" + +static bool verbose = false; +static bool json_output = false; +static char *confdir = UXC_ETC_CONFDIR; struct runtime_state { struct avl_node avl; - const char *container_name; - const char *instance_name; - const char *jail_name; + char *container_name; + char *instance_name; + char *jail_name; bool running; - int pid; + int runtime_pid; int exitcode; + 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:fjm:p:t:vVw:" +static struct option long_options[] = { + {"autostart", no_argument, 0, 'a' }, + {"bundle", required_argument, 0, 'b' }, + {"force", no_argument, 0, 'f' }, + {"json", no_argument, 0, 'j' }, + {"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 [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("\tstop {conf}\t\t\tstop 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 [--json]\t\t\t\tlist all configured containers\n"); + printf("\tcreate \t\t\t\t\t(re-)create \n"); + printf(" [--bundle ]\t\t\tOCI bundle at \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(" [--mounts v1,v2,...,vN]\t\trequire filesystems to be available\n"); + printf("\tstart \t\t\t\t\tstart container \n"); + printf("\tstate \t\t\t\t\tget state of container \n"); + printf("\tkill []\t\t\t\tsend signal to container \n"); + printf("\tenable \t\t\t\t\tstart container on boot\n"); + printf("\tdisable \t\t\t\t\tdon't start container on boot\n"); + printf("\tdelete [--force]\t\t\t\tdelete \n"); return EINVAL; } @@ -66,6 +113,10 @@ enum { CONF_PATH, CONF_JAIL, CONF_AUTOSTART, + CONF_PIDFILE, + CONF_TEMP_OVERLAY_SIZE, + CONF_WRITE_OVERLAY_PATH, + CONF_VOLUMES, __CONF_MAX, }; @@ -74,22 +125,31 @@ static const struct blobmsg_policy conf_policy[__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; + struct stat sb; - if (asprintf(&globstr, "%s/*.json", load_state?UXC_RUNDIR:UXC_CONFDIR) == -1) + if (!stat(UXC_VOL_CONFDIR, &sb)) { + if (sb.st_mode & S_IFDIR) + confdir = UXC_VOL_CONFDIR; + } + + if (asprintf(&globstr, "%s/*.json", 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); @@ -97,14 +157,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; @@ -155,6 +215,60 @@ runtime_alloc(const char *container_name) return s; } +enum { + STATE_OCIVERSION, + STATE_ID, + STATE_STATUS, + STATE_PID, + STATE_BUNDLE, + STATE_ANNOTATIONS, + __STATE_MAX, +}; + +static const struct blobmsg_policy state_policy[__STATE_MAX] = { + [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING }, + [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, + [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING }, + [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 }, + [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING }, + [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE }, +}; + + +static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr **ocistate = (struct blob_attr **)req->priv; + struct blob_attr *tb[__STATE_MAX]; + + blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); + + if (!tb[STATE_OCIVERSION] || + !tb[STATE_ID] || + !tb[STATE_STATUS] || + !tb[STATE_BUNDLE]) + return; + + *ocistate = blob_memdup(msg); +} + +static void get_ocistate(struct blob_attr **ocistate, const char *name) +{ + char *objname; + unsigned int id; + int ret; + *ocistate = NULL; + + 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); +} + static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg) { struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX]; @@ -198,7 +312,7 @@ static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg) rs = runtime_alloc(container_name); rs->instance_name = strdup(instance_name); rs->jail_name = strdup(jail_name); - rs->pid = pid; + rs->runtime_pid = pid; rs->exitcode = exitcode; rs->running = running; avl_insert(&runtime, &rs->avl); @@ -210,6 +324,7 @@ static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg) static int runtime_load(void) { + struct runtime_state *item, *tmp; uint32_t id; avl_init(&runtime, avl_strcmp, false, NULL); @@ -217,6 +332,10 @@ static int runtime_load(void) ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000)) return EIO; + + avl_for_each_element_safe(&runtime, item, avl, tmp) + get_ocistate(&item->ocistate, item->jail_name); + return 0; } @@ -226,19 +345,94 @@ static void runtime_free(void) avl_for_each_element_safe(&runtime, item, avl, tmp) { avl_delete(&runtime, &item->avl); + free(item->instance_name); + free(item->jail_name); + free(item->ocistate); free(item); } return; } -static int uxc_list(void) +static int uxc_state(char *name) { + struct runtime_state *s = avl_find_element(&runtime, name, s, avl); + struct blob_attr *ocistate = NULL; struct blob_attr *cur, *tb[__CONF_MAX]; int rem; + char *bundle = NULL; + char *jail_name = NULL; + char *state = NULL; + char *tmp; + static struct blob_buf buf; + + if (s) + ocistate = s->ocistate; + + if (ocistate) { + state = blobmsg_format_json_indent(ocistate, true, 0); + if (!state) + return 1; + + printf("%s\n", state); + free(state); + return 0; + } + + 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]) + continue; + + if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) { + if (tb[CONF_JAIL]) + jail_name = blobmsg_get_string(tb[CONF_JAIL]); + else + jail_name = name; + + bundle = blobmsg_get_string(tb[CONF_PATH]); + break; + } + } + + if (!bundle) + return ENOENT; + + blob_buf_init(&buf, 0); + blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING); + blobmsg_add_string(&buf, "id", jail_name); + blobmsg_add_string(&buf, "status", s?"stopped":"uninitialized"); + blobmsg_add_string(&buf, "bundle", bundle); + + tmp = blobmsg_format_json_indent(buf.head, true, 0); + if (!tmp) { + blob_buf_free(&buf); + return ENOMEM; + } + + printf("%s\n", tmp); + free(tmp); + + blob_buf_free(&buf); + + return 0; +} + +static int uxc_list(void) +{ + struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX]; + int rem; struct runtime_state *s = NULL; - char *name; + char *name, *ocistatus, *status, *tmp; + int container_pid = -1; bool autostart; + static struct blob_buf buf; + void *arr, *obj; + + if (json_output) { + blob_buf_init(&buf, 0); + arr = blobmsg_open_array(&buf, ""); + } blobmsg_for_each_attr(cur, blob_data(conf.head), rem) { blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); @@ -246,35 +440,87 @@ static int uxc_list(void) continue; autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]); - + ocistatus = NULL; + container_pid = 0; name = blobmsg_get_string(tb[CONF_NAME]); s = avl_find_element(&runtime, name, s, avl); - printf("[%c] %s %s", autostart?'*':' ', name, (s && s->running)?"RUNNING":"STOPPED"); + if (s && s->ocistate) { + blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(s->ocistate), blobmsg_len(s->ocistate)); + ocistatus = blobmsg_get_string(ts[STATE_STATUS]); + container_pid = blobmsg_get_u32(ts[STATE_PID]); + } + + status = ocistatus?:(s && s->running)?"creating":"stopped"; - if (s && !s->running && (s->exitcode >= 0)) - printf(" exitcode: %d (%s)", s->exitcode, strerror(s->exitcode)); + if (json_output) { + obj = blobmsg_open_table(&buf, ""); + blobmsg_add_string(&buf, "name", name); + blobmsg_add_string(&buf, "status", status); + blobmsg_add_u8(&buf, "autostart", autostart); + } else { + printf("[%c] %s %s", autostart?'*':' ', name, status); + } - if (s && s->running && (s->pid >= 0)) - printf(" pid: %d", s->pid); + if (s && !s->running && (s->exitcode >= 0)) { + if (json_output) + blobmsg_add_u32(&buf, "exitcode", s->exitcode); + else + printf(" exitcode: %d (%s)", s->exitcode, strerror(s->exitcode)); + } + + if (s && s->running && (s->runtime_pid >= 0)) { + if (json_output) + blobmsg_add_u32(&buf, "runtime_pid", s->runtime_pid); + else + printf(" runtime pid: %d", s->runtime_pid); + } + + if (s && s->running && (container_pid >= 0)) { + if (json_output) + blobmsg_add_u32(&buf, "container_pid", container_pid); + else + printf(" container pid: %d", container_pid); + } - printf("\n"); + if (!json_output) + printf("\n"); + else + blobmsg_close_table(&buf, obj); } + if (json_output) { + blobmsg_close_array(&buf, arr); + tmp = blobmsg_format_json_indent(buf.head, true, 0); + if (!tmp) { + blob_buf_free(&buf); + return ENOMEM; + } + printf("%s\n", tmp); + free(tmp); + blob_buf_free(&buf); + }; + return 0; } -static int uxc_start(char *name) +static int uxc_create(char *name, bool immediately) { static struct blob_buf req; struct blob_attr *cur, *tb[__CONF_MAX]; 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; + s = avl_find_element(&runtime, name, s, avl); + + if (s && (s->running)) + return EEXIST; + 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]) @@ -284,22 +530,25 @@ static int uxc_start(char *name) continue; found = true; - path = strdup(blobmsg_get_string(tb[CONF_PATH])); - - break; } if (!found) return ENOENT; - s = avl_find_element(&runtime, name, s, avl); + path = blobmsg_get_string(tb[CONF_PATH]); - if (s && (s->running)) - return EEXIST; + if (tb[CONF_PIDFILE]) + pidfile = blobmsg_get_string(tb[CONF_PIDFILE]); + + if (tb[CONF_TEMP_OVERLAY_SIZE]) + tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]); + + if (tb[CONF_WRITE_OVERLAY_PATH]) + writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]); if (tb[CONF_JAIL]) - jailname = strdup(blobmsg_get_string(tb[CONF_JAIL])); + jailname = blobmsg_get_string(tb[CONF_JAIL]); blob_buf_init(&req, 0); blobmsg_add_string(&req, "name", name); @@ -308,29 +557,63 @@ static int uxc_start(char *name) blobmsg_add_string(&req, "bundle", path); 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) { + char *tmp; + tmp = blobmsg_format_json_indent(req.head, true, 1); + if (!tmp) + return ENOMEM; + + fprintf(stderr, "adding container to procd:\n\t%s\n", tmp); + free(tmp); + } + ret = 0; if (ubus_lookup_id(ctx, "container", &id) || ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) { + blob_buf_free(&req); ret = EIO; } - free(jailname); - free(path); - blob_buf_free(&req); - return ret; } -static int uxc_stop(char *name) +static int uxc_start(const char *name) +{ + char *objname; + unsigned int id; + + if (asprintf(&objname, "container.%s", name) == -1) + return ENOMEM; + + if (ubus_lookup_id(ctx, objname, &id)) + return ENOENT; + + return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000); +} + +static int uxc_kill(char *name, int signal) { 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; @@ -355,21 +638,25 @@ static int uxc_stop(char *name) return ENOENT; blob_buf_init(&req, 0); + blobmsg_add_u32(&req, "signal", signal); blobmsg_add_string(&req, "name", name); - blobmsg_add_string(&req, "instance", s->instance_name); - ret = 0; - if (ubus_lookup_id(ctx, "container", &id) || - ubus_invoke(ctx, id, "del", req.head, NULL, NULL, 3000)) { - ret = EIO; - } + if (asprintf(&objname, "container.%s", name) == -1) + return ENOMEM; - blob_buf_free(&req); + ret = ubus_lookup_id(ctx, objname, &id); + free(objname); + if (ret) + return ENOENT; - return ret; + 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]; @@ -377,6 +664,10 @@ static int uxc_set(char *name, char *path, bool autostart, bool add) 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; @@ -409,59 +700,231 @@ static int uxc_set(char *name, char *path, bool autostart, bool add) return ENOTDIR; } - ret = mkdir(UXC_CONFDIR, 0755); + ret = mkdir(confdir, 0755); if (ret && errno != EEXIST) return ret; - if (asprintf(&fname, "%s/%s.json", UXC_CONFDIR, name) < 1) + if (asprintf(&fname, "%s/%s.json", confdir, name) == -1) return ENOMEM; f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (f < 0) return errno; - if (!add) - keeppath = strdup(blobmsg_get_string(tb[CONF_PATH])); + if (!add) { + keeppath = blobmsg_get_string(tb[CONF_PATH]); + if (tb[CONF_WRITE_OVERLAY_PATH]) + writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]); + + if (tb[CONF_TEMP_OVERLAY_SIZE]) + tmprwsize = 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); - dprintf(f, "%s\n", blobmsg_format_json_indent(req.head, true, 0)); + if (tmprwsize) + blobmsg_add_string(&req, "temp-overlay-size", tmprwsize); - if (!add) - free(keeppath); + 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); + } + tmp = blobmsg_format_json_indent(req.head, true, 0); + if (tmp) { + dprintf(f, "%s\n", tmp); + free(tmp); + } blob_buf_free(&req); + close(f); + + 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; + } - /* ToDo: tell ujail to run createRuntime and createContainer hooks */ 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_start(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; @@ -486,6 +949,36 @@ static int uxc_delete(char *name) 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; @@ -499,9 +992,25 @@ errout: return ret; } +static void reload_conf(void) +{ + blob_buf_free(&conf); + 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(); @@ -510,74 +1019,163 @@ 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); + ret = runtime_load(); 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; + while (true) { + int option_index = 0; + c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index); + if (c == -1) + break; - ret = uxc_start(argv[2]); - } else if(!strcmp("stop", argv[1])) { - if (argc < 3) - goto usage_out; + switch (c) { + case 'a': + autostart = true; + break; - ret = uxc_stop(argv[2]); - } else if(!strcmp("enable", argv[1])) { - if (argc < 3) - goto usage_out; + case 'b': + bundle = optarg; + break; - ret = uxc_set(argv[2], NULL, true, false); - } else if(!strcmp("disable", argv[1])) { - if (argc < 3) - goto usage_out; + case 'f': + force = true; + break; - ret = uxc_set(argv[2], NULL, false, false); - } else if(!strcmp("delete", argv[1])) { - if (argc < 3) - goto usage_out; + case 'j': + json_output = true; + break; - ret = uxc_delete(argv[2]); - } else if(!strcmp("create", argv[1])) { - bool autostart = false; - if (argc < 4) - goto usage_out; + case 'p': + pidfile = optarg; + break; - if (argc == 5) { - if (!strncmp("true", argv[4], 5)) - autostart = true; - else - autostart = atoi(argv[4]); + case 't': + tmprwsize = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'V': + printf("uxc %s\n", UXC_VERSION); + exit(0); + + case 'w': + writepath = optarg; + break; + + case 'm': + requiredmounts = optarg; + break; } - ret = uxc_set(argv[2], argv[3], autostart, true); - } 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: