<commit_before>/*
 * lxc: linux Container library
 *
 * (C) Copyright IBM Corp. 2007, 2008
 *
 * Authors:
 * <NAME> <<EMAIL>.<EMAIL> at free.fr>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>

#include "parse.h"
#include "config.h"
#include "confile.h"
#include "utils.h"
#include "log.h"
#include "conf.h"
#include "network.h"
#include "lxcseccomp.h"

#if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h>
#endif

lxc_log_define(lxc_confile, lxc);

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               },
	{ "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_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      },
	{ "lxc.network.ipv4.gateway", config_network_ipv4_gateway },
	{ "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 lxc_config_t);

extern struct lxc_config_t *lxc_getconfig(const char *key)
{
	int i;

	for (i = 0; i < config_size; i++)
		if (!strncmp(config[i].name, key,
			     strlen(config[i].name)))
			return &config[i];
	return NULL;
}

#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");
		return -1;
	}

	memset(netdev, 0, sizeof(*netdev));
	lxc_list_init(&netdev->ipv4);
	lxc_list_init(&netdev->ipv6);

	list = malloc(sizeof(*list));
	if (!list) {
		SYSERROR("failed to allocate memory");
		free(netdev);
		return -1;
	}

	lxc_list_init(list);
	list->elem = netdev;

	lxc_list_add_tail(network, list);

	if (!strcmp(value, "veth"))
		netdev->type = LXC_NET_VETH;
	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;
	}
	return 0;
}

static int config_ip_prefix(struct in_addr *addr)
{
	if (IN_CLASSA(addr->s_addr))
		return 32 - IN_CLASSA_NSHIFT;
	if (IN_CLASSB(addr->s_addr))
		return 32 - IN_CLASSB_NSHIFT;
	if (IN_CLASSC(addr->s_addr))
		return 32 - IN_CLASSC_NSHIFT;

	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 = NULL;

	if (lxc_list_empty(network)) {
		ERROR("network is not created for '%s' = '%s' option",
		      key, value);
		return NULL;
	}

	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);
		return NULL;
	}

	return netdev;
}

static int network_ifname(char **valuep, const char *value)
{
	return config_string_item_max(valuep, value, IFNAMSIZ);
}

#ifndef MACVLAN_MODE_PRIVATE
#  define MACVLAN_MODE_PRIVATE 1
#endif

#ifndef MACVLAN_MODE_VEPA
#  define MACVLAN_MODE_VEPA 2
#endif

#ifndef MACVLAN_MODE_BRIDGE
#  define MACVLAN_MODE_BRIDGE 4
#endif

static int macvlan_mode(int *valuep, const char *value)
{
	struct mc_mode {
		char *name;
		int mode;
	} m[] = {
		{ "private", MACVLAN_MODE_PRIVATE },
		{ "vepa", MACVLAN_MODE_VEPA },
		{ "bridge", MACVLAN_MODE_BRIDGE },
	};

	int i;

	for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) {
		if (strcmp(m[i].name, value))
			continue;

		*valuep = m[i].mode;
		return 0;
	}

	return -1;
}

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;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	netdev->flags |= IFF_UP;

	return 0;
}

static int config_network_link(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 network_ifname(&netdev->link, value);
}

static int config_network_name(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 network_ifname(&netdev->name, value);
}

static int config_network_veth_pair(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 network_ifname(&netdev->priv.veth_attr.pair, value);
}

static int config_network_macvlan_mode(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 macvlan_mode(&netdev->priv.macvlan_attr.mode, value);
}

static int config_network_hwaddr(const char *key, const char *value,
				 struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;

	char *new_value = strdup(value);
	if (!new_value) {
		SYSERROR("failed to strdup '%s': %m", value);
		return -1;
	}
	rand_complete_hwaddr(new_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, const char *value,
			       struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	if (get_u16(&netdev->priv.vlan_attr.vid, value, 0))
		return -1;

	return 0;
}

static int config_network_mtu(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->mtu, value);
}

static int config_network_ipv4(const char *key, const char *value,
			       struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;
	struct lxc_inetdev *inetdev;
	struct lxc_list *list;
	char *cursor, *slash, *addr = NULL, *bcast = NULL, *prefix = NULL;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	inetdev = malloc(sizeof(*inetdev));<commit_msg>confile: sanity-check netdev->type before setting netdev->priv elements<commit_after>/*
 * lxc: linux Container library
 *
 * (C) Copyright IBM Corp. 2007, 2008
 *
 * Authors:
 * <NAME> <daniel.lezcano at free.fr>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>

#include "parse.h"
#include "config.h"
#include "confile.h"
#include "utils.h"
#include "log.h"
#include "conf.h"
#include "network.h"
#include "lxcseccomp.h"

#if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h>
#endif

lxc_log_define(lxc_confile, lxc);

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               },
	{ "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_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      },
	{ "lxc.network.ipv4.gateway", config_network_ipv4_gateway },
	{ "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 lxc_config_t);

extern struct lxc_config_t *lxc_getconfig(const char *key)
{
	int i;

	for (i = 0; i < config_size; i++)
		if (!strncmp(config[i].name, key,
			     strlen(config[i].name)))
			return &config[i];
	return NULL;
}

#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 '<KEY>.' 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");
		return -1;
	}

	memset(netdev, 0, sizeof(*netdev));
	lxc_list_init(&netdev->ipv4);
	lxc_list_init(&netdev->ipv6);

	list = malloc(sizeof(*list));
	if (!list) {
		SYSERROR("failed to allocate memory");
		free(netdev);
		return -1;
	}

	lxc_list_init(list);
	list->elem = netdev;

	lxc_list_add_tail(network, list);

	if (!strcmp(value, "veth"))
		netdev->type = LXC_NET_VETH;
	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;
	}
	return 0;
}

static int config_ip_prefix(struct in_addr *addr)
{
	if (IN_CLASSA(addr->s_addr))
		return 32 - IN_CLASSA_NSHIFT;
	if (IN_CLASSB(addr->s_addr))
		return 32 - IN_CLASSB_NSHIFT;
	if (IN_CLASSC(addr->s_addr))
		return 32 - IN_CLASSC_NSHIFT;

	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 = NULL;

	if (lxc_list_empty(network)) {
		ERROR("network is not created for '%s' = '%s' option",
		      key, value);
		return NULL;
	}

	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);
		return NULL;
	}

	return netdev;
}

static int network_ifname(char **valuep, const char *value)
{
	return config_string_item_max(valuep, value, IFNAMSIZ);
}

#ifndef MACVLAN_MODE_PRIVATE
#  define MACVLAN_MODE_PRIVATE 1
#endif

#ifndef MACVLAN_MODE_VEPA
#  define MACVLAN_MODE_VEPA 2
#endif

#ifndef MACVLAN_MODE_BRIDGE
#  define MACVLAN_MODE_BRIDGE 4
#endif

static int macvlan_mode(int *valuep, const char *value)
{
	struct mc_mode {
		char *name;
		int mode;
	} m[] = {
		{ "private", MACVLAN_MODE_PRIVATE },
		{ "vepa", MACVLAN_MODE_VEPA },
		{ "bridge", MACVLAN_MODE_BRIDGE },
	};

	int i;

	for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) {
		if (strcmp(m[i].name, value))
			continue;

		*valuep = m[i].mode;
		return 0;
	}

	return -1;
}

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;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	netdev->flags |= IFF_UP;

	return 0;
}

static int config_network_link(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 network_ifname(&netdev->link, value);
}

static int config_network_name(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 network_ifname(&netdev->name, value);
}

static int config_network_veth_pair(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;

	if (netdev->type != LXC_NET_VETH) {
		ERROR("Invalid veth pair for a non-veth netdev")
		return -1;
	}
	return network_ifname(&netdev->priv.veth_attr.pair, value);
}

static int config_network_macvlan_mode(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;

	if (netdev->type != LXC_NET_MACVLAN) {
		ERROR("Invalid macvlan.mode for a non-macvlan netdev")
		return -1;
	}
	return macvlan_mode(&netdev->priv.macvlan_attr.mode, value);
}

static int config_network_hwaddr(const char *key, const char *value,
				 struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;

	char *new_value = strdup(value);
	if (!new_value) {
		SYSERROR("failed to strdup '%s': %m", value);
		return -1;
	}
	rand_complete_hwaddr(new_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, const char *value,
			       struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	if (netdev->type != LXC_NET_VLAN) {
		ERROR("Invalid vlan.id for a non-macvlan netdev")
		return -1;
	}
	if (get_u16(&netdev->priv.vlan_attr.vid, value, 0))
		return -1;

	return 0;
}

static int config_network_mtu(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->mtu, value);
}

static int config_network_ipv4(const char *key, const char *value,
			       struct lxc_conf *lxc_conf)
{
	struct lxc_netdev *netdev;
	struct lxc_inetdev *inetdev;
	struct lxc_list *list;
	char *cursor, *slash, *addr = NULL, *bcast = NULL, *prefix = NULL;

	netdev = network_netdev(key, value, &lxc_conf->network);
	if (!netdev)
		return -1;

	inetdev = malloc(sizeof(*inetdev));
{'fraction_non_alphanumeric': 0.13682789536690354, 'fraction_numerical': 0.008277934846285117, 'mean_word_length': 3.3083507077800944, 'pattern_counts': {'":': 0, '<': 56, '<?xml version=': 0, '>': 131, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6465632', 'n_tokens_mistral': 14881, 'n_tokens_neox': 14339, 'n_words': 3591}