mirror of
git://github.com/lxc/lxc
synced 2025-08-22 13:41:33 +00:00
1041 lines
21 KiB
C
1041 lines
21 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE 1
|
|
#endif
|
|
#include <arpa/inet.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "conf.h"
|
|
#include "config.h"
|
|
#include "confile.h"
|
|
#include "confile_utils.h"
|
|
#include "error.h"
|
|
#include "list.h"
|
|
#include "log.h"
|
|
#include "lxccontainer.h"
|
|
#include "macro.h"
|
|
#include "network.h"
|
|
#include "parse.h"
|
|
#include "utils.h"
|
|
|
|
#ifndef HAVE_STRLCPY
|
|
#include "include/strlcpy.h"
|
|
#endif
|
|
|
|
lxc_log_define(confile_utils, lxc);
|
|
|
|
int parse_idmaps(const char *idmap, char *type, unsigned long *nsid,
|
|
unsigned long *hostid, unsigned long *range)
|
|
{
|
|
int ret = -1;
|
|
unsigned long tmp_hostid, tmp_nsid, tmp_range;
|
|
char tmp_type;
|
|
char *window, *slide;
|
|
char *dup = NULL;
|
|
|
|
/* Duplicate string. */
|
|
dup = strdup(idmap);
|
|
if (!dup)
|
|
goto on_error;
|
|
|
|
/* A prototypical idmap entry would be: "u 1000 1000000 65536" */
|
|
|
|
/* align */
|
|
slide = window = dup;
|
|
/* skip whitespace */
|
|
slide += strspn(slide, " \t\r");
|
|
if (slide != window && *slide == '\0')
|
|
goto on_error;
|
|
|
|
/* Validate type. */
|
|
if (*slide != 'u' && *slide != 'g') {
|
|
ERROR("Invalid id mapping type: %c", *slide);
|
|
goto on_error;
|
|
}
|
|
|
|
/* Assign type. */
|
|
tmp_type = *slide;
|
|
|
|
/* move beyond type */
|
|
slide++;
|
|
/* align */
|
|
window = slide;
|
|
/* Validate that only whitespace follows. */
|
|
slide += strspn(slide, " \t\r");
|
|
/* There must be whitespace. */
|
|
if (slide == window)
|
|
goto on_error;
|
|
|
|
/* Mark beginning of nsid. */
|
|
window = slide;
|
|
/* Validate that non-whitespace follows. */
|
|
slide += strcspn(slide, " \t\r");
|
|
/* There must be non-whitespace. */
|
|
if (slide == window || *slide == '\0')
|
|
goto on_error;
|
|
/* Mark end of nsid. */
|
|
*slide = '\0';
|
|
|
|
/* Parse nsid. */
|
|
if (lxc_safe_ulong(window, &tmp_nsid) < 0) {
|
|
ERROR("Failed to parse nsid: %s", window);
|
|
goto on_error;
|
|
}
|
|
|
|
/* Move beyond \0. */
|
|
slide++;
|
|
/* Validate that only whitespace follows. */
|
|
slide += strspn(slide, " \t\r");
|
|
/* If there was only one whitespace then we whiped it with our \0 above.
|
|
* So only ensure that we're not at the end of the string.
|
|
*/
|
|
if (*slide == '\0')
|
|
goto on_error;
|
|
|
|
/* Mark beginning of hostid. */
|
|
window = slide;
|
|
/* Validate that non-whitespace follows. */
|
|
slide += strcspn(slide, " \t\r");
|
|
/* There must be non-whitespace. */
|
|
if (slide == window || *slide == '\0')
|
|
goto on_error;
|
|
/* Mark end of nsid. */
|
|
*slide = '\0';
|
|
|
|
/* Parse hostid. */
|
|
if (lxc_safe_ulong(window, &tmp_hostid) < 0) {
|
|
ERROR("Failed to parse hostid: %s", window);
|
|
goto on_error;
|
|
}
|
|
|
|
/* Move beyond \0. */
|
|
slide++;
|
|
/* Validate that only whitespace follows. */
|
|
slide += strspn(slide, " \t\r");
|
|
/* If there was only one whitespace then we whiped it with our \0 above.
|
|
* So only ensure that we're not at the end of the string.
|
|
*/
|
|
if (*slide == '\0')
|
|
goto on_error;
|
|
|
|
/* Mark beginning of range. */
|
|
window = slide;
|
|
/* Validate that non-whitespace follows. */
|
|
slide += strcspn(slide, " \t\r");
|
|
/* There must be non-whitespace. */
|
|
if (slide == window)
|
|
goto on_error;
|
|
|
|
/* The range is the last valid entry we expect. So make sure that there
|
|
* is no trailing garbage and if there is, error out.
|
|
*/
|
|
if (*(slide + strspn(slide, " \t\r\n")) != '\0')
|
|
goto on_error;
|
|
|
|
/* Mark end of range. */
|
|
*slide = '\0';
|
|
|
|
/* Parse range. */
|
|
if (lxc_safe_ulong(window, &tmp_range) < 0) {
|
|
ERROR("Failed to parse id mapping range: %s", window);
|
|
goto on_error;
|
|
}
|
|
|
|
*type = tmp_type;
|
|
*nsid = tmp_nsid;
|
|
*hostid = tmp_hostid;
|
|
*range = tmp_range;
|
|
|
|
/* Yay, we survived. */
|
|
ret = 0;
|
|
|
|
on_error:
|
|
free(dup);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool lxc_config_value_empty(const char *value)
|
|
{
|
|
if (value && strlen(value) > 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail)
|
|
{
|
|
struct lxc_list *newlist;
|
|
struct lxc_netdev *netdev = NULL;
|
|
|
|
/* network does not exist */
|
|
netdev = malloc(sizeof(*netdev));
|
|
if (!netdev)
|
|
return NULL;
|
|
|
|
memset(netdev, 0, sizeof(*netdev));
|
|
lxc_list_init(&netdev->ipv4);
|
|
lxc_list_init(&netdev->ipv6);
|
|
|
|
/* give network a unique index */
|
|
netdev->idx = idx;
|
|
|
|
/* prepare new list */
|
|
newlist = malloc(sizeof(*newlist));
|
|
if (!newlist) {
|
|
free(netdev);
|
|
return NULL;
|
|
}
|
|
|
|
lxc_list_init(newlist);
|
|
newlist->elem = netdev;
|
|
|
|
if (tail)
|
|
lxc_list_add_tail(networks, newlist);
|
|
else
|
|
lxc_list_add(networks, newlist);
|
|
|
|
return netdev;
|
|
}
|
|
|
|
/* Takes care of finding the correct netdev struct in the networks list or
|
|
* allocates a new one if it couldn't be found.
|
|
*/
|
|
struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf,
|
|
unsigned int idx, bool allocate)
|
|
{
|
|
struct lxc_netdev *netdev = NULL;
|
|
struct lxc_list *networks = &conf->network;
|
|
struct lxc_list *insert = networks;
|
|
|
|
/* lookup network */
|
|
if (!lxc_list_empty(networks)) {
|
|
lxc_list_for_each(insert, networks) {
|
|
netdev = insert->elem;
|
|
if (netdev->idx == idx)
|
|
return netdev;
|
|
else if (netdev->idx > idx)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!allocate)
|
|
return NULL;
|
|
|
|
return lxc_network_add(insert, idx, true);
|
|
}
|
|
|
|
void lxc_log_configured_netdevs(const struct lxc_conf *conf)
|
|
{
|
|
struct lxc_netdev *netdev;
|
|
struct lxc_list *it = (struct lxc_list *)&conf->network;;
|
|
|
|
if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) &&
|
|
(lxc_log_get_level() != LXC_LOG_LEVEL_TRACE))
|
|
return;
|
|
|
|
if (lxc_list_empty(it)) {
|
|
TRACE("container has no networks configured");
|
|
return;
|
|
}
|
|
|
|
lxc_list_for_each(it, &conf->network) {
|
|
struct lxc_list *cur, *next;
|
|
struct lxc_inetdev *inet4dev;
|
|
struct lxc_inet6dev *inet6dev;
|
|
char bufinet4[INET_ADDRSTRLEN], bufinet6[INET6_ADDRSTRLEN];
|
|
|
|
netdev = it->elem;
|
|
|
|
TRACE("index: %zd", netdev->idx);
|
|
TRACE("ifindex: %d", netdev->ifindex);
|
|
|
|
switch (netdev->type) {
|
|
case LXC_NET_VETH:
|
|
TRACE("type: veth");
|
|
|
|
if (netdev->priv.veth_attr.pair[0] != '\0')
|
|
TRACE("veth pair: %s",
|
|
netdev->priv.veth_attr.pair);
|
|
|
|
if (netdev->priv.veth_attr.veth1[0] != '\0')
|
|
TRACE("veth1 : %s",
|
|
netdev->priv.veth_attr.veth1);
|
|
|
|
if (netdev->priv.veth_attr.ifindex > 0)
|
|
TRACE("host side ifindex for veth device: %d",
|
|
netdev->priv.veth_attr.ifindex);
|
|
break;
|
|
case LXC_NET_MACVLAN:
|
|
TRACE("type: macvlan");
|
|
|
|
if (netdev->priv.macvlan_attr.mode > 0) {
|
|
char *mode;
|
|
|
|
mode = lxc_macvlan_flag_to_mode(
|
|
netdev->priv.macvlan_attr.mode);
|
|
TRACE("macvlan mode: %s",
|
|
mode ? mode : "(invalid mode)");
|
|
}
|
|
break;
|
|
case LXC_NET_IPVLAN:
|
|
TRACE("type: ipvlan");
|
|
|
|
char *mode;
|
|
mode = lxc_ipvlan_flag_to_mode(netdev->priv.ipvlan_attr.mode);
|
|
TRACE("ipvlan mode: %s", mode ? mode : "(invalid mode)");
|
|
|
|
char *isolation;
|
|
isolation = lxc_ipvlan_flag_to_isolation(netdev->priv.ipvlan_attr.isolation);
|
|
TRACE("ipvlan isolation: %s", isolation ? isolation : "(invalid isolation)");
|
|
break;
|
|
case LXC_NET_VLAN:
|
|
TRACE("type: vlan");
|
|
TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
|
|
break;
|
|
case LXC_NET_PHYS:
|
|
TRACE("type: phys");
|
|
|
|
if (netdev->priv.phys_attr.ifindex > 0)
|
|
TRACE("host side ifindex for phys device: %d",
|
|
netdev->priv.phys_attr.ifindex);
|
|
break;
|
|
case LXC_NET_EMPTY:
|
|
TRACE("type: empty");
|
|
break;
|
|
case LXC_NET_NONE:
|
|
TRACE("type: none");
|
|
break;
|
|
default:
|
|
ERROR("Invalid network type %d", netdev->type);
|
|
return;
|
|
}
|
|
|
|
if (netdev->type != LXC_NET_EMPTY) {
|
|
TRACE("flags: %s",
|
|
netdev->flags == IFF_UP ? "up" : "none");
|
|
|
|
if (netdev->link[0] != '\0')
|
|
TRACE("link: %s", netdev->link);
|
|
|
|
/* l2proxy only used when link is specified */
|
|
if (netdev->link[0] != '\0')
|
|
TRACE("l2proxy: %s", netdev->l2proxy ? "true" : "false");
|
|
|
|
if (netdev->name[0] != '\0')
|
|
TRACE("name: %s", netdev->name);
|
|
|
|
if (netdev->hwaddr)
|
|
TRACE("hwaddr: %s", netdev->hwaddr);
|
|
|
|
if (netdev->mtu)
|
|
TRACE("mtu: %s", netdev->mtu);
|
|
|
|
if (netdev->upscript)
|
|
TRACE("upscript: %s", netdev->upscript);
|
|
|
|
if (netdev->downscript)
|
|
TRACE("downscript: %s", netdev->downscript);
|
|
|
|
TRACE("ipv4 gateway auto: %s",
|
|
netdev->ipv4_gateway_auto ? "true" : "false");
|
|
|
|
TRACE("ipv4 gateway dev: %s",
|
|
netdev->ipv4_gateway_dev ? "true" : "false");
|
|
|
|
if (netdev->ipv4_gateway) {
|
|
inet_ntop(AF_INET, netdev->ipv4_gateway,
|
|
bufinet4, sizeof(bufinet4));
|
|
TRACE("ipv4 gateway: %s", bufinet4);
|
|
}
|
|
|
|
lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
|
|
inet4dev = cur->elem;
|
|
inet_ntop(AF_INET, &inet4dev->addr, bufinet4,
|
|
sizeof(bufinet4));
|
|
TRACE("ipv4 addr: %s", bufinet4);
|
|
}
|
|
|
|
TRACE("ipv6 gateway auto: %s",
|
|
netdev->ipv6_gateway_auto ? "true" : "false");
|
|
|
|
TRACE("ipv6 gateway dev: %s",
|
|
netdev->ipv6_gateway_dev ? "true" : "false");
|
|
|
|
if (netdev->ipv6_gateway) {
|
|
inet_ntop(AF_INET6, netdev->ipv6_gateway,
|
|
bufinet6, sizeof(bufinet6));
|
|
TRACE("ipv6 gateway: %s", bufinet6);
|
|
}
|
|
|
|
lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
|
|
inet6dev = cur->elem;
|
|
inet_ntop(AF_INET6, &inet6dev->addr, bufinet6,
|
|
sizeof(bufinet6));
|
|
TRACE("ipv6 addr: %s", bufinet6);
|
|
}
|
|
|
|
if (netdev->type == LXC_NET_VETH) {
|
|
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv4_routes, next) {
|
|
inet4dev = cur->elem;
|
|
if (!inet_ntop(AF_INET, &inet4dev->addr, bufinet4, sizeof(bufinet4))) {
|
|
ERROR("Invalid ipv4 veth route");
|
|
return;
|
|
}
|
|
|
|
TRACE("ipv4 veth route: %s/%u", bufinet4, inet4dev->prefix);
|
|
}
|
|
|
|
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv6_routes, next) {
|
|
inet6dev = cur->elem;
|
|
if (!inet_ntop(AF_INET6, &inet6dev->addr, bufinet6, sizeof(bufinet6))) {
|
|
ERROR("Invalid ipv6 veth route");
|
|
return;
|
|
}
|
|
|
|
TRACE("ipv6 veth route: %s/%u", bufinet6, inet6dev->prefix);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void lxc_free_netdev(struct lxc_netdev *netdev)
|
|
{
|
|
struct lxc_list *cur, *next;
|
|
|
|
free(netdev->upscript);
|
|
free(netdev->downscript);
|
|
free(netdev->hwaddr);
|
|
free(netdev->mtu);
|
|
|
|
free(netdev->ipv4_gateway);
|
|
lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
|
|
lxc_list_del(cur);
|
|
free(cur->elem);
|
|
free(cur);
|
|
}
|
|
|
|
free(netdev->ipv6_gateway);
|
|
lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
|
|
lxc_list_del(cur);
|
|
free(cur->elem);
|
|
free(cur);
|
|
}
|
|
|
|
if (netdev->type == LXC_NET_VETH) {
|
|
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv4_routes, next) {
|
|
lxc_list_del(cur);
|
|
free(cur->elem);
|
|
free(cur);
|
|
}
|
|
|
|
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv6_routes, next) {
|
|
lxc_list_del(cur);
|
|
free(cur->elem);
|
|
free(cur);
|
|
}
|
|
}
|
|
|
|
free(netdev);
|
|
}
|
|
|
|
bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx)
|
|
{
|
|
struct lxc_list *cur, *next;
|
|
struct lxc_netdev *netdev;
|
|
bool found = false;
|
|
|
|
lxc_list_for_each_safe(cur, &conf->network, next) {
|
|
netdev = cur->elem;
|
|
if (netdev->idx != idx)
|
|
continue;
|
|
|
|
lxc_list_del(cur);
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
return false;
|
|
|
|
lxc_free_netdev(netdev);
|
|
free(cur);
|
|
|
|
return true;
|
|
}
|
|
|
|
void lxc_free_networks(struct lxc_list *networks)
|
|
{
|
|
struct lxc_list *cur, *next;
|
|
struct lxc_netdev *netdev;
|
|
|
|
lxc_list_for_each_safe(cur, networks, next) {
|
|
netdev = cur->elem;
|
|
lxc_free_netdev(netdev);
|
|
free(cur);
|
|
}
|
|
|
|
/* prevent segfaults */
|
|
lxc_list_init(networks);
|
|
}
|
|
|
|
|
|
static struct lxc_veth_mode {
|
|
char *name;
|
|
int mode;
|
|
} veth_mode[] = {
|
|
{ "bridge", VETH_MODE_BRIDGE },
|
|
{ "router", VETH_MODE_ROUTER },
|
|
};
|
|
|
|
int lxc_veth_mode_to_flag(int *mode, const char *value)
|
|
{
|
|
for (size_t i = 0; i < sizeof(veth_mode) / sizeof(veth_mode[0]); i++) {
|
|
if (strcmp(veth_mode[i].name, value) != 0)
|
|
continue;
|
|
|
|
*mode = veth_mode[i].mode;
|
|
return 0;
|
|
}
|
|
|
|
return minus_one_set_errno(EINVAL);
|
|
}
|
|
|
|
static struct lxc_macvlan_mode {
|
|
char *name;
|
|
int mode;
|
|
} macvlan_mode[] = {
|
|
{ "private", MACVLAN_MODE_PRIVATE },
|
|
{ "vepa", MACVLAN_MODE_VEPA },
|
|
{ "bridge", MACVLAN_MODE_BRIDGE },
|
|
{ "passthru", MACVLAN_MODE_PASSTHRU },
|
|
};
|
|
|
|
int lxc_macvlan_mode_to_flag(int *mode, const char *value)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
|
|
if (strcmp(macvlan_mode[i].name, value))
|
|
continue;
|
|
|
|
*mode = macvlan_mode[i].mode;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
char *lxc_macvlan_flag_to_mode(int mode)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
|
|
if (macvlan_mode[i].mode != mode)
|
|
continue;
|
|
|
|
return macvlan_mode[i].name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct lxc_ipvlan_mode {
|
|
char *name;
|
|
int mode;
|
|
} ipvlan_mode[] = {
|
|
{ "l3", IPVLAN_MODE_L3 },
|
|
{ "l3s", IPVLAN_MODE_L3S },
|
|
{ "l2", IPVLAN_MODE_L2 },
|
|
};
|
|
|
|
int lxc_ipvlan_mode_to_flag(int *mode, const char *value)
|
|
{
|
|
for (size_t i = 0; i < sizeof(ipvlan_mode) / sizeof(ipvlan_mode[0]); i++) {
|
|
if (strcmp(ipvlan_mode[i].name, value) != 0)
|
|
continue;
|
|
|
|
*mode = ipvlan_mode[i].mode;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
char *lxc_ipvlan_flag_to_mode(int mode)
|
|
{
|
|
for (size_t i = 0; i < sizeof(ipvlan_mode) / sizeof(ipvlan_mode[0]); i++) {
|
|
if (ipvlan_mode[i].mode != mode)
|
|
continue;
|
|
|
|
return ipvlan_mode[i].name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct lxc_ipvlan_isolation {
|
|
char *name;
|
|
int flag;
|
|
} ipvlan_isolation[] = {
|
|
{ "bridge", IPVLAN_ISOLATION_BRIDGE },
|
|
{ "private", IPVLAN_ISOLATION_PRIVATE },
|
|
{ "vepa", IPVLAN_ISOLATION_VEPA },
|
|
};
|
|
|
|
int lxc_ipvlan_isolation_to_flag(int *flag, const char *value)
|
|
{
|
|
for (size_t i = 0; i < sizeof(ipvlan_isolation) / sizeof(ipvlan_isolation[0]); i++) {
|
|
if (strcmp(ipvlan_isolation[i].name, value) != 0)
|
|
continue;
|
|
|
|
*flag = ipvlan_isolation[i].flag;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
char *lxc_ipvlan_flag_to_isolation(int flag)
|
|
{
|
|
for (size_t i = 0; i < sizeof(ipvlan_isolation) / sizeof(ipvlan_isolation[0]); i++) {
|
|
if (ipvlan_isolation[i].flag != flag)
|
|
continue;
|
|
|
|
return ipvlan_isolation[i].name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int set_config_string_item(char **conf_item, const char *value)
|
|
{
|
|
char *new_value;
|
|
|
|
if (lxc_config_value_empty(value)) {
|
|
free(*conf_item);
|
|
*conf_item = NULL;
|
|
return 0;
|
|
}
|
|
|
|
new_value = strdup(value);
|
|
if (!new_value) {
|
|
SYSERROR("Failed to duplicate string \"%s\"", value);
|
|
return -1;
|
|
}
|
|
|
|
free(*conf_item);
|
|
*conf_item = new_value;
|
|
return 0;
|
|
}
|
|
|
|
int set_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 set_config_string_item(conf_item, value);
|
|
}
|
|
|
|
int set_config_path_item(char **conf_item, const char *value)
|
|
{
|
|
return set_config_string_item_max(conf_item, value, PATH_MAX);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int network_ifname(char *valuep, const char *value, size_t size)
|
|
{
|
|
size_t retlen;
|
|
|
|
if (!valuep || !value)
|
|
return -1;
|
|
|
|
retlen = strlcpy(valuep, value, size);
|
|
if (retlen >= size)
|
|
ERROR("Network device name \"%s\" is too long (>= %zu)", value,
|
|
size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool lxc_config_net_is_hwaddr(const char *line)
|
|
{
|
|
unsigned index;
|
|
char tmp[7];
|
|
|
|
if (strncmp(line, "lxc.net", 7) != 0)
|
|
return false;
|
|
|
|
if (strncmp(line, "lxc.net.hwaddr", 14) == 0)
|
|
return true;
|
|
|
|
if (strncmp(line, "lxc.network.hwaddr", 18) == 0)
|
|
return true;
|
|
|
|
if (sscanf(line, "lxc.net.%u.%6s", &index, tmp) == 2 ||
|
|
sscanf(line, "lxc.network.%u.%6s", &index, tmp) == 2)
|
|
return strncmp(tmp, "hwaddr", 6) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
void rand_complete_hwaddr(char *hwaddr)
|
|
{
|
|
const char hex[] = "0123456789abcdef";
|
|
char *curs = hwaddr;
|
|
#ifdef HAVE_RAND_R
|
|
unsigned int seed;
|
|
|
|
seed = randseed(false);
|
|
#else
|
|
|
|
(void)randseed(true);
|
|
#endif
|
|
|
|
while (*curs != '\0' && *curs != '\n') {
|
|
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++;
|
|
}
|
|
}
|
|
|
|
bool new_hwaddr(char *hwaddr)
|
|
{
|
|
int ret;
|
|
#ifdef HAVE_RAND_R
|
|
unsigned int seed;
|
|
|
|
seed = randseed(false);
|
|
|
|
ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand_r(&seed) % 255,
|
|
rand_r(&seed) % 255, rand_r(&seed) % 255);
|
|
#else
|
|
|
|
(void)randseed(true);
|
|
|
|
ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
|
|
rand() % 255, rand() % 255);
|
|
#endif
|
|
if (ret < 0 || ret >= 18) {
|
|
SYSERROR("Failed to call snprintf()");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int lxc_get_conf_str(char *retv, int inlen, const char *value)
|
|
{
|
|
size_t value_len;
|
|
|
|
if (!value)
|
|
return 0;
|
|
|
|
value_len = strlen(value);
|
|
if (retv && inlen >= value_len + 1)
|
|
memcpy(retv, value, value_len + 1);
|
|
|
|
return value_len;
|
|
}
|
|
|
|
int lxc_get_conf_bool(struct lxc_conf *c, char *retv, int inlen, bool v)
|
|
{
|
|
int len;
|
|
int fulllen = 0;
|
|
|
|
if (!retv)
|
|
inlen = 0;
|
|
else
|
|
memset(retv, 0, inlen);
|
|
|
|
strprint(retv, inlen, "%d", v);
|
|
|
|
return fulllen;
|
|
}
|
|
|
|
int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
|
|
{
|
|
int len;
|
|
int fulllen = 0;
|
|
|
|
if (!retv)
|
|
inlen = 0;
|
|
else
|
|
memset(retv, 0, inlen);
|
|
|
|
strprint(retv, inlen, "%d", v);
|
|
|
|
return fulllen;
|
|
}
|
|
|
|
int lxc_get_conf_size_t(struct lxc_conf *c, char *retv, int inlen, size_t v)
|
|
{
|
|
int len;
|
|
int fulllen = 0;
|
|
|
|
if (!retv)
|
|
inlen = 0;
|
|
else
|
|
memset(retv, 0, inlen);
|
|
|
|
strprint(retv, inlen, "%zu", v);
|
|
|
|
return fulllen;
|
|
}
|
|
|
|
int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v)
|
|
{
|
|
int len;
|
|
int fulllen = 0;
|
|
|
|
if (!retv)
|
|
inlen = 0;
|
|
else
|
|
memset(retv, 0, inlen);
|
|
|
|
strprint(retv, inlen, "%"PRIu64, v);
|
|
|
|
return fulllen;
|
|
}
|
|
|
|
static int lxc_container_name_to_pid(const char *lxcname_or_pid,
|
|
const char *lxcpath)
|
|
{
|
|
int ret;
|
|
signed long int pid;
|
|
char *err = NULL;
|
|
|
|
pid = strtol(lxcname_or_pid, &err, 10);
|
|
if (*err != '\0' || pid < 1) {
|
|
struct lxc_container *c;
|
|
|
|
c = lxc_container_new(lxcname_or_pid, lxcpath);
|
|
if (!c) {
|
|
ERROR("\"%s\" is not a valid pid nor a container name",
|
|
lxcname_or_pid);
|
|
return -1;
|
|
}
|
|
|
|
if (!c->may_control(c)) {
|
|
ERROR("Insufficient privileges to control container "
|
|
"\"%s\"", c->name);
|
|
lxc_container_put(c);
|
|
return -1;
|
|
}
|
|
|
|
pid = c->init_pid(c);
|
|
if (pid < 1) {
|
|
ERROR("Container \"%s\" is not running", c->name);
|
|
lxc_container_put(c);
|
|
return -1;
|
|
}
|
|
|
|
lxc_container_put(c);
|
|
}
|
|
|
|
ret = kill(pid, 0);
|
|
if (ret < 0) {
|
|
SYSERROR("Failed to send signal to pid %d", (int)pid);
|
|
return -1;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
int lxc_inherit_namespace(const char *nsfd_path, const char *lxcpath,
|
|
const char *namespace)
|
|
{
|
|
int fd, pid;
|
|
char *dup, *lastslash;
|
|
|
|
if (nsfd_path[0] == '/') {
|
|
return open(nsfd_path, O_RDONLY | O_CLOEXEC);
|
|
}
|
|
|
|
lastslash = strrchr(nsfd_path, '/');
|
|
if (lastslash) {
|
|
dup = strdup(nsfd_path);
|
|
if (!dup)
|
|
return -1;
|
|
|
|
dup[lastslash - nsfd_path] = '\0';
|
|
pid = lxc_container_name_to_pid(lastslash + 1, dup);
|
|
free(dup);
|
|
} else {
|
|
pid = lxc_container_name_to_pid(nsfd_path, lxcpath);
|
|
}
|
|
|
|
if (pid < 0)
|
|
return -1;
|
|
|
|
fd = lxc_preserve_ns(pid, namespace);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
return fd;
|
|
}
|
|
|
|
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" },
|
|
#ifdef SIGTRAP
|
|
{ SIGTRAP, "TRAP" },
|
|
#endif
|
|
#ifdef SIGIOT
|
|
{ SIGIOT, "IOT" },
|
|
#endif
|
|
#ifdef SIGEMT
|
|
{ SIGEMT, "EMT" },
|
|
#endif
|
|
#ifdef SIGBUS
|
|
{ SIGBUS, "BUS" },
|
|
#endif
|
|
#ifdef SIGSTKFLT
|
|
{ SIGSTKFLT, "STKFLT" },
|
|
#endif
|
|
#ifdef SIGCLD
|
|
{ SIGCLD, "CLD" },
|
|
#endif
|
|
#ifdef SIGURG
|
|
{ SIGURG, "URG" },
|
|
#endif
|
|
#ifdef SIGXCPU
|
|
{ SIGXCPU, "XCPU" },
|
|
#endif
|
|
#ifdef SIGXFSZ
|
|
{ SIGXFSZ, "XFSZ" },
|
|
#endif
|
|
#ifdef SIGVTALRM
|
|
{ SIGVTALRM, "VTALRM" },
|
|
#endif
|
|
#ifdef SIGPROF
|
|
{ SIGPROF, "PROF" },
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
{ SIGWINCH, "WINCH" },
|
|
#endif
|
|
#ifdef SIGIO
|
|
{ SIGIO, "IO" },
|
|
#endif
|
|
#ifdef SIGPOLL
|
|
{ SIGPOLL, "POLL" },
|
|
#endif
|
|
#ifdef SIGINFO
|
|
{ SIGINFO, "INFO" },
|
|
#endif
|
|
#ifdef SIGLOST
|
|
{ SIGLOST, "LOST" },
|
|
#endif
|
|
#ifdef SIGPWR
|
|
{ SIGPWR, "PWR" },
|
|
#endif
|
|
#ifdef SIGUNUSED
|
|
{ SIGUNUSED, "UNUSED" },
|
|
#endif
|
|
#ifdef SIGSYS
|
|
{ SIGSYS, "SYS" },
|
|
#endif
|
|
};
|
|
|
|
static int sig_num(const char *sig)
|
|
{
|
|
unsigned int signum;
|
|
|
|
if (lxc_safe_uint(sig, &signum) < 0)
|
|
return -1;
|
|
|
|
return signum;
|
|
}
|
|
|
|
static int rt_sig_num(const char *signame)
|
|
{
|
|
int rtmax = 0, sig_n = 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;
|
|
}
|
|
|
|
int sig_parse(const char *signame)
|
|
{
|
|
size_t 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;
|
|
}
|