int value;
};
+struct limit_opt {
+ char *name;
+ int value;
+};
+
/*
* The lxc_conf of the container currently being worked on in an
* API call
static struct caps_opt caps_opt[] = {};
#endif
+static struct limit_opt limit_opt[] = {
+#ifdef RLIMIT_AS
+ { "as", RLIMIT_AS },
+#endif
+#ifdef RLIMIT_CORE
+ { "core", RLIMIT_CORE },
+#endif
+#ifdef RLIMIT_CPU
+ { "cpu", RLIMIT_CPU },
+#endif
+#ifdef RLIMIT_DATA
+ { "data", RLIMIT_DATA },
+#endif
+#ifdef RLIMIT_FSIZE
+ { "fsize", RLIMIT_FSIZE },
+#endif
+#ifdef RLIMIT_LOCKS
+ { "locks", RLIMIT_LOCKS },
+#endif
+#ifdef RLIMIT_MEMLOCK
+ { "memlock", RLIMIT_MEMLOCK },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ { "msgqueue", RLIMIT_MSGQUEUE },
+#endif
+#ifdef RLIMIT_NICE
+ { "nice", RLIMIT_NICE },
+#endif
+#ifdef RLIMIT_NOFILE
+ { "nofile", RLIMIT_NOFILE },
+#endif
+#ifdef RLIMIT_NPROC
+ { "nproc", RLIMIT_NPROC },
+#endif
+#ifdef RLIMIT_RSS
+ { "rss", RLIMIT_RSS },
+#endif
+#ifdef RLIMIT_RTPRIO
+ { "rtprio", RLIMIT_RTPRIO },
+#endif
+#ifdef RLIMIT_RTTIME
+ { "rttime", RLIMIT_RTTIME },
+#endif
+#ifdef RLIMIT_SIGPENDING
+ { "sigpending", RLIMIT_SIGPENDING },
+#endif
+#ifdef RLIMIT_STACK
+ { "stack", RLIMIT_STACK },
+#endif
+};
+
static int run_buffer(char *buffer)
{
struct lxc_popen_FILE *f;
return 0;
}
+static int parse_resource(const char *res) {
+ size_t i;
+ int resid = -1;
+
+ for (i = 0; i < sizeof(limit_opt)/sizeof(limit_opt[0]); ++i) {
+ if (strcmp(res, limit_opt[i].name) == 0)
+ return limit_opt[i].value;
+ }
+
+ /* try to see if it's numeric, so the user may specify
+ * resources that the running kernel knows about but
+ * we don't */
+ if (lxc_safe_int(res, &resid) == 0)
+ return resid;
+ return -1;
+}
+
+int setup_resource_limits(struct lxc_list *limits, pid_t pid) {
+ struct lxc_list *it;
+ struct lxc_limit *lim;
+ int resid;
+
+ lxc_list_for_each(it, limits) {
+ lim = it->elem;
+
+ resid = parse_resource(lim->resource);
+ if (resid < 0) {
+ ERROR("unknown resource %s", lim->resource);
+ return -1;
+ }
+
+ if (prlimit(pid, resid, &lim->limit, NULL) != 0) {
+ ERROR("failed to set limit %s: %s", lim->resource, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
/* try to move physical nics to the init netns */
void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf)
{
lxc_list_init(&new->includes);
lxc_list_init(&new->aliens);
lxc_list_init(&new->environment);
+ lxc_list_init(&new->limits);
for (i=0; i<NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]);
lxc_list_init(&new->groups);
return 0;
}
+int lxc_clear_limits(struct lxc_conf *c, const char *key)
+{
+ struct lxc_list *it, *next;
+ bool all = false;
+ const char *k = NULL;
+
+ if (strcmp(key, "lxc.limit") == 0)
+ all = true;
+ else if (strncmp(key, "lxc.limit.", sizeof("lxc.limit.")-1) == 0)
+ k = key + sizeof("lxc.limit.")-1;
+ else
+ return -1;
+
+ lxc_list_for_each_safe(it, &c->limits, next) {
+ struct lxc_limit *lim = it->elem;
+ if (!all && strcmp(lim->resource, k) != 0)
+ continue;
+ lxc_list_del(it);
+ free(lim->resource);
+ free(lim);
+ free(it);
+ }
+ return 0;
+}
+
int lxc_clear_groups(struct lxc_conf *c)
{
struct lxc_list *it,*next;
lxc_clear_includes(conf);
lxc_clear_aliens(conf);
lxc_clear_environment(conf);
+ lxc_clear_limits(conf, "lxc.limit");
free(conf);
}
#include <net/if.h>
#include <sys/param.h>
#include <sys/types.h>
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
#include <stdbool.h>
#include "list.h"
char *value;
};
+#if !HAVE_SYS_RESOURCE_H
+# define RLIM_INFINITY ((unsigned long)-1)
+struct rlimit {
+ unsigned long rlim_cur;
+ unsigned long rlim_max;
+};
+#endif
+/*
+ * Defines a structure to configure resource limits to set via setrlimit().
+ * @resource : the resource name in lowercase without the RLIMIT_ prefix
+ * @limit : the limit to set
+ */
+struct lxc_limit {
+ char *resource;
+ struct rlimit limit;
+};
+
enum idtype {
ID_TYPE_UID,
ID_TYPE_GID
/* Whether PR_SET_NO_NEW_PRIVS will be set for the container. */
bool no_new_privs;
+
+ /* RLIMIT_* limits */
+ struct lxc_list limits;
};
#ifdef HAVE_TLS
extern int lxc_clear_idmaps(struct lxc_conf *c);
extern int lxc_clear_groups(struct lxc_conf *c);
extern int lxc_clear_environment(struct lxc_conf *c);
+extern int lxc_clear_limits(struct lxc_conf *c, const char *key);
extern int lxc_delete_autodev(struct lxc_handler *handler);
extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
struct cgroup_process_info;
extern int lxc_setup(struct lxc_handler *handler);
+extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
+
extern void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf);
extern int find_unmapped_nsuid(struct lxc_conf *conf, enum idtype idtype);
static int config_init_gid(const char *, const char *, struct lxc_conf *);
static int config_ephemeral(const char *, const char *, struct lxc_conf *);
static int config_no_new_privs(const char *, const char *, struct lxc_conf *);
+static int config_limit(const char *, const char *, struct lxc_conf *);
static struct lxc_config_t config[] = {
{ "lxc.ephemeral", config_ephemeral },
{ "lxc.syslog", config_syslog },
{ "lxc.no_new_privs", config_no_new_privs },
+ { "lxc.limit", config_limit },
};
struct signame {
return -1;
}
+static bool parse_limit_value(const char **value, unsigned long *res) {
+ char *endptr = NULL;
+
+ if (strncmp(*value, "unlimited", sizeof("unlimited")-1) == 0) {
+ *res = RLIM_INFINITY;
+ *value += sizeof("unlimited")-1;
+ return true;
+ }
+
+ errno = 0;
+ *res = strtoul(*value, &endptr, 10);
+ if (errno || !endptr)
+ return false;
+ *value = endptr;
+
+ return true;
+}
+
+static int config_limit(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
+{
+ struct lxc_list *limlist = NULL;
+ struct lxc_limit *limelem = NULL;
+ struct lxc_list *iter;
+ struct rlimit limit;
+ unsigned long limit_value;
+
+ if (!value || strlen(value) == 0)
+ return lxc_clear_limits(lxc_conf, key);
+
+ if (strncmp(key, "lxc.limit.", sizeof("lxc.limit.")-1) != 0)
+ return -1;
+
+ key += sizeof("lxc.limit.")-1;
+
+ /* soft limit comes first in the value */
+ if (!parse_limit_value(&value, &limit_value))
+ return -1;
+ limit.rlim_cur = limit_value;
+
+ /* skip spaces and a colon */
+ while (isspace(*value))
+ ++value;
+ if (*value == ':')
+ ++value;
+ else if (*value) /* any other character is an error here */
+ return -1;
+ while (isspace(*value))
+ ++value;
+
+ /* optional hard limit */
+ if (*value) {
+ if (!parse_limit_value(&value, &limit_value))
+ return -1;
+ limit.rlim_max = limit_value;
+ /* check for trailing garbage */
+ while (isspace(*value))
+ ++value;
+ if (*value)
+ return -1;
+ } else {
+ /* a single value sets both hard and soft limit */
+ limit.rlim_max = limit.rlim_cur;
+ }
+
+ /* find existing list element */
+ lxc_list_for_each(iter, &lxc_conf->limits) {
+ limelem = iter->elem;
+ if (!strcmp(key, limelem->resource)) {
+ limelem->limit = limit;
+ return 0;
+ }
+ }
+
+ /* allocate list element */
+ limlist = malloc(sizeof(*limlist));
+ if (!limlist)
+ goto out;
+
+ limelem = malloc(sizeof(*limelem));
+ if (!limelem)
+ goto out;
+ memset(limelem, 0, sizeof(*limelem));
+
+ limelem->resource = strdup(key);
+ if (!limelem->resource)
+ goto out;
+ limelem->limit = limit;
+
+ limlist->elem = limelem;
+
+ lxc_list_add_tail(&lxc_conf->limits, limlist);
+
+ return 0;
+
+out:
+ free(limlist);
+ if (limelem) {
+ free(limelem->resource);
+ free(limelem);
+ }
+ return -1;
+}
+
static int config_idmap(const char *key, const char *value, struct lxc_conf *lxc_conf)
{
char *token = "lxc.id_map";
return fulllen;
}
+/*
+ * If you ask for a specific value, i.e. lxc.limit.nofile, then just the value
+ * will be printed. If you ask for 'lxc.limit', then all limit entries will be
+ * printed, in 'lxc.limit.resource = value' format.
+ */
+static int lxc_get_limit_entry(struct lxc_conf *c, char *retv, int inlen,
+ const char *key)
+{
+ int fulllen = 0, len;
+ int all = 0;
+ struct lxc_list *it;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ if (strcmp(key, "all") == 0)
+ all = 1;
+
+ lxc_list_for_each(it, &c->limits) {
+ char buf[LXC_NUMSTRLEN64*2+2]; /* 2 colon separated 64 bit integers or the word 'unlimited' */
+ int partlen;
+ struct lxc_limit *lim = it->elem;
+
+ if (lim->limit.rlim_cur == RLIM_INFINITY) {
+ memcpy(buf, "unlimited", sizeof("unlimited"));
+ partlen = sizeof("unlimited")-1;
+ } else {
+ partlen = sprintf(buf, "%lu", lim->limit.rlim_cur);
+ }
+ if (lim->limit.rlim_cur != lim->limit.rlim_max) {
+ if (lim->limit.rlim_max == RLIM_INFINITY) {
+ memcpy(buf+partlen, ":unlimited", sizeof(":unlimited"));
+ } else {
+ sprintf(buf+partlen, ":%lu", lim->limit.rlim_max);
+ }
+ }
+
+ if (all) {
+ strprint(retv, inlen, "lxc.limit.%s = %s\n", lim->resource, buf);
+ } else if (strcmp(lim->resource, key) == 0) {
+ strprint(retv, inlen, "%s", buf);
+ }
+ }
+
+ return fulllen;
+}
+
static int lxc_get_item_hooks(struct lxc_conf *c, char *retv, int inlen,
const char *key)
{
v = c->syslog;
else if (strcmp(key, "lxc.no_new_privs") == 0)
return lxc_get_conf_int(c, retv, inlen, c->no_new_privs);
+ else if (strcmp(key, "lxc.limit") == 0) // all limits
+ return lxc_get_limit_entry(c, retv, inlen, "all");
+ else if (strncmp(key, "lxc.limit.", 10) == 0) // specific limit
+ return lxc_get_limit_entry(c, retv, inlen, key + 10);
else return -1;
if (!v)
return lxc_clear_environment(c);
else if (strncmp(key, "lxc.id_map", 10) == 0)
return lxc_clear_idmaps(c);
+ else if (strncmp(key, "lxc.limit", 9) == 0)
+ return lxc_clear_limits(c, key);
return -1;
}