X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2Flxc%2Fconfile.c;h=ac05b7562dbe9f7f15d481ee909222d4707b7b02;hb=0769b82a42ccdb8daa378b493be8ea092a283b24;hp=09902ba4b8e01ebd6ff5fc21f82eebc5b52f6d60;hpb=e075f5d9b64175dad6e591e3f1d05a8434c4699f;p=mirror_lxc.git diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 09902ba4b..ac05b7562 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -4,7 +4,7 @@ * (C) Copyright IBM Corp. 2007, 2008 * * Authors: - * Daniel Lezcano + * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,92 +18,119 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define _GNU_SOURCE #include #include #include #include #include #include -#include +#include +#include #include #include #include #include -#include #include #include #include #include "parse.h" +#include "config.h" #include "confile.h" #include "utils.h" +#include "log.h" +#include "conf.h" +#include "network.h" +#include "lxcseccomp.h" -#include -#include +#if HAVE_SYS_PERSONALITY_H +#include +#endif lxc_log_define(lxc_confile, lxc); -static int config_personality(const char *, char *, struct lxc_conf *); -static int config_pts(const char *, char *, struct lxc_conf *); -static int config_tty(const char *, char *, struct lxc_conf *); -static int config_ttydir(const char *, char *, struct lxc_conf *); -#if HAVE_APPARMOR -static int config_aa_profile(const char *, char *, struct lxc_conf *); -#endif -static int config_cgroup(const char *, char *, struct lxc_conf *); -static int config_mount(const char *, char *, struct lxc_conf *); -static int config_rootfs(const char *, char *, struct lxc_conf *); -static int config_rootfs_mount(const char *, char *, struct lxc_conf *); -static int config_pivotdir(const char *, char *, struct lxc_conf *); -static int config_utsname(const char *, char *, struct lxc_conf *); -static int config_network_type(const char *, char *, struct lxc_conf *); -static int config_network_flags(const char *, char *, struct lxc_conf *); -static int config_network_link(const char *, char *, struct lxc_conf *); -static int config_network_name(const char *, char *, struct lxc_conf *); -static int config_network_veth_pair(const char *, char *, struct lxc_conf *); -static int config_network_macvlan_mode(const char *, char *, struct lxc_conf *); -static int config_network_hwaddr(const char *, char *, struct lxc_conf *); -static int config_network_vlan_id(const char *, char *, struct lxc_conf *); -static int config_network_mtu(const char *, char *, struct lxc_conf *); -static int config_network_ipv4(const char *, char *, struct lxc_conf *); -static int config_network_ipv4_gateway(const char *, char *, struct lxc_conf *); -static int config_network_script(const char *, char *, struct lxc_conf *); -static int config_network_ipv6(const char *, char *, struct lxc_conf *); -static int config_network_ipv6_gateway(const char *, char *, struct lxc_conf *); -static int config_cap_drop(const char *, char *, struct lxc_conf *); -static int config_console(const char *, char *, struct lxc_conf *); - -typedef int (*config_cb)(const char *, char *, struct lxc_conf *); - -struct config { - char *name; - config_cb cb; -}; - -static struct config config[] = { +static int config_personality(const char *, const char *, struct lxc_conf *); +static int config_pts(const char *, const char *, struct lxc_conf *); +static int config_tty(const char *, const char *, struct lxc_conf *); +static int config_ttydir(const char *, const char *, struct lxc_conf *); +static int config_kmsg(const char *, const char *, struct lxc_conf *); +static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *); +static int config_lsm_se_context(const char *, const char *, struct lxc_conf *); +static int config_cgroup(const char *, const char *, struct lxc_conf *); +static int config_idmap(const char *, const char *, struct lxc_conf *); +static int config_loglevel(const char *, const char *, struct lxc_conf *); +static int config_logfile(const char *, const char *, struct lxc_conf *); +static int config_mount(const char *, const char *, struct lxc_conf *); +static int config_rootfs(const char *, const char *, struct lxc_conf *); +static int config_rootfs_mount(const char *, const char *, struct lxc_conf *); +static int config_rootfs_options(const char *, const char *, struct lxc_conf *); +static int config_pivotdir(const char *, const char *, struct lxc_conf *); +static int config_utsname(const char *, const char *, struct lxc_conf *); +static int config_hook(const char *, const char *, struct lxc_conf *lxc_conf); +static int config_network_type(const char *, const char *, struct lxc_conf *); +static int config_network_flags(const char *, const char *, struct lxc_conf *); +static int config_network_link(const char *, const char *, struct lxc_conf *); +static int config_network_name(const char *, const char *, struct lxc_conf *); +static int config_network_veth_pair(const char *, const char *, struct lxc_conf *); +static int config_network_macvlan_mode(const char *, const char *, struct lxc_conf *); +static int config_network_hwaddr(const char *, const char *, struct lxc_conf *); +static int config_network_vlan_id(const char *, const char *, struct lxc_conf *); +static int config_network_mtu(const char *, const char *, struct lxc_conf *); +static int config_network_ipv4(const char *, const char *, struct lxc_conf *); +static int config_network_ipv4_gateway(const char *, const char *, struct lxc_conf *); +static int config_network_script_up(const char *, const char *, struct lxc_conf *); +static int config_network_script_down(const char *, const char *, struct lxc_conf *); +static int config_network_ipv6(const char *, const char *, struct lxc_conf *); +static int config_network_ipv6_gateway(const char *, const char *, struct lxc_conf *); +static int config_cap_drop(const char *, const char *, struct lxc_conf *); +static int config_cap_keep(const char *, const char *, struct lxc_conf *); +static int config_console(const char *, const char *, struct lxc_conf *); +static int config_seccomp(const char *, const char *, struct lxc_conf *); +static int config_includefile(const char *, const char *, struct lxc_conf *); +static int config_network_nic(const char *, const char *, struct lxc_conf *); +static int config_autodev(const char *, const char *, struct lxc_conf *); +static int config_haltsignal(const char *, const char *, struct lxc_conf *); +static int config_stopsignal(const char *, const char *, struct lxc_conf *); +static int config_start(const char *, const char *, struct lxc_conf *); +static int config_group(const char *, const char *, struct lxc_conf *); + +static struct lxc_config_t config[] = { { "lxc.arch", config_personality }, { "lxc.pts", config_pts }, { "lxc.tty", config_tty }, { "lxc.devttydir", config_ttydir }, -#if HAVE_APPARMOR - { "lxc.aa_profile", config_aa_profile }, -#endif + { "lxc.kmsg", config_kmsg }, + { "lxc.aa_profile", config_lsm_aa_profile }, + { "lxc.se_context", config_lsm_se_context }, { "lxc.cgroup", config_cgroup }, + { "lxc.id_map", config_idmap }, + { "lxc.loglevel", config_loglevel }, + { "lxc.logfile", config_logfile }, { "lxc.mount", config_mount }, { "lxc.rootfs.mount", config_rootfs_mount }, + { "lxc.rootfs.options", config_rootfs_options }, { "lxc.rootfs", config_rootfs }, { "lxc.pivotdir", config_pivotdir }, { "lxc.utsname", config_utsname }, + { "lxc.hook.pre-start", config_hook }, + { "lxc.hook.pre-mount", config_hook }, + { "lxc.hook.mount", config_hook }, + { "lxc.hook.autodev", config_hook }, + { "lxc.hook.start", config_hook }, + { "lxc.hook.post-stop", config_hook }, + { "lxc.hook.clone", config_hook }, { "lxc.network.type", config_network_type }, { "lxc.network.flags", config_network_flags }, { "lxc.network.link", config_network_link }, { "lxc.network.name", config_network_name }, { "lxc.network.macvlan.mode", config_network_macvlan_mode }, { "lxc.network.veth.pair", config_network_veth_pair }, - { "lxc.network.script.up", config_network_script }, + { "lxc.network.script.up", config_network_script_up }, + { "lxc.network.script.down", config_network_script_down }, { "lxc.network.hwaddr", config_network_hwaddr }, { "lxc.network.mtu", config_network_mtu }, { "lxc.network.vlan.id", config_network_vlan_id }, @@ -111,13 +138,52 @@ static struct config config[] = { { "lxc.network.ipv4", config_network_ipv4 }, { "lxc.network.ipv6.gateway", config_network_ipv6_gateway }, { "lxc.network.ipv6", config_network_ipv6 }, + /* config_network_nic must come after all other 'lxc.network.*' entries */ + { "lxc.network.", config_network_nic }, { "lxc.cap.drop", config_cap_drop }, + { "lxc.cap.keep", config_cap_keep }, { "lxc.console", config_console }, + { "lxc.seccomp", config_seccomp }, + { "lxc.include", config_includefile }, + { "lxc.autodev", config_autodev }, + { "lxc.haltsignal", config_haltsignal }, + { "lxc.stopsignal", config_stopsignal }, + { "lxc.start.auto", config_start }, + { "lxc.start.delay", config_start }, + { "lxc.start.order", config_start }, + { "lxc.group", config_group }, +}; + +struct signame { + int num; + const char *name; +}; + +static const struct signame signames[] = { + { SIGHUP, "HUP" }, + { SIGINT, "INT" }, + { SIGQUIT, "QUIT" }, + { SIGILL, "ILL" }, + { SIGABRT, "ABRT" }, + { SIGFPE, "FPE" }, + { SIGKILL, "KILL" }, + { SIGSEGV, "SEGV" }, + { SIGPIPE, "PIPE" }, + { SIGALRM, "ALRM" }, + { SIGTERM, "TERM" }, + { SIGUSR1, "USR1" }, + { SIGUSR2, "USR2" }, + { SIGCHLD, "CHLD" }, + { SIGCONT, "CONT" }, + { SIGSTOP, "STOP" }, + { SIGTSTP, "TSTP" }, + { SIGTTIN, "TTIN" }, + { SIGTTOU, "TTOU" }, }; -static const size_t config_size = sizeof(config)/sizeof(struct config); +static const size_t config_size = sizeof(config)/sizeof(struct lxc_config_t); -static struct config *getconfig(const char *key) +extern struct lxc_config_t *lxc_getconfig(const char *key) { int i; @@ -128,13 +194,127 @@ static struct config *getconfig(const char *key) return NULL; } -static int config_network_type(const char *key, char *value, +#define strprint(str, inlen, ...) \ + do { \ + len = snprintf(str, inlen, ##__VA_ARGS__); \ + if (len < 0) { SYSERROR("snprintf"); return -1; }; \ + fulllen += len; \ + if (inlen > 0) { \ + if (str) str += len; \ + inlen -= len; \ + if (inlen < 0) inlen = 0; \ + } \ + } while (0); + +int lxc_listconfigs(char *retv, int inlen) +{ + int i, fulllen = 0, len; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + for (i = 0; i < config_size; i++) { + char *s = config[i].name; + if (s[strlen(s)-1] == '.') + continue; + strprint(retv, inlen, "%s\n", s); + } + return fulllen; +} + +static int config_string_item(char **conf_item, const char *value) +{ + char *new_value; + + if (!value || strlen(value) == 0) { + if (*conf_item) + free(*conf_item); + *conf_item = NULL; + return 0; + } + + new_value = strdup(value); + if (!new_value) { + SYSERROR("failed to strdup '%s': %m", value); + return -1; + } + + if (*conf_item) + free(*conf_item); + *conf_item = new_value; + return 0; +} + +static int config_string_item_max(char **conf_item, const char *value, + size_t max) +{ + if (strlen(value) >= max) { + ERROR("%s is too long (>= %lu)", value, (unsigned long)max); + return -1; + } + + return config_string_item(conf_item, value); +} + +static int config_path_item(char **conf_item, const char *value) +{ + return config_string_item_max(conf_item, value, PATH_MAX); +} + +/* + * config entry is something like "lxc.network.0.ipv4" + * the key 'lxc.network.' was found. So we make sure next + * comes an integer, find the right callback (by rewriting + * the key), and call it. + */ +static int config_network_nic(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *copy = strdup(key), *p; + int ret = -1; + struct lxc_config_t *config; + + if (!copy) { + SYSERROR("failed to allocate memory"); + return -1; + } + /* + * ok we know that to get here we've got "lxc.network." + * and it isn't any of the other network entries. So + * after the second . should come an integer (# of defined + * nic) followed by a valid entry. + */ + if (*(key+12) < '0' || *(key+12) > '9') + goto out; + p = index(key+12, '.'); + if (!p) + goto out; + strcpy(copy+12, p+1); + config = lxc_getconfig(copy); + if (!config) { + ERROR("unknown key %s", key); + goto out; + } + ret = config->cb(key, value, lxc_conf); + +out: + free(copy); + return ret; +} + +static int macvlan_mode(int *valuep, const char *value); + +static int config_network_type(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_list *network = &lxc_conf->network; struct lxc_netdev *netdev; struct lxc_list *list; + if (!value || strlen(value) == 0) + return lxc_clear_config_network(lxc_conf); + netdev = malloc(sizeof(*netdev)); if (!netdev) { SYSERROR("failed to allocate memory"); @@ -148,6 +328,7 @@ static int config_network_type(const char *key, char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(netdev); return -1; } @@ -158,14 +339,18 @@ static int config_network_type(const char *key, char *value, if (!strcmp(value, "veth")) netdev->type = LXC_NET_VETH; - else if (!strcmp(value, "macvlan")) + else if (!strcmp(value, "macvlan")) { netdev->type = LXC_NET_MACVLAN; + macvlan_mode(&netdev->priv.macvlan_attr.mode, "private"); + } else if (!strcmp(value, "vlan")) netdev->type = LXC_NET_VLAN; else if (!strcmp(value, "phys")) netdev->type = LXC_NET_PHYS; else if (!strcmp(value, "empty")) netdev->type = LXC_NET_EMPTY; + else if (!strcmp(value, "none")) + netdev->type = LXC_NET_NONE; else { ERROR("invalid network type %s", value); return -1; @@ -185,10 +370,91 @@ static int config_ip_prefix(struct in_addr *addr) return 0; } +/* + * if you have p="lxc.network.0.link", pass it p+12 + * to get back '0' (the index of the nic) + */ +static int get_network_netdev_idx(const char *key) +{ + int ret, idx; + + if (*key < '0' || *key > '9') + return -1; + ret = sscanf(key, "%d", &idx); + if (ret != 1) + return -1; + return idx; +} + +/* + * if you have p="lxc.network.0", pass this p+12 and it will return + * the netdev of the first configured nic + */ +static struct lxc_netdev *get_netdev_from_key(const char *key, + struct lxc_list *network) +{ + int i = 0, idx = get_network_netdev_idx(key); + struct lxc_netdev *netdev = NULL; + struct lxc_list *it; + if (idx == -1) + return NULL; + lxc_list_for_each(it, network) { + if (idx == i++) { + netdev = it->elem; + break; + } + } + return netdev; +} + +extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, + char *retv, int inlen) +{ + struct lxc_netdev *netdev; + int fulllen = 0, len; + + netdev = get_netdev_from_key(key+12, &c->network); + if (!netdev) + return -1; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "script.up\n"); + strprint(retv, inlen, "script.down\n"); + if (netdev->type != LXC_NET_EMPTY) { + strprint(retv, inlen, "flags\n"); + strprint(retv, inlen, "link\n"); + strprint(retv, inlen, "name\n"); + strprint(retv, inlen, "hwaddr\n"); + strprint(retv, inlen, "mtu\n"); + strprint(retv, inlen, "ipv6\n"); + strprint(retv, inlen, "ipv6_gateway\n"); + strprint(retv, inlen, "ipv4\n"); + strprint(retv, inlen, "ipv4_gateway\n"); + } + switch(netdev->type) { + case LXC_NET_VETH: + strprint(retv, inlen, "veth.pair\n"); + break; + case LXC_NET_MACVLAN: + strprint(retv, inlen, "macvlan.mode\n"); + break; + case LXC_NET_VLAN: + strprint(retv, inlen, "vlan.id\n"); + break; + case LXC_NET_PHYS: + break; + } + return fulllen; +} + static struct lxc_netdev *network_netdev(const char *key, const char *value, struct lxc_list *network) { - struct lxc_netdev *netdev; + struct lxc_netdev *netdev = NULL; if (lxc_list_empty(network)) { ERROR("network is not created for '%s' = '%s' option", @@ -196,7 +462,11 @@ static struct lxc_netdev *network_netdev(const char *key, const char *value, return NULL; } - netdev = lxc_list_last_elem(network); + if (get_network_netdev_idx(key+12) == -1) + netdev = lxc_list_last_elem(network); + else + netdev = get_netdev_from_key(key+12, network); + if (!netdev) { ERROR("no network device defined for '%s' = '%s' option", key, value); @@ -206,20 +476,9 @@ static struct lxc_netdev *network_netdev(const char *key, const char *value, return netdev; } -static int network_ifname(char **valuep, char *value) +static int network_ifname(char **valuep, const char *value) { - if (strlen(value) >= IFNAMSIZ) { - ERROR("invalid interface name: %s", value); - return -1; - } - - *valuep = strdup(value); - if (!*valuep) { - ERROR("failed to dup string '%s'", value); - return -1; - } - - return 0; + return config_string_item_max(valuep, value, IFNAMSIZ); } #ifndef MACVLAN_MODE_PRIVATE @@ -234,7 +493,7 @@ static int network_ifname(char **valuep, char *value) # define MACVLAN_MODE_BRIDGE 4 #endif -static int macvlan_mode(int *valuep, char *value) +static int macvlan_mode(int *valuep, const char *value) { struct mc_mode { char *name; @@ -258,7 +517,38 @@ static int macvlan_mode(int *valuep, char *value) return -1; } -static int config_network_flags(const char *key, char *value, +static int rand_complete_hwaddr(char *hwaddr) +{ + const char hex[] = "0123456789abcdef"; + char *curs = hwaddr; + +#ifndef HAVE_RAND_R + randseed(true); +#else + unsigned int seed=randseed(false); +#endif + while (*curs != '\0') + { + if ( *curs == 'x' || *curs == 'X' ) { + if (curs - hwaddr == 1) { + //ensure address is unicast +#ifdef HAVE_RAND_R + *curs = hex[rand_r(&seed) & 0x0E]; + } else { + *curs = hex[rand_r(&seed) & 0x0F]; +#else + *curs = hex[rand() & 0x0E]; + } else { + *curs = hex[rand() & 0x0F]; +#endif + } + } + curs++; + } + return 0; +} + +static int config_network_flags(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -272,7 +562,7 @@ static int config_network_flags(const char *key, char *value, return 0; } -static int config_network_link(const char *key, char *value, +static int config_network_link(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -284,7 +574,7 @@ static int config_network_link(const char *key, char *value, return network_ifname(&netdev->link, value); } -static int config_network_name(const char *key, char *value, +static int config_network_name(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -296,7 +586,7 @@ static int config_network_name(const char *key, char *value, return network_ifname(&netdev->name, value); } -static int config_network_veth_pair(const char *key, char *value, +static int config_network_veth_pair(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -308,7 +598,7 @@ static int config_network_veth_pair(const char *key, char *value, return network_ifname(&netdev->priv.veth_attr.pair, value); } -static int config_network_macvlan_mode(const char *key, char *value, +static int config_network_macvlan_mode(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -320,25 +610,35 @@ static int config_network_macvlan_mode(const char *key, char *value, return macvlan_mode(&netdev->priv.macvlan_attr.mode, value); } -static int config_network_hwaddr(const char *key, char *value, +static int config_network_hwaddr(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); - if (!netdev) + char *new_value = strdup(value); + if (!new_value) { + SYSERROR("failed to strdup '%s': %m", value); return -1; + } + rand_complete_hwaddr(new_value); - netdev->hwaddr = strdup(value); - if (!netdev->hwaddr) { - SYSERROR("failed to dup string '%s'", value); + netdev = network_netdev(key, new_value, &lxc_conf->network); + if (!netdev) { + free(new_value); return -1; + }; + + if (!new_value || strlen(new_value) == 0) { + free(new_value); + netdev->hwaddr = NULL; + return 0; } + netdev->hwaddr = new_value; return 0; } -static int config_network_vlan_id(const char *key, char *value, +static int config_network_vlan_id(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -353,7 +653,7 @@ static int config_network_vlan_id(const char *key, char *value, return 0; } -static int config_network_mtu(const char *key, char *value, +static int config_network_mtu(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -362,16 +662,10 @@ static int config_network_mtu(const char *key, char *value, if (!netdev) return -1; - netdev->mtu = strdup(value); - if (!netdev->mtu) { - SYSERROR("failed to dup string '%s'", value); - return -1; - } - - return 0; + return config_string_item(&netdev->mtu, value); } -static int config_network_ipv4(const char *key, char *value, +static int config_network_ipv4(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -393,13 +687,20 @@ static int config_network_ipv4(const char *key, char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(inetdev); return -1; } lxc_list_init(list); list->elem = inetdev; - addr = value; + addr = strdup(value); + if (!addr) { + ERROR("no address specified"); + free(inetdev); + free(list); + return -1; + } cursor = strstr(addr, " "); if (cursor) { @@ -413,18 +714,19 @@ static int config_network_ipv4(const char *key, char *value, prefix = slash + 1; } - if (!addr) { - ERROR("no address specified"); - return -1; - } - if (!inet_pton(AF_INET, addr, &inetdev->addr)) { SYSERROR("invalid ipv4 address: %s", value); + free(inetdev); + free(addr); + free(list); return -1; } if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { SYSERROR("invalid ipv4 broadcast address: %s", value); + free(inetdev); + free(list); + free(addr); return -1; } @@ -441,12 +743,13 @@ static int config_network_ipv4(const char *key, char *value, htonl(INADDR_BROADCAST >> inetdev->prefix); } - lxc_list_add(&netdev->ipv4, list); + lxc_list_add_tail(&netdev->ipv4, list); + free(addr); return 0; } -static int config_network_ipv4_gateway(const char *key, char *value, +static int config_network_ipv4_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; @@ -464,15 +767,18 @@ static int config_network_ipv4_gateway(const char *key, char *value, if (!value) { ERROR("no ipv4 gateway address specified"); + free(gw); return -1; } if (!strcmp(value, "auto")) { + free(gw); netdev->ipv4_gateway = NULL; netdev->ipv4_gateway_auto = true; } else { if (!inet_pton(AF_INET, value, gw)) { SYSERROR("invalid ipv4 gateway address: %s", value); + free(gw); return -1; } @@ -483,13 +789,13 @@ static int config_network_ipv4_gateway(const char *key, char *value, return 0; } -static int config_network_ipv6(const char *key, char *value, +static int config_network_ipv6(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; struct lxc_inet6dev *inet6dev; struct lxc_list *list; - char *slash; + char *slash,*valdup; char *netmask; netdev = network_netdev(key, value, &lxc_conf->network); @@ -506,46 +812,52 @@ static int config_network_ipv6(const char *key, char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(inet6dev); return -1; } lxc_list_init(list); list->elem = inet6dev; + valdup = strdup(value); + if (!valdup) { + ERROR("no address specified"); + free(list); + free(inet6dev); + return -1; + } + inet6dev->prefix = 64; - slash = strstr(value, "/"); + slash = strstr(valdup, "/"); if (slash) { *slash = '\0'; netmask = slash + 1; inet6dev->prefix = atoi(netmask); } - if (!inet_pton(AF_INET6, value, &inet6dev->addr)) { - SYSERROR("invalid ipv6 address: %s", value); + if (!inet_pton(AF_INET6, valdup, &inet6dev->addr)) { + SYSERROR("invalid ipv6 address: %s", valdup); + free(list); + free(inet6dev); + free(valdup); return -1; } - lxc_list_add(&netdev->ipv6, list); + lxc_list_add_tail(&netdev->ipv6, list); + free(valdup); return 0; } -static int config_network_ipv6_gateway(const char *key, char *value, +static int config_network_ipv6_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; - struct in6_addr *gw; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; - gw = malloc(sizeof(*gw)); - if (!gw) { - SYSERROR("failed to allocate ipv6 gateway address"); - return -1; - } - if (!value) { ERROR("no ipv6 gateway address specified"); return -1; @@ -555,8 +867,17 @@ static int config_network_ipv6_gateway(const char *key, char *value, netdev->ipv6_gateway = NULL; netdev->ipv6_gateway_auto = true; } else { + struct in6_addr *gw; + + gw = malloc(sizeof(*gw)); + if (!gw) { + SYSERROR("failed to allocate ipv6 gateway address"); + return -1; + } + if (!inet_pton(AF_INET6, value, gw)) { SYSERROR("invalid ipv6 gateway address: %s", value); + free(gw); return -1; } @@ -567,30 +888,83 @@ static int config_network_ipv6_gateway(const char *key, char *value, return 0; } -static int config_network_script(const char *key, char *value, - struct lxc_conf *lxc_conf) +static int config_network_script_up(const char *key, const char *value, + struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) - return -1; + return -1; + + return config_string_item(&netdev->upscript, value); +} + +static int config_network_script_down(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + return config_string_item(&netdev->downscript, value); +} + +static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) +{ + struct lxc_list *hooklist; + + hooklist = malloc(sizeof(*hooklist)); + if (!hooklist) { + free(hook); + return -1; + } + hooklist->elem = hook; + lxc_list_add_tail(&lxc_conf->hooks[which], hooklist); + return 0; +} + +static int config_seccomp(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_path_item(&lxc_conf->seccomp, value); +} + +static int config_hook(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *copy; + + if (!value || strlen(value) == 0) + return lxc_clear_hooks(lxc_conf, key); - char *copy = strdup(value); + copy = strdup(value); if (!copy) { SYSERROR("failed to dup string '%s'", value); return -1; } - if (strcmp(key, "lxc.network.script.up") == 0) { - netdev->upscript = copy; - return 0; - } + if (strcmp(key, "lxc.hook.pre-start") == 0) + return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); + else if (strcmp(key, "lxc.hook.pre-mount") == 0) + return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy); + else if (strcmp(key, "lxc.hook.autodev") == 0) + return add_hook(lxc_conf, LXCHOOK_AUTODEV, copy); + else if (strcmp(key, "lxc.hook.mount") == 0) + return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); + else if (strcmp(key, "lxc.hook.start") == 0) + return add_hook(lxc_conf, LXCHOOK_START, copy); + else if (strcmp(key, "lxc.hook.post-stop") == 0) + return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); + else if (strcmp(key, "lxc.hook.clone") == 0) + return add_hook(lxc_conf, LXCHOOK_CLONE, copy); SYSERROR("Unknown key: %s", key); free(copy); return -1; } -static int config_personality(const char *key, char *value, +static int config_personality(const char *key, const char *value, struct lxc_conf *lxc_conf) { signed long personality = lxc_config_parse_arch(value); @@ -603,7 +977,8 @@ static int config_personality(const char *key, char *value, return 0; } -static int config_pts(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_pts(const char *key, const char *value, + struct lxc_conf *lxc_conf) { int maxpts = atoi(value); @@ -612,7 +987,73 @@ static int config_pts(const char *key, char *value, struct lxc_conf *lxc_conf) return 0; } -static int config_tty(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_start(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + if(strcmp(key, "lxc.start.auto") == 0) { + lxc_conf->start_auto = atoi(value); + return 0; + } + else if (strcmp(key, "lxc.start.delay") == 0) { + lxc_conf->start_delay = atoi(value); + return 0; + } + else if (strcmp(key, "lxc.start.order") == 0) { + lxc_conf->start_order = atoi(value); + return 0; + } + SYSERROR("Unknown key: %s", key); + return -1; +} + +static int config_group(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *groups, *groupptr, *sptr, *token; + struct lxc_list *grouplist; + int ret = -1; + + if (!strlen(value)) + return lxc_clear_groups(lxc_conf); + + groups = strdup(value); + if (!groups) { + SYSERROR("failed to dup '%s'", value); + return -1; + } + + /* in case several groups are specified in a single line + * split these groups in a single element for the list */ + for (groupptr = groups;;groupptr = NULL) { + token = strtok_r(groupptr, " \t", &sptr); + if (!token) { + ret = 0; + break; + } + + grouplist = malloc(sizeof(*grouplist)); + if (!grouplist) { + SYSERROR("failed to allocate groups list"); + break; + } + + grouplist->elem = strdup(token); + if (!grouplist->elem) { + SYSERROR("failed to dup '%s'", token); + free(grouplist); + break; + } + + lxc_list_add_tail(&lxc_conf->groups, grouplist); + } + + free(groups); + + return ret; +} + +static int config_tty(const char *key, const char *value, + struct lxc_conf *lxc_conf) { int nbtty = atoi(value); @@ -621,53 +1062,170 @@ static int config_tty(const char *key, char *value, struct lxc_conf *lxc_conf) return 0; } -static int config_ttydir(const char *key, char *value, +static int config_ttydir(const char *key, const char *value, struct lxc_conf *lxc_conf) { - char *path; + return config_string_item_max(&lxc_conf->ttydir, value, NAME_MAX+1); +} - if (!value || strlen(value) == 0) - return 0; - path = strdup(value); - if (!path) { - SYSERROR("failed to strdup '%s': %m", value); - return -1; - } +static int config_kmsg(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + int v = atoi(value); - lxc_conf->ttydir = path; + lxc_conf->kmsg = v; return 0; } -#if HAVE_APPARMOR -static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_lsm_aa_profile(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_string_item(&lxc_conf->lsm_aa_profile, value); +} + +static int config_lsm_se_context(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_string_item(&lxc_conf->lsm_se_context, value); +} + +static int config_logfile(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + int ret; + + // store these values in the lxc_conf, and then try to set for + // actual current logging. + ret = config_path_item(&lxc_conf->logfile, value); + if (ret == 0) + ret = lxc_log_set_file(lxc_conf->logfile); + return ret; +} + +static int config_loglevel(const char *key, const char *value, + struct lxc_conf *lxc_conf) { - char *path; + int newlevel; if (!value || strlen(value) == 0) return 0; - path = strdup(value); - if (!path) { - SYSERROR("failed to strdup '%s': %m", value); - return -1; - } - lxc_conf->aa_profile = path; + if (value[0] >= '0' && value[0] <= '9') + newlevel = atoi(value); + else + newlevel = lxc_log_priority_to_int(value); + // store these values in the lxc_conf, and then try to set for + // actual current logging. + lxc_conf->loglevel = newlevel; + return lxc_log_set_level(newlevel); +} + +static int config_autodev(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + int v = atoi(value); + + lxc_conf->autodev = v; return 0; } -#endif -static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf) +static int sig_num(const char *sig) { - char *token = "lxc.cgroup."; - char *subkey; - struct lxc_list *cglist = NULL; - struct lxc_cgroup *cgelem = NULL; - - subkey = strstr(key, token); + int n; + char *endp = NULL; - if (!subkey) + errno = 0; + n = strtol(sig, &endp, 10); + if (sig == endp || n < 0 || errno != 0) + return -1; + return n; +} + +static int rt_sig_num(const char *signame) +{ + int sig_n = 0; + int rtmax = 0; + + if (strncasecmp(signame, "max-", 4) == 0) { + rtmax = 1; + } + signame += 4; + if (!isdigit(*signame)) + return -1; + sig_n = sig_num(signame); + sig_n = rtmax ? SIGRTMAX - sig_n : SIGRTMIN + sig_n; + if (sig_n > SIGRTMAX || sig_n < SIGRTMIN) + return -1; + return sig_n; +} + +static const char *sig_name(int signum) { + int n; + + for (n = 0; n < sizeof(signames) / sizeof((signames)[0]); n++) { + if (n == signames[n].num) + return signames[n].name; + } + return ""; +} + +static int sig_parse(const char *signame) { + int n; + + if (isdigit(*signame)) { + return sig_num(signame); + } else if (strncasecmp(signame, "sig", 3) == 0) { + signame += 3; + if (strncasecmp(signame, "rt", 2) == 0) + return rt_sig_num(signame + 2); + for (n = 0; n < sizeof(signames) / sizeof((signames)[0]); n++) { + if (strcasecmp (signames[n].name, signame) == 0) + return signames[n].num; + } + } + return -1; +} + +static int config_haltsignal(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + int sig_n = sig_parse(value); + + if (sig_n < 0) + return -1; + lxc_conf->haltsignal = sig_n; + + return 0; +} + +static int config_stopsignal(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + int sig_n = sig_parse(value); + + if (sig_n < 0) + return -1; + lxc_conf->stopsignal = sig_n; + + return 0; +} + +static int config_cgroup(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *token = "lxc.cgroup."; + char *subkey; + struct lxc_list *cglist = NULL; + struct lxc_cgroup *cgelem = NULL; + + if (!value || strlen(value) == 0) + return lxc_clear_cgroups(lxc_conf, key); + + subkey = strstr(key, token); + + if (!subkey) return -1; if (!strlen(subkey)) @@ -716,39 +1274,165 @@ out: return -1; } -static int config_fstab(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_idmap(const char *key, const char *value, struct lxc_conf *lxc_conf) { - if (strlen(value) >= MAXPATHLEN) { - ERROR("%s path is too long", value); + char *token = "lxc.id_map"; + char *subkey; + struct lxc_list *idmaplist = NULL; + struct id_map *idmap = NULL; + unsigned long hostid, nsid, range; + char type; + int ret; + + if (!value || strlen(value) == 0) + return lxc_clear_idmaps(lxc_conf); + + subkey = strstr(key, token); + + if (!subkey) + return -1; + + if (!strlen(subkey)) return -1; + + idmaplist = malloc(sizeof(*idmaplist)); + if (!idmaplist) + goto out; + + idmap = malloc(sizeof(*idmap)); + if (!idmap) + goto out; + memset(idmap, 0, sizeof(*idmap)); + + ret = sscanf(value, "%c %lu %lu %lu", &type, &nsid, &hostid, &range); + if (ret != 4) + goto out; + + INFO("read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range); + if (type == 'u') + idmap->idtype = ID_TYPE_UID; + else if (type == 'g') + idmap->idtype = ID_TYPE_GID; + else + goto out; + + idmap->hostid = hostid; + idmap->nsid = nsid; + idmap->range = range; + + idmaplist->elem = idmap; + lxc_list_add_tail(&lxc_conf->id_map, idmaplist); + + return 0; + +out: + if (idmaplist) + free(idmaplist); + + if (idmap) { + free(idmap); } - lxc_conf->fstab = strdup(value); - if (!lxc_conf->fstab) { - SYSERROR("failed to duplicate string %s", value); + return -1; +} + +static int config_fstab(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_path_item(&lxc_conf->fstab, value); +} + +static int config_mount_auto(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *autos, *autoptr, *sptr, *token; + static struct { const char *token; int mask; int flag; } allowed_auto_mounts[] = { + { "proc", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, + { "proc:mixed", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, + { "proc:rw", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW }, + { "sys", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, + { "sys:ro", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, + { "sys:rw", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW }, + { "cgroup", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC }, + { "cgroup:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED }, + { "cgroup:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO }, + { "cgroup:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW }, + { "cgroup-full", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC }, + { "cgroup-full:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED }, + { "cgroup-full:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO }, + { "cgroup-full:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW }, + /* NB: For adding anything that ist just a single on/off, but has + * no options: keep mask and flag identical and just define the + * enum value as an unused bit so far + */ + { NULL, 0 } + }; + int i; + int ret = -1; + + if (!strlen(value)) + return -1; + + autos = strdup(value); + if (!autos) { + SYSERROR("failed to dup '%s'", value); return -1; } - return 0; + for (autoptr = autos; ; autoptr = NULL) { + token = strtok_r(autoptr, " \t", &sptr); + if (!token) { + ret = 0; + break; + } + + for (i = 0; allowed_auto_mounts[i].token; i++) { + if (!strcmp(allowed_auto_mounts[i].token, token)) + break; + } + + if (!allowed_auto_mounts[i].token) { + ERROR("Invalid filesystem to automount: %s", token); + break; + } + + lxc_conf->auto_mounts &= ~allowed_auto_mounts[i].mask; + lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag; + } + + free(autos); + + return ret; } -static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_mount(const char *key, const char *value, + struct lxc_conf *lxc_conf) { char *fstab_token = "lxc.mount"; char *token = "lxc.mount.entry"; + char *auto_token = "lxc.mount.auto"; char *subkey; char *mntelem; struct lxc_list *mntlist; + if (!value || strlen(value) == 0) + return lxc_clear_mount_entries(lxc_conf); + subkey = strstr(key, token); if (!subkey) { - subkey = strstr(key, fstab_token); + subkey = strstr(key, auto_token); - if (!subkey) - return -1; + if (!subkey) { + subkey = strstr(key, fstab_token); + + if (!subkey) + return -1; - return config_fstab(key, value, lxc_conf); + return config_fstab(key, value, lxc_conf); + } + + return config_mount_auto(key, value, lxc_conf); } if (!strlen(subkey)) @@ -759,8 +1443,10 @@ static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf) return -1; mntelem = strdup(value); - if (!mntelem) + if (!mntelem) { + free(mntlist); return -1; + } mntlist->elem = mntelem; lxc_list_add_tail(&lxc_conf->mount_list, mntlist); @@ -768,15 +1454,61 @@ static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf) return 0; } -static int config_cap_drop(const char *key, char *value, +static int config_cap_keep(const char *key, const char *value, struct lxc_conf *lxc_conf) { - char *dropcaps, *sptr, *token; - struct lxc_list *droplist; + char *keepcaps, *keepptr, *sptr, *token; + struct lxc_list *keeplist; int ret = -1; if (!strlen(value)) + return lxc_clear_config_keepcaps(lxc_conf); + + keepcaps = strdup(value); + if (!keepcaps) { + SYSERROR("failed to dup '%s'", value); return -1; + } + + /* in case several capability keep is specified in a single line + * split these caps in a single element for the list */ + for (keepptr = keepcaps;;keepptr = NULL) { + token = strtok_r(keepptr, " \t", &sptr); + if (!token) { + ret = 0; + break; + } + + keeplist = malloc(sizeof(*keeplist)); + if (!keeplist) { + SYSERROR("failed to allocate keepcap list"); + break; + } + + keeplist->elem = strdup(token); + if (!keeplist->elem) { + SYSERROR("failed to dup '%s'", token); + free(keeplist); + break; + } + + lxc_list_add_tail(&lxc_conf->keepcaps, keeplist); + } + + free(keepcaps); + + return ret; +} + +static int config_cap_drop(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *dropcaps, *dropptr, *sptr, *token; + struct lxc_list *droplist; + int ret = -1; + + if (!strlen(value)) + return lxc_clear_config_caps(lxc_conf); dropcaps = strdup(value); if (!dropcaps) { @@ -786,13 +1518,12 @@ static int config_cap_drop(const char *key, char *value, /* in case several capability drop is specified in a single line * split these caps in a single element for the list */ - for (;;) { - token = strtok_r(dropcaps, " \t", &sptr); + for (dropptr = dropcaps;;dropptr = NULL) { + token = strtok_r(dropptr, " \t", &sptr); if (!token) { ret = 0; break; } - dropcaps = NULL; droplist = malloc(sizeof(*droplist)); if (!droplist) { @@ -815,71 +1546,44 @@ static int config_cap_drop(const char *key, char *value, return ret; } -static int config_console(const char *key, char *value, +static int config_console(const char *key, const char *value, struct lxc_conf *lxc_conf) { - char *path; - - path = strdup(value); - if (!path) { - SYSERROR("failed to strdup '%s': %m", value); - return -1; - } - - lxc_conf->console.path = path; - - return 0; + return config_path_item(&lxc_conf->console.path, value); } -static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_includefile(const char *key, const char *value, + struct lxc_conf *lxc_conf) { - if (strlen(value) >= MAXPATHLEN) { - ERROR("%s path is too long", value); - return -1; - } - - lxc_conf->rootfs.path = strdup(value); - if (!lxc_conf->rootfs.path) { - SYSERROR("failed to duplicate string %s", value); - return -1; - } - - return 0; + return lxc_config_read(value, lxc_conf); } -static int config_rootfs_mount(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_rootfs(const char *key, const char *value, + struct lxc_conf *lxc_conf) { - if (strlen(value) >= MAXPATHLEN) { - ERROR("%s path is too long", value); - return -1; - } - - lxc_conf->rootfs.mount = strdup(value); - if (!lxc_conf->rootfs.mount) { - SYSERROR("failed to duplicate string '%s'", value); - return -1; - } - - return 0; + return config_path_item(&lxc_conf->rootfs.path, value); } -static int config_pivotdir(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_rootfs_mount(const char *key, const char *value, + struct lxc_conf *lxc_conf) { - if (strlen(value) >= MAXPATHLEN) { - ERROR("%s path is too long", value); - return -1; - } + return config_path_item(&lxc_conf->rootfs.mount, value); +} - lxc_conf->rootfs.pivot = strdup(value); - if (!lxc_conf->rootfs.pivot) { - SYSERROR("failed to duplicate string %s", value); - return -1; - } +static int config_rootfs_options(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_string_item(&lxc_conf->rootfs.options, value); +} - return 0; +static int config_pivotdir(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + return config_path_item(&lxc_conf->rootfs.pivot, value); } -static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf) +static int config_utsname(const char *key, const char *value, + struct lxc_conf *lxc_conf) { struct utsname *utsname; @@ -891,11 +1595,14 @@ static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_con if (strlen(value) >= sizeof(utsname->nodename)) { ERROR("node name '%s' is too long", - utsname->nodename); + value); + free(utsname); return -1; } strcpy(utsname->nodename, value); + if (lxc_conf->utsname) + free(lxc_conf->utsname); lxc_conf->utsname = utsname; return 0; @@ -903,7 +1610,7 @@ static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_con static int parse_line(char *buffer, void *data) { - struct config *config; + struct lxc_config_t *config; char *line, *linep; char *dot; char *key; @@ -948,9 +1655,9 @@ static int parse_line(char *buffer, void *data) value += lxc_char_left_gc(value, strlen(value)); value[lxc_char_right_gc(value, strlen(value))] = '\0'; - config = getconfig(key); + config = lxc_getconfig(key); if (!config) { - ERROR("unknow key %s", key); + ERROR("unknown key %s", key); goto out; } @@ -961,13 +1668,20 @@ out: return ret; } -int lxc_config_readline(char *buffer, struct lxc_conf *conf) +static int lxc_config_readline(char *buffer, struct lxc_conf *conf) { return parse_line(buffer, conf); } int lxc_config_read(const char *file, struct lxc_conf *conf) { + if( access(file, R_OK) == -1 ) { + return -1; + } + /* Catch only the top level config file name in the structure */ + if( ! conf->rcfile ) { + conf->rcfile = strdup( file ); + } return lxc_file_for_each_line(file, parse_line, conf); } @@ -986,7 +1700,7 @@ int lxc_config_define_add(struct lxc_list *defines, char* arg) int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) { - struct lxc_list *it; + struct lxc_list *it,*next; int ret = 0; lxc_list_for_each(it, defines) { @@ -995,7 +1709,7 @@ int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) break; } - lxc_list_for_each(it, defines) { + lxc_list_for_each_safe(it, defines, next) { lxc_list_del(it); free(it); } @@ -1005,12 +1719,19 @@ int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) signed long lxc_config_parse_arch(const char *arch) { + #if HAVE_SYS_PERSONALITY_H struct per_name { char *name; unsigned long per; - } pername[4] = { + } pername[] = { { "x86", PER_LINUX32 }, + { "linux32", PER_LINUX32 }, + { "i386", PER_LINUX32 }, + { "i486", PER_LINUX32 }, + { "i586", PER_LINUX32 }, { "i686", PER_LINUX32 }, + { "athlon", PER_LINUX32 }, + { "linux64", PER_LINUX }, { "x86_64", PER_LINUX }, { "amd64", PER_LINUX }, }; @@ -1022,6 +1743,662 @@ signed long lxc_config_parse_arch(const char *arch) if (!strcmp(pername[i].name, arch)) return pername[i].per; } + #endif return -1; } + +int lxc_fill_elevated_privileges(char *flaglist, int *flags) +{ + char *token, *saveptr = NULL; + int i, aflag; + struct { const char *token; int flag; } all_privs[] = { + { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, + { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, + { "LSM", LXC_ATTACH_LSM_EXEC }, + { NULL, 0 } + }; + + if (!flaglist) { + /* for the sake of backward compatibility, drop all privileges + if none is specified */ + for (i = 0; all_privs[i].token; i++) { + *flags |= all_privs[i].flag; + } + return 0; + } + + token = strtok_r(flaglist, "|", &saveptr); + while (token) { + aflag = -1; + for (i = 0; all_privs[i].token; i++) { + if (!strcmp(all_privs[i].token, token)) + aflag = all_privs[i].flag; + } + if (aflag < 0) + return -1; + + *flags |= aflag; + + token = strtok_r(NULL, "|", &saveptr); + } + return 0; +} + +static int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) +{ + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + return snprintf(retv, inlen, "%d", v); +} + +static int lxc_get_arch_entry(struct lxc_conf *c, char *retv, int inlen) +{ + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + #if HAVE_SYS_PERSONALITY_H + int len = 0; + + switch(c->personality) { + case PER_LINUX32: strprint(retv, inlen, "i686"); break; + case PER_LINUX: strprint(retv, inlen, "x86_64"); break; + default: break; + } + #endif + + return fulllen; +} + +/* + * If you ask for a specific cgroup value, i.e. lxc.cgroup.devices.list, + * then just the value(s) will be printed. Since there still could be + * more than one, it is newline-separated. + * (Maybe that's ambigous, since some values, i.e. devices.list, will + * already have newlines?) + * If you ask for 'lxc.cgroup", then all cgroup entries will be printed, + * in 'lxc.cgroup.subsystem.key = value' format. + */ +static int lxc_get_cgroup_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->cgroup) { + struct lxc_cgroup *cg = it->elem; + if (all) { + strprint(retv, inlen, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); + } else if (strcmp(cg->subsystem, key) == 0) { + strprint(retv, inlen, "%s\n", cg->value); + } + } + return fulllen; +} + +static int lxc_get_item_hooks(struct lxc_conf *c, char *retv, int inlen, + const char *key) +{ + char *subkey; + int len, fulllen = 0, found = -1; + struct lxc_list *it; + int i; + + /* "lxc.hook.mount" */ + subkey = index(key, '.'); + if (subkey) subkey = index(subkey+1, '.'); + if (!subkey) + return -1; + subkey++; + if (!*subkey) + return -1; + for (i=0; ihooks[found]) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_item_groups(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->groups) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_item_cap_drop(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->caps) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_item_cap_keep(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->keepcaps) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->mount_list) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_auto_mounts(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + const char *sep = ""; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!(c->auto_mounts & LXC_AUTO_ALL_MASK)) + return 0; + + switch (c->auto_mounts & LXC_AUTO_PROC_MASK) { + case LXC_AUTO_PROC_MIXED: strprint(retv, inlen, "%sproc:mixed", sep); sep = " "; break; + case LXC_AUTO_PROC_RW: strprint(retv, inlen, "%sproc:rw", sep); sep = " "; break; + default: break; + } + switch (c->auto_mounts & LXC_AUTO_SYS_MASK) { + case LXC_AUTO_SYS_RO: strprint(retv, inlen, "%ssys:ro", sep); sep = " "; break; + case LXC_AUTO_SYS_RW: strprint(retv, inlen, "%ssys:rw", sep); sep = " "; break; + default: break; + } + switch (c->auto_mounts & LXC_AUTO_CGROUP_MASK) { + case LXC_AUTO_CGROUP_NOSPEC: strprint(retv, inlen, "%scgroup", sep); sep = " "; break; + case LXC_AUTO_CGROUP_MIXED: strprint(retv, inlen, "%scgroup:mixed", sep); sep = " "; break; + case LXC_AUTO_CGROUP_RO: strprint(retv, inlen, "%scgroup:ro", sep); sep = " "; break; + case LXC_AUTO_CGROUP_RW: strprint(retv, inlen, "%scgroup:rw", sep); sep = " "; break; + case LXC_AUTO_CGROUP_FULL_NOSPEC: strprint(retv, inlen, "%scgroup-full", sep); sep = " "; break; + case LXC_AUTO_CGROUP_FULL_MIXED: strprint(retv, inlen, "%scgroup-full:mixed", sep); sep = " "; break; + case LXC_AUTO_CGROUP_FULL_RO: strprint(retv, inlen, "%scgroup-full:ro", sep); sep = " "; break; + case LXC_AUTO_CGROUP_FULL_RW: strprint(retv, inlen, "%scgroup-full:rw", sep); sep = " "; break; + default: break; + } + + return fulllen; +} + +/* + * lxc.network.0.XXX, where XXX can be: name, type, link, flags, type, + * macvlan.mode, veth.pair, vlan, ipv4, ipv6, script.up, hwaddr, mtu, + * ipv4_gateway, ipv6_gateway. ipvX_gateway can return 'auto' instead + * of an address. ipv4 and ipv6 return lists (newline-separated). + * things like veth.pair return '' if invalid (i.e. if called for vlan + * type). + */ +static int lxc_get_item_nic(struct lxc_conf *c, char *retv, int inlen, + const char *key) +{ + char *p1; + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + p1 = index(key, '.'); + if (!p1 || *(p1+1) == '\0') return -1; + p1++; + + netdev = get_netdev_from_key(key, &c->network); + if (!netdev) + return -1; + if (strcmp(p1, "name") == 0) { + if (netdev->name) + strprint(retv, inlen, "%s", netdev->name); + } else if (strcmp(p1, "type") == 0) { + strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); + } else if (strcmp(p1, "link") == 0) { + if (netdev->link) + strprint(retv, inlen, "%s", netdev->link); + } else if (strcmp(p1, "flags") == 0) { + if (netdev->flags & IFF_UP) + strprint(retv, inlen, "up"); + } else if (strcmp(p1, "script.up") == 0) { + if (netdev->upscript) + strprint(retv, inlen, "%s", netdev->upscript); + } else if (strcmp(p1, "script.down") == 0) { + if (netdev->downscript) + strprint(retv, inlen, "%s", netdev->downscript); + } else if (strcmp(p1, "hwaddr") == 0) { + if (netdev->hwaddr) + strprint(retv, inlen, "%s", netdev->hwaddr); + } else if (strcmp(p1, "mtu") == 0) { + if (netdev->mtu) + strprint(retv, inlen, "%s", netdev->mtu); + } else if (strcmp(p1, "macvlan.mode") == 0) { + if (netdev->type == LXC_NET_MACVLAN) { + const char *mode; + switch (netdev->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: mode = "private"; break; + case MACVLAN_MODE_VEPA: mode = "vepa"; break; + case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; + default: mode = "(invalid)"; break; + } + strprint(retv, inlen, "%s", mode); + } + } else if (strcmp(p1, "veth.pair") == 0) { + if (netdev->type == LXC_NET_VETH) { + strprint(retv, inlen, "%s", + netdev->priv.veth_attr.pair ? + netdev->priv.veth_attr.pair : + netdev->priv.veth_attr.veth1); + } + } else if (strcmp(p1, "vlan") == 0) { + if (netdev->type == LXC_NET_VLAN) { + strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); + } + } else if (strcmp(p1, "ipv4_gateway") == 0) { + if (netdev->ipv4_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv4_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv4") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv4) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s\n", buf); + } + } else if (strcmp(p1, "ipv6_gateway") == 0) { + if (netdev->ipv6_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv6_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv6_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv6") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv6) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s\n", buf); + } + } + return fulllen; +} + +static int lxc_get_item_network(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); + } + return fulllen; +} + +int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv, + int inlen) +{ + const char *v = NULL; + + if (strcmp(key, "lxc.mount.entry") == 0) + return lxc_get_mount_entries(c, retv, inlen); + else if (strcmp(key, "lxc.mount.auto") == 0) + return lxc_get_auto_mounts(c, retv, inlen); + else if (strcmp(key, "lxc.mount") == 0) + v = c->fstab; + else if (strcmp(key, "lxc.tty") == 0) + return lxc_get_conf_int(c, retv, inlen, c->tty); + else if (strcmp(key, "lxc.pts") == 0) + return lxc_get_conf_int(c, retv, inlen, c->pts); + else if (strcmp(key, "lxc.devttydir") == 0) + v = c->ttydir; + else if (strcmp(key, "lxc.arch") == 0) + return lxc_get_arch_entry(c, retv, inlen); + else if (strcmp(key, "lxc.aa_profile") == 0) + v = c->lsm_aa_profile; + else if (strcmp(key, "lxc.se_context") == 0) + v = c->lsm_se_context; + else if (strcmp(key, "lxc.logfile") == 0) + v = lxc_log_get_file(); + else if (strcmp(key, "lxc.loglevel") == 0) + v = lxc_log_priority_to_string(lxc_log_get_level()); + else if (strcmp(key, "lxc.cgroup") == 0) // all cgroup info + return lxc_get_cgroup_entry(c, retv, inlen, "all"); + else if (strncmp(key, "lxc.cgroup.", 11) == 0) // specific cgroup info + return lxc_get_cgroup_entry(c, retv, inlen, key + 11); + else if (strcmp(key, "lxc.utsname") == 0) + v = c->utsname ? c->utsname->nodename : NULL; + else if (strcmp(key, "lxc.console") == 0) + v = c->console.path; + else if (strcmp(key, "lxc.rootfs.mount") == 0) + v = c->rootfs.mount; + else if (strcmp(key, "lxc.rootfs.options") == 0) + v = c->rootfs.options; + else if (strcmp(key, "lxc.rootfs") == 0) + v = c->rootfs.path; + else if (strcmp(key, "lxc.pivotdir") == 0) + v = c->rootfs.pivot; + else if (strcmp(key, "lxc.cap.drop") == 0) + return lxc_get_item_cap_drop(c, retv, inlen); + else if (strcmp(key, "lxc.cap.keep") == 0) + return lxc_get_item_cap_keep(c, retv, inlen); + else if (strncmp(key, "lxc.hook", 8) == 0) + return lxc_get_item_hooks(c, retv, inlen, key); + else if (strcmp(key, "lxc.network") == 0) + return lxc_get_item_network(c, retv, inlen); + else if (strncmp(key, "lxc.network.", 12) == 0) + return lxc_get_item_nic(c, retv, inlen, key + 12); + else if (strcmp(key, "lxc.start.auto") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_auto); + else if (strcmp(key, "lxc.start.delay") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_delay); + else if (strcmp(key, "lxc.start.order") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_order); + else if (strcmp(key, "lxc.group") == 0) + return lxc_get_item_groups(c, retv, inlen); + else if (strcmp(key, "lxc.seccomp") == 0) + v = c->seccomp; + else return -1; + + if (!v) + return 0; + if (retv && inlen >= strlen(v) + 1) + strncpy(retv, v, strlen(v)+1); + return strlen(v); +} + +int lxc_clear_config_item(struct lxc_conf *c, const char *key) +{ + if (strcmp(key, "lxc.network") == 0) + return lxc_clear_config_network(c); + else if (strncmp(key, "lxc.network.", 12) == 0) + return lxc_clear_nic(c, key + 12); + else if (strcmp(key, "lxc.cap.drop") == 0) + return lxc_clear_config_caps(c); + else if (strcmp(key, "lxc.cap.keep") == 0) + return lxc_clear_config_keepcaps(c); + else if (strncmp(key, "lxc.cgroup", 10) == 0) + return lxc_clear_cgroups(c, key); + else if (strcmp(key, "lxc.mount.entries") == 0) + return lxc_clear_mount_entries(c); + else if (strcmp(key, "lxc.mount.auto") == 0) + return lxc_clear_automounts(c); + else if (strncmp(key, "lxc.hook", 8) == 0) + return lxc_clear_hooks(c, key); + else if (strncmp(key, "lxc.group", 9) == 0) + return lxc_clear_groups(c); + else if (strncmp(key, "lxc.seccomp", 11) == 0) { + lxc_seccomp_free(c); + return 0; + } + + return -1; +} + +/* + * writing out a confile. + */ +void write_config(FILE *fout, struct lxc_conf *c) +{ + struct lxc_list *it; + int i; + + if (c->fstab) + fprintf(fout, "lxc.mount = %s\n", c->fstab); + lxc_list_for_each(it, &c->mount_list) { + fprintf(fout, "lxc.mount.entry = %s\n", (char *)it->elem); + } + if (c->auto_mounts & LXC_AUTO_ALL_MASK) { + fprintf(fout, "lxc.mount.auto ="); + switch (c->auto_mounts & LXC_AUTO_PROC_MASK) { + case LXC_AUTO_PROC_MIXED: fprintf(fout, " proc:mixed"); break; + case LXC_AUTO_PROC_RW: fprintf(fout, " proc:rw"); break; + default: break; + } + switch (c->auto_mounts & LXC_AUTO_SYS_MASK) { + case LXC_AUTO_SYS_RO: fprintf(fout, " sys:ro"); break; + case LXC_AUTO_SYS_RW: fprintf(fout, " sys:rw"); break; + default: break; + } + switch (c->auto_mounts & LXC_AUTO_CGROUP_MASK) { + case LXC_AUTO_CGROUP_NOSPEC: fprintf(fout, " cgroup"); break; + case LXC_AUTO_CGROUP_MIXED: fprintf(fout, " cgroup:mixed"); break; + case LXC_AUTO_CGROUP_RO: fprintf(fout, " cgroup:ro"); break; + case LXC_AUTO_CGROUP_RW: fprintf(fout, " cgroup:rw"); break; + case LXC_AUTO_CGROUP_FULL_NOSPEC: fprintf(fout, " cgroup-full"); break; + case LXC_AUTO_CGROUP_FULL_MIXED: fprintf(fout, " cgroup-full:mixed"); break; + case LXC_AUTO_CGROUP_FULL_RO: fprintf(fout, " cgroup-full:ro"); break; + case LXC_AUTO_CGROUP_FULL_RW: fprintf(fout, " cgroup-full:rw"); break; + default: break; + } + fprintf(fout, "\n"); + } + if (c->tty) + fprintf(fout, "lxc.tty = %d\n", c->tty); + if (c->pts) + fprintf(fout, "lxc.pts = %d\n", c->pts); + if (c->ttydir) + fprintf(fout, "lxc.devttydir = %s\n", c->ttydir); + if (c->haltsignal) + fprintf(fout, "lxc.haltsignal = SIG%s\n", sig_name(c->haltsignal)); + if (c->stopsignal) + fprintf(fout, "lxc.stopsignal = SIG%s\n", sig_name(c->stopsignal)); + #if HAVE_SYS_PERSONALITY_H + switch(c->personality) { + case PER_LINUX32: fprintf(fout, "lxc.arch = i686\n"); break; + case PER_LINUX: fprintf(fout, "lxc.arch = x86_64\n"); break; + default: break; + } + #endif + if (c->lsm_aa_profile) + fprintf(fout, "lxc.aa_profile = %s\n", c->lsm_aa_profile); + if (c->lsm_se_context) + fprintf(fout, "lxc.se_context = %s\n", c->lsm_se_context); + if (c->seccomp) + fprintf(fout, "lxc.seccomp = %s\n", c->seccomp); + if (c->kmsg == 0) + fprintf(fout, "lxc.kmsg = 0\n"); + if (c->autodev > 0) + fprintf(fout, "lxc.autodev = 1\n"); + if (c->loglevel != LXC_LOG_PRIORITY_NOTSET) + fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel)); + if (c->logfile) + fprintf(fout, "lxc.logfile = %s\n", c->logfile); + lxc_list_for_each(it, &c->cgroup) { + struct lxc_cgroup *cg = it->elem; + fprintf(fout, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); + } + if (c->utsname) + fprintf(fout, "lxc.utsname = %s\n", c->utsname->nodename); + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + struct lxc_list *it2; + fprintf(fout, "lxc.network.type = %s\n", t ? t : "(invalid)"); + if (n->flags & IFF_UP) + fprintf(fout, "lxc.network.flags = up\n"); + if (n->link) + fprintf(fout, "lxc.network.link = %s\n", n->link); + if (n->name) + fprintf(fout, "lxc.network.name = %s\n", n->name); + if (n->type == LXC_NET_MACVLAN) { + const char *mode; + switch (n->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: mode = "private"; break; + case MACVLAN_MODE_VEPA: mode = "vepa"; break; + case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; + default: mode = "(invalid)"; break; + } + fprintf(fout, "lxc.network.macvlan.mode = %s\n", mode); + } else if (n->type == LXC_NET_VETH) { + if (n->priv.veth_attr.pair) + fprintf(fout, "lxc.network.veth.pair = %s\n", + n->priv.veth_attr.pair); + } else if (n->type == LXC_NET_VLAN) { + fprintf(fout, "lxc.network.vlan.id = %d\n", n->priv.vlan_attr.vid); + } + if (n->upscript) + fprintf(fout, "lxc.network.script.up = %s\n", n->upscript); + if (n->downscript) + fprintf(fout, "lxc.network.script.down = %s\n", n->downscript); + if (n->hwaddr) + fprintf(fout, "lxc.network.hwaddr = %s\n", n->hwaddr); + if (n->mtu) + fprintf(fout, "lxc.network.mtu = %s\n", n->mtu); + if (n->ipv4_gateway_auto) + fprintf(fout, "lxc.network.ipv4.gateway = auto\n"); + else if (n->ipv4_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, n->ipv4_gateway, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv4.gateway = %s\n", buf); + } + lxc_list_for_each(it2, &n->ipv4) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv4 = %s", buf); + + if (i->prefix) + fprintf(fout, "/%d", i->prefix); + + if (i->bcast.s_addr != (i->addr.s_addr | + htonl(INADDR_BROADCAST >> i->prefix))) { + + inet_ntop(AF_INET, &i->bcast, buf, sizeof(buf)); + fprintf(fout, " %s\n", buf); + } + else + fprintf(fout, "\n"); + } + if (n->ipv6_gateway_auto) + fprintf(fout, "lxc.network.ipv6.gateway = auto\n"); + else if (n->ipv6_gateway) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, n->ipv6_gateway, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv6.gateway = %s\n", buf); + } + lxc_list_for_each(it2, &n->ipv6) { + struct lxc_inet6dev *i = it2->elem; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); + if (i->prefix) + fprintf(fout, "lxc.network.ipv6 = %s/%d\n", + buf, i->prefix); + else + fprintf(fout, "lxc.network.ipv6 = %s\n", buf); + } + } + lxc_list_for_each(it, &c->caps) + fprintf(fout, "lxc.cap.drop = %s\n", (char *)it->elem); + lxc_list_for_each(it, &c->keepcaps) + fprintf(fout, "lxc.cap.keep = %s\n", (char *)it->elem); + lxc_list_for_each(it, &c->id_map) { + struct id_map *idmap = it->elem; + fprintf(fout, "lxc.id_map = %c %lu %lu %lu\n", + idmap->idtype == ID_TYPE_UID ? 'u' : 'g', idmap->nsid, + idmap->hostid, idmap->range); + } + for (i=0; ihooks[i]) + fprintf(fout, "lxc.hook.%s = %s\n", + lxchook_names[i], (char *)it->elem); + } + if (c->console.path) + fprintf(fout, "lxc.console = %s\n", c->console.path); + if (c->rootfs.path) + fprintf(fout, "lxc.rootfs = %s\n", c->rootfs.path); + if (c->rootfs.mount && strcmp(c->rootfs.mount, LXCROOTFSMOUNT) != 0) + fprintf(fout, "lxc.rootfs.mount = %s\n", c->rootfs.mount); + if (c->rootfs.options) + fprintf(fout, "lxc.rootfs.options = %s\n", c->rootfs.options); + if (c->rootfs.pivot) + fprintf(fout, "lxc.pivotdir = %s\n", c->rootfs.pivot); + if (c->start_auto) + fprintf(fout, "lxc.start.auto = %d\n", c->start_auto); + if (c->start_delay) + fprintf(fout, "lxc.start.delay = %d\n", c->start_delay); + if (c->start_order) + fprintf(fout, "lxc.start.order = %d\n", c->start_order); + lxc_list_for_each(it, &c->groups) + fprintf(fout, "lxc.group = %s\n", (char *)it->elem); +}