--- /dev/null
+From 67a23f3f3fa65f1646ad20a8591b15895cada0a4 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 4 Nov 2016 10:19:07 +0100
+Subject: [PATCH 09/14] conf: implement resource limits
+
+This adds lxc.limit.<name> options consisting of one or two
+colon separated numerical values (soft and optional hard
+limit). If only one number is specified it'll be used for
+both soft and hard limit. Additionally the word 'unlimited'
+can be used instead of numbers.
+
+Eg.
+ lxc.limit.nofile = 30000:32768
+ lxc.limit.stack = unlimited
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ configure.ac | 2 +-
+ src/lxc/attach.c | 5 ++
+ src/lxc/conf.c | 122 +++++++++++++++++++++++++++++++++++++++++
+ src/lxc/conf.h | 26 +++++++++
+ src/lxc/confile.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/lxc/start.c | 5 ++
+ 6 files changed, 320 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index fa3926a9..2857d5ae 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -633,7 +633,7 @@ AM_CONDITIONAL([IS_BIONIC], [test "x$is_bionic" = "xyes"])
+ AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include <sys/prctl.h>])
+
+ # Check for some headers
+-AC_CHECK_HEADERS([sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h])
++AC_CHECK_HEADERS([sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h sys/resource.h])
+
+ # lookup major()/minor()/makedev()
+ AC_HEADER_MAJOR
+diff --git a/src/lxc/attach.c b/src/lxc/attach.c
+index 119b9c14..6fdc6e8a 100644
+--- a/src/lxc/attach.c
++++ b/src/lxc/attach.c
+@@ -854,6 +854,11 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
+ goto on_error;
+ }
+
++ /* Setup resource limits */
++ if (!lxc_list_empty(&init_ctx->container->lxc_conf->limits) && setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid)) {
++ goto on_error;
++ }
++
+ /* Open /proc before setns() to the containers namespace so we
+ * don't rely on any information from inside the container.
+ */
+diff --git a/src/lxc/conf.c b/src/lxc/conf.c
+index 3bedcf0f..d68ab2e2 100644
+--- a/src/lxc/conf.c
++++ b/src/lxc/conf.c
+@@ -243,6 +243,11 @@ struct caps_opt {
+ int value;
+ };
+
++struct limit_opt {
++ char *name;
++ int value;
++};
++
+ /*
+ * The lxc_conf of the container currently being worked on in an
+ * API call
+@@ -376,6 +381,57 @@ static struct caps_opt caps_opt[] = {
+ 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;
+@@ -2534,6 +2590,45 @@ static int setup_network(struct lxc_list *network)
+ 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)
+ {
+@@ -2620,6 +2715,7 @@ struct lxc_conf *lxc_conf_init(void)
+ 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);
+@@ -4317,6 +4413,31 @@ int lxc_clear_cgroups(struct lxc_conf *c, const char *key)
+ 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;
+@@ -4462,6 +4583,7 @@ void lxc_conf_free(struct lxc_conf *conf)
+ lxc_clear_includes(conf);
+ lxc_clear_aliens(conf);
+ lxc_clear_environment(conf);
++ lxc_clear_limits(conf, "lxc.limit");
+ free(conf);
+ }
+
+diff --git a/src/lxc/conf.h b/src/lxc/conf.h
+index c790bf7c..7dc05288 100644
+--- a/src/lxc/conf.h
++++ b/src/lxc/conf.h
+@@ -30,6 +30,9 @@
+ #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"
+@@ -149,6 +152,23 @@ struct lxc_cgroup {
+ 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
+@@ -378,6 +398,9 @@ struct lxc_conf {
+
+ /* indicator if the container will be destroyed on shutdown */
+ unsigned int ephemeral;
++
++ /* RLIMIT_* limits */
++ struct lxc_list limits;
+ };
+
+ #ifdef HAVE_TLS
+@@ -421,6 +444,7 @@ extern int lxc_clear_hooks(struct lxc_conf *c, const char *key);
+ 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,
+@@ -433,6 +457,8 @@ 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);
+diff --git a/src/lxc/confile.c b/src/lxc/confile.c
+index 9b22c6d3..89f8c625 100644
+--- a/src/lxc/confile.c
++++ b/src/lxc/confile.c
+@@ -112,6 +112,7 @@ static int config_init_cmd(const char *, const char *, struct lxc_conf *);
+ static int config_init_uid(const char *, const char *, struct lxc_conf *);
+ 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_limit(const char *, const char *, struct lxc_conf *);
+
+ static struct lxc_config_t config[] = {
+
+@@ -184,6 +185,7 @@ static struct lxc_config_t config[] = {
+ { "lxc.init_uid", config_init_uid },
+ { "lxc.init_gid", config_init_gid },
+ { "lxc.ephemeral", config_ephemeral },
++ { "lxc.limit", config_limit },
+ };
+
+ struct signame {
+@@ -1500,6 +1502,110 @@ out:
+ 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";
+@@ -2233,6 +2339,55 @@ static int lxc_get_cgroup_entry(struct lxc_conf *c, char *retv, int inlen,
+ 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)
+ {
+@@ -2594,6 +2749,10 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
+ return lxc_get_conf_int(c, retv, inlen, c->init_gid);
+ else if (strcmp(key, "lxc.ephemeral") == 0)
+ return lxc_get_conf_int(c, retv, inlen, c->ephemeral);
++ 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)
+@@ -2627,6 +2786,8 @@ int lxc_clear_config_item(struct lxc_conf *c, const char *key)
+ 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;
+ }
+
+diff --git a/src/lxc/start.c b/src/lxc/start.c
+index a909c631..29edb8f7 100644
+--- a/src/lxc/start.c
++++ b/src/lxc/start.c
+@@ -1244,6 +1244,11 @@ static int lxc_spawn(struct lxc_handler *handler)
+ if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))
+ goto out_delete_net;
+
++ if (!lxc_list_empty(&handler->conf->limits) && setup_resource_limits(&handler->conf->limits, handler->pid)) {
++ ERROR("failed to setup resource limits for '%s'", name);
++ return -1;
++ }
++
+ if (!cgroup_setup_limits(handler, true)) {
+ ERROR("Failed to setup the devices cgroup for container \"%s\".", name);
+ goto out_delete_net;
+--
+2.11.0
+
--- /dev/null
+From fd224d9bff0b269ea1524cf7546314488014c018 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 4 Nov 2016 11:45:47 +0100
+Subject: [PATCH 11/14] test: resource limit config entries
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ src/tests/get_item.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 64 insertions(+)
+
+diff --git a/src/tests/get_item.c b/src/tests/get_item.c
+index 9750f312..93f2d965 100644
+--- a/src/tests/get_item.c
++++ b/src/tests/get_item.c
+@@ -149,6 +149,70 @@ int main(int argc, char *argv[])
+ }
+ printf("lxc.mount.entry returned %d %s\n", ret, v2);
+
++ ret = c->get_config_item(c, "lxc.limit", v3, 2047);
++ if (ret != 0) {
++ fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret);
++ goto out;
++ }
++
++ if (!c->set_config_item(c, "lxc.limit.nofile", "1234:unlimited")) {
++ fprintf(stderr, "%d: failed to set limit.nofile\n", __LINE__);
++ goto out;
++ }
++ ret = c->get_config_item(c, "lxc.limit.nofile", v2, 255);
++ if (ret < 0) {
++ fprintf(stderr, "%d: get_config_item(lxc.limit.nofile) returned %d\n", __LINE__, ret);
++ goto out;
++ }
++ if (strcmp(v2, "1234:unlimited")) {
++ fprintf(stderr, "%d: lxc.limit.nofile returned wrong value: %d %s not 14 1234:unlimited\n", __LINE__, ret, v2);
++ goto out;
++ }
++ printf("lxc.limit.nofile returned %d %s\n", ret, v2);
++
++ if (!c->set_config_item(c, "lxc.limit.stack", "unlimited")) {
++ fprintf(stderr, "%d: failed to set limit.stack\n", __LINE__);
++ goto out;
++ }
++ ret = c->get_config_item(c, "lxc.limit.stack", v2, 255);
++ if (ret < 0) {
++ fprintf(stderr, "%d: get_config_item(lxc.limit.stack) returned %d\n", __LINE__, ret);
++ goto out;
++ }
++ if (strcmp(v2, "unlimited")) {
++ fprintf(stderr, "%d: lxc.limit.stack returned wrong value: %d %s not 9 unlimited\n", __LINE__, ret, v2);
++ goto out;
++ }
++ printf("lxc.limit.stack returned %d %s\n", ret, v2);
++
++#define LIMIT_STACK "lxc.limit.stack = unlimited\n"
++#define ALL_LIMITS "lxc.limit.nofile = 1234:unlimited\n" LIMIT_STACK
++ ret = c->get_config_item(c, "lxc.limit", v3, 2047);
++ if (ret != sizeof(ALL_LIMITS)-1) {
++ fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret);
++ goto out;
++ }
++ if (strcmp(v3, ALL_LIMITS)) {
++ fprintf(stderr, "%d: lxc.limit returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(ALL_LIMITS)-1, ALL_LIMITS);
++ goto out;
++ }
++ printf("lxc.limit returned %d %s\n", ret, v3);
++
++ if (!c->clear_config_item(c, "lxc.limit.nofile")) {
++ fprintf(stderr, "%d: failed clearing limit.nofile\n", __LINE__);
++ goto out;
++ }
++ ret = c->get_config_item(c, "lxc.limit", v3, 2047);
++ if (ret != sizeof(LIMIT_STACK)-1) {
++ fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret);
++ goto out;
++ }
++ if (strcmp(v3, LIMIT_STACK)) {
++ fprintf(stderr, "%d: lxc.limit returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(LIMIT_STACK)-1, LIMIT_STACK);
++ goto out;
++ }
++ printf("lxc.limit returned %d %s\n", ret, v3);
++
+ if (!c->set_config_item(c, "lxc.aa_profile", "unconfined")) {
+ fprintf(stderr, "%d: failed to set aa_profile\n", __LINE__);
+ goto out;
+--
+2.11.0
+