#include "instance.h"
#define UJAIL_BIN_PATH "/sbin/ujail"
+#define CGROUP_BASEDIR "/sys/fs/cgroup/services"
enum {
INSTANCE_ATTR_COMMAND,
INSTANCE_ATTR_JAIL,
INSTANCE_ATTR_TRACE,
INSTANCE_ATTR_SECCOMP,
+ INSTANCE_ATTR_CAPABILITIES,
INSTANCE_ATTR_PIDFILE,
INSTANCE_ATTR_RELOADSIG,
INSTANCE_ATTR_TERMTIMEOUT,
[INSTANCE_ATTR_JAIL] = { "jail", BLOBMSG_TYPE_TABLE },
[INSTANCE_ATTR_TRACE] = { "trace", BLOBMSG_TYPE_BOOL },
[INSTANCE_ATTR_SECCOMP] = { "seccomp", BLOBMSG_TYPE_STRING },
+ [INSTANCE_ATTR_CAPABILITIES] = { "capabilities", BLOBMSG_TYPE_STRING },
[INSTANCE_ATTR_PIDFILE] = { "pidfile", BLOBMSG_TYPE_STRING },
[INSTANCE_ATTR_RELOADSIG] = { "reload_signal", BLOBMSG_TYPE_INT32 },
[INSTANCE_ATTR_TERMTIMEOUT] = { "term_timeout", BLOBMSG_TYPE_INT32 },
JAIL_ATTR_CONSOLE,
JAIL_ATTR_REQUIREJAIL,
JAIL_ATTR_IMMEDIATELY,
+ JAIL_ATTR_PIDFILE,
+ JAIL_ATTR_SETNS,
__JAIL_ATTR_MAX,
};
[JAIL_ATTR_CONSOLE] = { "console", BLOBMSG_TYPE_BOOL },
[JAIL_ATTR_REQUIREJAIL] = { "requirejail", BLOBMSG_TYPE_BOOL },
[JAIL_ATTR_IMMEDIATELY] = { "immediately", BLOBMSG_TYPE_BOOL },
+ [JAIL_ATTR_PIDFILE] = { "pidfile", BLOBMSG_TYPE_STRING },
+ [JAIL_ATTR_SETNS] = { "setns", BLOBMSG_TYPE_ARRAY },
+};
+
+enum {
+ JAIL_SETNS_ATTR_PID,
+ JAIL_SETNS_ATTR_NS,
+ __JAIL_SETNS_ATTR_MAX,
+};
+
+static const struct blobmsg_policy jail_setns_attr[__JAIL_SETNS_ATTR_MAX] = {
+ [JAIL_SETNS_ATTR_PID] = { "pid", BLOBMSG_TYPE_INT32 },
+ [JAIL_SETNS_ATTR_NS] = { "namespaces", BLOBMSG_TYPE_ARRAY },
};
struct instance_netdev {
}
}
+static char *
+instance_gen_setns_argstr(struct blob_attr *attr)
+{
+ struct blob_attr *tb[__JAIL_SETNS_ATTR_MAX];
+ struct blob_attr *cur;
+ int rem, len, total;
+ char *ret;
+
+ blobmsg_parse(jail_setns_attr, __JAIL_SETNS_ATTR_MAX, tb,
+ blobmsg_data(attr), blobmsg_data_len(attr));
+
+ if (!tb[JAIL_SETNS_ATTR_PID] || !tb[JAIL_SETNS_ATTR_NS])
+ return NULL;
+
+ len = snprintf(NULL, 0, "%d:", blobmsg_get_u32(tb[JAIL_SETNS_ATTR_PID]));
+
+ blobmsg_for_each_attr(cur, tb[JAIL_SETNS_ATTR_NS], rem) {
+ char *tmp;
+
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+ return NULL;
+
+ tmp = blobmsg_get_string(cur);
+ if (!tmp)
+ return NULL;
+
+ len += strlen(tmp) + 1;
+ }
+
+ total = len;
+ ret = malloc(total);
+ if (!ret)
+ return NULL;
+
+ len = snprintf(ret, total, "%d:", blobmsg_get_u32(tb[JAIL_SETNS_ATTR_PID]));
+
+ blobmsg_for_each_attr(cur, tb[JAIL_SETNS_ATTR_NS], rem) {
+ strncpy(&ret[len], blobmsg_get_string(cur), total - len);
+ len += strlen(blobmsg_get_string(cur));
+ ret[len++] = ',';
+ }
+ ret[total - 1] = '\0';
+
+ return ret;
+}
+
static inline int
jail_run(struct service_instance *in, char **argv)
{
+ char *term_timeout_str;
struct blobmsg_list_node *var;
struct jail *jail = &in->jail;
int argc = 0;
argv[argc++] = UJAIL_BIN_PATH;
+ if (asprintf(&term_timeout_str, "%d", in->term_timeout) == -1)
+ exit(ENOMEM);
+
+ argv[argc++] = "-t";
+ argv[argc++] = term_timeout_str;
+
if (jail->name) {
argv[argc++] = "-n";
argv[argc++] = jail->name;
argv[argc++] = in->group;
}
+ if (in->capabilities) {
+ argv[argc++] = "-C";
+ argv[argc++] = in->capabilities;
+ }
+
if (in->no_new_privs)
argv[argc++] = "-c";
if (in->immediately)
argv[argc++] = "-i";
+ if (jail->pidfile) {
+ argv[argc++] = "-P";
+ argv[argc++] = jail->pidfile;
+ }
+
if (in->bundle) {
argv[argc++] = "-J";
argv[argc++] = in->bundle;
if (in->require_jail)
argv[argc++] = "-E";
+ blobmsg_list_for_each(&in->env, var) {
+ argv[argc++] = "-e";
+ argv[argc++] = (char *) blobmsg_name(var->data);
+ }
+
blobmsg_list_for_each(&jail->mount, var) {
const char *type = blobmsg_data(var->data);
argv[argc++] = (char *) blobmsg_name(var->data);
}
+ blobmsg_list_for_each(&jail->setns, var) {
+ char *setns_arg = instance_gen_setns_argstr(var->data);
+
+ if (setns_arg) {
+ argv[argc++] = "-j";
+ argv[argc++] = setns_arg;
+ }
+ }
+
argv[argc++] = "--";
return argc;
exit(127);
}
+static void
+instance_add_cgroup(const char *service, const char *instance)
+{
+ struct stat sb;
+ char cgnamebuf[256];
+ int fd;
+
+ if (stat("/sys/fs/cgroup/cgroup.subtree_control", &sb))
+ return;
+
+ mkdir(CGROUP_BASEDIR, 0700);
+
+ snprintf(cgnamebuf, sizeof(cgnamebuf), "%s/%s", CGROUP_BASEDIR, service);
+ mkdir(cgnamebuf, 0700);
+ snprintf(cgnamebuf, sizeof(cgnamebuf), "%s/%s/%s", CGROUP_BASEDIR, service, instance);
+ mkdir(cgnamebuf, 0700);
+ strcat(cgnamebuf, "/cgroup.procs");
+
+ fd = open(cgnamebuf, O_WRONLY);
+ if (fd == -1)
+ return;
+
+ dprintf(fd, "%d", getpid());
+ close(fd);
+}
+
static void
instance_free_stdio(struct service_instance *in)
{
uloop_done();
closefd(opipe[0]);
closefd(epipe[0]);
+ instance_add_cgroup(in->srv->name, in->name);
instance_run(in, opipe[1], epipe[1]);
return;
}
in->halt = halt;
in->restart = in->respawn = false;
kill(in->proc.pid, SIGTERM);
- uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
+ if (!in->has_jail)
+ uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
}
static void
in->halt = true;
in->restart = true;
kill(in->proc.pid, SIGTERM);
- uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
+ if (!in->has_jail)
+ uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
}
static void
if (string_changed(in->seccomp, in_new->seccomp))
return true;
+ if (string_changed(in->capabilities, in_new->capabilities))
+ return true;
+
if (!blobmsg_list_equal(&in->limits, &in_new->limits))
return true;
if (!blobmsg_list_equal(&in->jail.mount, &in_new->jail.mount))
return true;
+ if (!blobmsg_list_equal(&in->jail.setns, &in_new->jail.setns))
+ return true;
+
if (!blobmsg_list_equal(&in->errors, &in_new->errors))
return true;
if (string_changed(in->jail.hostname, in_new->jail.hostname))
return true;
+ if (string_changed(in->jail.pidfile, in_new->jail.pidfile))
+ return true;
+
if (in->jail.procfs != in_new->jail.procfs)
return true;
if (in->jail.console != in_new->jail.console)
return true;
- if (!blobmsg_list_equal(&in->jail.mount, &in_new->jail.mount))
- return true;
-
if (in->watchdog.mode != in_new->watchdog.mode)
return true;
{
struct blob_attr *tb[__JAIL_ATTR_MAX];
struct jail *jail = &in->jail;
+ struct blobmsg_list_node *var;
blobmsg_parse(jail_attr, __JAIL_ATTR_MAX, tb,
blobmsg_data(attr), blobmsg_data_len(attr));
- jail->argc = 2;
+ jail->argc = 4;
if (tb[JAIL_ATTR_REQUIREJAIL] && blobmsg_get_bool(tb[JAIL_ATTR_REQUIREJAIL])) {
in->require_jail = true;
jail->console = true;
jail->argc++;
}
+ if (tb[JAIL_ATTR_PIDFILE]) {
+ jail->pidfile = strdup(blobmsg_get_string(tb[JAIL_ATTR_PIDFILE]));
+ jail->argc += 2;
+ }
+
+ if (tb[JAIL_ATTR_SETNS]) {
+ struct blob_attr *cur;
+ int rem;
+
+ blobmsg_for_each_attr(cur, tb[JAIL_ATTR_SETNS], rem)
+ jail->argc += 2;
+ blobmsg_list_fill(&jail->setns, blobmsg_data(tb[JAIL_ATTR_SETNS]),
+ blobmsg_data_len(tb[JAIL_ATTR_SETNS]), true);
+ }
if (tb[JAIL_ATTR_MOUNT]) {
struct blob_attr *cur;
jail->argc += 2;
instance_fill_array(&jail->mount, tb[JAIL_ATTR_MOUNT], NULL, false);
}
+
+ blobmsg_list_for_each(&in->env, var)
+ jail->argc += 2;
+
if (in->seccomp)
jail->argc += 2;
+ if (in->capabilities)
+ jail->argc += 2;
+
if (in->user)
jail->argc += 2;
if (!in->trace && tb[INSTANCE_ATTR_SECCOMP])
in->seccomp = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_SECCOMP]));
+ if (tb[INSTANCE_ATTR_CAPABILITIES])
+ in->capabilities = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_CAPABILITIES]));
+
if (tb[INSTANCE_ATTR_EXTROOT])
in->extroot = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_EXTROOT]));
if (tb[INSTANCE_ATTR_RELOADSIG])
in->reload_signal = blobmsg_get_u32(tb[INSTANCE_ATTR_RELOADSIG]);
- if (!in->trace && tb[INSTANCE_ATTR_JAIL])
- in->has_jail = instance_jail_parse(in, tb[INSTANCE_ATTR_JAIL]);
-
- if (in->has_jail) {
- r = stat(UJAIL_BIN_PATH, &s);
- if (r < 0) {
- if (in->require_jail) {
- ERROR("Cannot jail service %s::%s. %s: %m (%d)\n",
- in->srv->name, in->name, UJAIL_BIN_PATH, r);
- return false;
- }
- DEBUG(2, "unable to find %s: %m (%d)\n", UJAIL_BIN_PATH, r);
- in->has_jail = false;
- }
- }
-
if (tb[INSTANCE_ATTR_STDOUT] && blobmsg_get_bool(tb[INSTANCE_ATTR_STDOUT]))
in->_stdout.fd.fd = -1;
}
}
+ if (!in->trace && tb[INSTANCE_ATTR_JAIL])
+ in->has_jail = instance_jail_parse(in, tb[INSTANCE_ATTR_JAIL]);
+
+ if (in->has_jail) {
+ r = stat(UJAIL_BIN_PATH, &s);
+ if (r < 0) {
+ if (in->require_jail) {
+ ERROR("Cannot jail service %s::%s. %s: %m (%d)\n",
+ in->srv->name, in->name, UJAIL_BIN_PATH, r);
+ return false;
+ }
+ DEBUG(2, "unable to find %s: %m (%d)\n", UJAIL_BIN_PATH, r);
+ in->has_jail = false;
+ }
+ }
+
return true;
}
blobmsg_list_free(&in->limits);
blobmsg_list_free(&in->errors);
blobmsg_list_free(&in->jail.mount);
+ blobmsg_list_free(&in->jail.setns);
}
static void
blobmsg_list_move(&in->limits, &in_src->limits);
blobmsg_list_move(&in->errors, &in_src->errors);
blobmsg_list_move(&in->jail.mount, &in_src->jail.mount);
+ blobmsg_list_move(&in->jail.setns, &in_src->jail.setns);
in->trigger = in_src->trigger;
in->command = in_src->command;
in->respawn = in_src->respawn;
instance_config_move_strdup(&in->pidfile, in_src->pidfile);
instance_config_move_strdup(&in->seccomp, in_src->seccomp);
+ instance_config_move_strdup(&in->capabilities, in_src->capabilities);
instance_config_move_strdup(&in->bundle, in_src->bundle);
instance_config_move_strdup(&in->extroot, in_src->extroot);
instance_config_move_strdup(&in->overlaydir, in_src->overlaydir);
instance_config_move_strdup(&in->group, in_src->group);
instance_config_move_strdup(&in->jail.name, in_src->jail.name);
instance_config_move_strdup(&in->jail.hostname, in_src->jail.hostname);
+ instance_config_move_strdup(&in->jail.pidfile, in_src->jail.pidfile);
free(in->config);
in->config = in_src->config;
free(in->bundle);
free(in->jail.name);
free(in->jail.hostname);
+ free(in->jail.pidfile);
free(in->seccomp);
+ free(in->capabilities);
free(in->pidfile);
free(in);
}
blobmsg_list_simple_init(&in->limits);
blobmsg_list_simple_init(&in->errors);
blobmsg_list_simple_init(&in->jail.mount);
+ blobmsg_list_simple_init(&in->jail.setns);
in->watchdog.timeout.cb = instance_watchdog;
if (in->seccomp)
blobmsg_add_string(b, "seccomp", in->seccomp);
+ if (in->capabilities)
+ blobmsg_add_string(b, "capabilities", in->capabilities);
+
if (in->pidfile)
blobmsg_add_string(b, "pidfile", in->pidfile);
blobmsg_add_u8(b, "userns", in->jail.userns);
blobmsg_add_u8(b, "cgroupsns", in->jail.cgroupsns);
} else {
+ if (in->jail.pidfile)
+ blobmsg_add_string(b, "pidfile", in->jail.pidfile);
+
blobmsg_add_u8(b, "immediately", in->immediately);
}
blobmsg_add_u8(b, "console", (in->console.fd.fd > -1));
blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data));
blobmsg_close_table(b, e);
}
+
+ if (!avl_is_empty(&in->jail.setns.avl)) {
+ struct blobmsg_list_node *var;
+ void *s = blobmsg_open_array(b, "setns");
+ blobmsg_list_for_each(&in->jail.setns, var)
+ blobmsg_add_blob(b, var->data);
+ blobmsg_close_array(b, s);
+ }
}
if (in->extroot)