diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 7a6dd4841..ed8bef7e1 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -338,6 +338,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Proc + + Configure proc filesystem for the container. + + + + + + + + + Specify the proc file name to be set. The file name available + are those listed under /proc/PID/. + Example: + + + lxc.proc.oom_score_adj = 10 + + + + + + Ephemeral diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 6a452064b..64d02d845 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2398,6 +2398,38 @@ int setup_sysctl_parameters(struct lxc_list *sysctls) return 0; } +int setup_proc_filesystem(struct lxc_list *procs, pid_t pid) +{ + struct lxc_list *it; + struct lxc_proc *elem; + char *tmp = NULL; + char filename[MAXPATHLEN] = {0}; + int ret = 0; + + lxc_list_for_each(it, procs) { + elem = it->elem; + tmp = lxc_string_replace(".", "/", elem->filename); + if (!tmp) { + ERROR("Failed to replace key %s", elem->filename); + return -1; + } + + ret = snprintf(filename, sizeof(filename), "/proc/%d/%s", pid, tmp); + free(tmp); + if (ret < 0 || (size_t)ret >= sizeof(filename)) { + ERROR("Error setting up proc filesystem path"); + return -1; + } + + ret = lxc_write_to_file(filename, elem->value, strlen(elem->value), false); + if (ret < 0) { + ERROR("Failed to setup proc filesystem %s to %s", elem->filename, elem->value); + return -1; + } + } + return 0; +} + static char *default_rootfs_mount = LXCROOTFSMOUNT; struct lxc_conf *lxc_conf_init(void) @@ -2449,6 +2481,7 @@ struct lxc_conf *lxc_conf_init(void) lxc_list_init(&new->environment); lxc_list_init(&new->limits); lxc_list_init(&new->sysctls); + lxc_list_init(&new->procs); for (i = 0; i < NUM_LXC_HOOKS; i++) lxc_list_init(&new->hooks[i]); lxc_list_init(&new->groups); @@ -3384,6 +3417,33 @@ int lxc_clear_sysctls(struct lxc_conf *c, const char *key) return 0; } +int lxc_clear_procs(struct lxc_conf *c, const char *key) +{ + struct lxc_list *it,*next; + bool all = false; + const char *k = NULL; + + if (strcmp(key, "lxc.proc") == 0) + all = true; + else if (strncmp(key, "lxc.proc.", sizeof("lxc.proc.") - 1) == 0) + k = key + sizeof("lxc.proc.") - 1; + else + return -1; + + lxc_list_for_each_safe(it, &c->procs, next) { + struct lxc_proc *proc = it->elem; + if (!all && strcmp(proc->filename, k) != 0) + continue; + lxc_list_del(it); + free(proc->filename); + free(proc->value); + free(proc); + free(it); + } + + return 0; +} + int lxc_clear_groups(struct lxc_conf *c) { struct lxc_list *it,*next; @@ -3524,6 +3584,7 @@ void lxc_conf_free(struct lxc_conf *conf) lxc_clear_environment(conf); lxc_clear_limits(conf, "lxc.prlimit"); lxc_clear_sysctls(conf, "lxc.sysctl"); + lxc_clear_procs(conf, "lxc.proc"); free(conf->cgroup_meta.dir); free(conf->cgroup_meta.controllers); free(conf); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index e60a6151f..0cf04a53f 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -105,6 +105,16 @@ struct lxc_sysctl { char *value; }; +/* + * Defines a structure to configure proc filesystem at runtime. + * @filename : the proc filesystem will be configured without the "lxc.proc" prefix + * @value : the value to set + */ +struct lxc_proc { + char *filename; + char *value; +}; + /* * id_map is an id map entry. Form in confile is: * lxc.idmap = u 0 9800 100 @@ -383,6 +393,9 @@ struct lxc_conf { /* sysctls */ struct lxc_list sysctls; + + /* procs */ + struct lxc_list procs; }; int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, @@ -443,5 +456,7 @@ extern int run_script(const char *name, const char *section, const char *script, extern int in_caplist(int cap, struct lxc_list *caps); extern int setup_sysctl_parameters(struct lxc_list *sysctls); extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key); +extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid); +extern int lxc_clear_procs(struct lxc_conf *c, const char *key); #endif /* __LXC_CONF_H */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 584f93630..47424b4f2 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -142,6 +142,7 @@ lxc_config_define(tty_max); lxc_config_define(tty_dir); lxc_config_define(uts_name); lxc_config_define(sysctl); +lxc_config_define(proc); static struct lxc_config_t config[] = { /* REMOVE in LXC 3.0 */ @@ -243,6 +244,7 @@ static struct lxc_config_t config[] = { { "lxc.tty.max", false, set_config_tty_max, get_config_tty_max, clr_config_tty_max, }, { "lxc.uts.name", false, set_config_uts_name, get_config_uts_name, clr_config_uts_name, }, { "lxc.sysctl", false, set_config_sysctl, get_config_sysctl, clr_config_sysctl, }, + { "lxc.proc", false, set_config_proc, get_config_proc, clr_config_proc, }, /* [START]: REMOVE IN LXC 3.0 */ { "lxc.pts", true, set_config_pty_max, get_config_pty_max, clr_config_pty_max, }, @@ -1562,6 +1564,55 @@ on_error: return -1; } +static int set_config_proc(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + const char *subkey; + struct lxc_list *proclist = NULL; + struct lxc_proc *procelem = NULL; + + if (lxc_config_value_empty(value)) + return clr_config_proc(key, lxc_conf, NULL); + + if (strncmp(key, "lxc.proc.", sizeof("lxc.proc.") -1) != 0) + return -1; + + subkey = key + sizeof("lxc.proc.") - 1; + if (*subkey == '\0') + return -EINVAL; + + proclist = malloc(sizeof(*proclist)); + if (!proclist) + goto on_error; + + procelem = malloc(sizeof(*procelem)); + if (!procelem) + goto on_error; + memset(procelem, 0, sizeof(*procelem)); + + procelem->filename = strdup(subkey); + procelem->value = strdup(value); + + if (!procelem->filename || !procelem->value) + goto on_error; + + proclist->elem = procelem; + + lxc_list_add_tail(&lxc_conf->procs, proclist); + + return 0; + +on_error: + free(proclist); + if (procelem) { + free(procelem->filename); + free(procelem->value); + free(procelem); + } + + return -1; +} + static int set_config_idmaps(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { @@ -3414,6 +3465,40 @@ static int get_config_sysctl(const char *key, char *retv, int inlen, return fulllen; } +static int get_config_proc(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + struct lxc_list *it; + int len; + int fulllen = 0; + bool get_all = false; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (strcmp(key, "lxc.proc") == 0) + get_all = true; + else if (strncmp(key, "lxc.proc.", sizeof("lxc.proc.") - 1) == 0) + key += sizeof("lxc.proc.") - 1; + else + return -1; + + lxc_list_for_each(it, &c->procs) { + struct lxc_proc *proc = it->elem; + + if (get_all) { + strprint(retv, inlen, "lxc.proc.%s = %s\n", + proc->filename, proc->value); + } else if (strcmp(proc->filename, key) == 0) { + strprint(retv, inlen, "%s", proc->value); + } + } + + return fulllen; +} + static int get_config_noop(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { @@ -3797,6 +3882,12 @@ static inline int clr_config_sysctl(const char *key, struct lxc_conf *c, return lxc_clear_sysctls(c, key); } +static inline int clr_config_proc(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_procs(c, key); +} + static inline int clr_config_includefiles(const char *key, struct lxc_conf *c, void *data) { diff --git a/src/lxc/start.c b/src/lxc/start.c index a8acfea17..c301d7f0b 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1386,6 +1386,12 @@ static int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; } + if (!lxc_list_empty(&conf->procs)) { + ret = setup_proc_filesystem(&conf->procs, handler->pid); + if (ret < 0) + goto out_delete_net; + } + /* Tell the child to continue its initialization. We'll get * LXC_SYNC_CGROUP when it is ready for us to setup cgroups. */ diff --git a/src/tests/get_item.c b/src/tests/get_item.c index 7a15c1c89..c2c561235 100644 --- a/src/tests/get_item.c +++ b/src/tests/get_item.c @@ -309,6 +309,73 @@ int main(int argc, char *argv[]) } printf("lxc.sysctl returned %d %s\n", ret, v3); +#define PROC_OOM_SCORE_ADJ "lxc.proc.oom_score_adj = 10\n" +#define ALL_PROCS "lxc.proc.setgroups = allow\n" PROC_OOM_SCORE_ADJ + + ret = c->get_config_item(c, "lxc.proc", v3, 2047); + if (ret != 0) { + fprintf(stderr, "%d: get_config_item(proc) returned %d\n", __LINE__, ret); + goto out; + } + + if (!c->set_config_item(c, "lxc.proc.setgroups", "allow")) { + fprintf(stderr, "%d: failed to set lxc.proc.setgroups\n", __LINE__); + goto out; + } + + ret = c->get_config_item(c, "lxc.proc.setgroups", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.proc.setgroups) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "allow")) { + fprintf(stderr, "%d: lxc.proc.setgroups returned wrong value: %d %s not 10\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.proc.setgroups returned %d %s\n", ret, v2); + + if (!c->set_config_item(c, "lxc.proc.oom_score_adj", "10")) { + fprintf(stderr, "%d: failed to set lxc.proc.oom_score_adj\n", __LINE__); + goto out; + } + + ret = c->get_config_item(c, "lxc.proc.oom_score_adj", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.proc.oom_score_adj) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "10")) { + fprintf(stderr, "%d: lxc.proc.oom_score_adj returned wrong value: %d %s not 10\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.proc.oom_score_adj returned %d %s\n", ret, v2); + + ret = c->get_config_item(c, "lxc.proc", v3, 2047); + if (ret != sizeof(ALL_PROCS)-1) { + fprintf(stderr, "%d: get_config_item(proc) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, ALL_PROCS)) { + fprintf(stderr, "%d: lxc.proc returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(ALL_PROCS) - 1, ALL_PROCS); + goto out; + } + printf("lxc.proc returned %d %s\n", ret, v3); + + if (!c->clear_config_item(c, "lxc.proc.setgroups")) { + fprintf(stderr, "%d: failed clearing lxc.proc.setgroups\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.proc", v3, 2047); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(proc) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, PROC_OOM_SCORE_ADJ)) { + fprintf(stderr, "%d: lxc.proc returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(PROC_OOM_SCORE_ADJ) - 1, PROC_OOM_SCORE_ADJ); + goto out; + } + printf("lxc.proc 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; diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index 556d6d034..f6fda5a05 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -929,6 +929,13 @@ int main(int argc, char *argv[]) goto non_test_error; } + /* lxc.proc */ + if (set_get_compare_clear_save_load(c, "lxc.proc.oom_score_adj", "10", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.proc.oom_score_adj"); + goto non_test_error; + } + /* REMOVE IN LXC 3.0 legacy lxc.limit.* key */