iSulad/src/pack_config.c
Grooooot 7be59a7c67 iSulad: sync openeuler at 3.6
Signed-off-by: Grooooot <isula@huawei.com>
2020-03-06 14:58:03 +08:00

2268 lines
65 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: tanyifeng
* Create: 2018-11-08
* Description: provide container package configure functions
******************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
#include "log.h"
#include "pack_config.h"
#include "host_config.h"
#include "utils.h"
#include "parse_common.h"
#include "path.h"
#include "container_config.h"
static bool parse_restart_policy(const char *policy, host_config_restart_policy **rp)
{
bool ret = false;
char *dup = NULL;
char *dotpos = NULL;
if (rp == NULL || policy == NULL) {
return true;
}
dup = util_strdup_s(policy);
*rp = util_common_calloc_s(sizeof(host_config_restart_policy));
if (*rp == NULL) {
ERROR("Restart policy: Out of memory");
goto cleanup;
}
dotpos = strchr(dup, ':');
if (dotpos != NULL) {
int nret;
*dotpos++ = '\0';
if (strchr(dotpos, ':') != NULL) {
COMMAND_ERROR("Invalid restart policy format");
goto cleanup;
}
nret = util_safe_int(dotpos, &(*rp)->maximum_retry_count);
if (nret != 0) {
COMMAND_ERROR("Maximum retry count must be an integer: %s", strerror(-nret));
goto cleanup;
}
}
(*rp)->name = util_strdup_s(dup);
ret = true;
cleanup:
free(dup);
return ret;
}
static int pack_host_config_ns_change_files(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (dstconfig == NULL || srcconfig == NULL) {
return -1;
}
if (srcconfig->ns_change_files_len != 0 && srcconfig->ns_change_files != NULL) {
if (srcconfig->ns_change_files_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many capabilities to add!");
ret = -1;
goto out;
}
dstconfig->ns_change_files = util_common_calloc_s(srcconfig->ns_change_files_len * sizeof(char *));
if (dstconfig->ns_change_files == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->ns_change_files_len; i++) {
dstconfig->ns_change_files[dstconfig->ns_change_files_len] = util_strdup_s(srcconfig->ns_change_files[i]);
dstconfig->ns_change_files_len++;
}
}
out:
return ret;
}
static int pack_host_config_cap_add(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* cap-add */
if (srcconfig->cap_add_len != 0 && srcconfig->cap_add != NULL) {
if (srcconfig->cap_add_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many capabilities to add!");
ret = -1;
goto out;
}
dstconfig->cap_add = util_common_calloc_s(srcconfig->cap_add_len * sizeof(char *));
if (dstconfig->cap_add == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->cap_add_len; i++) {
dstconfig->cap_add[dstconfig->cap_add_len] = util_strdup_s(srcconfig->cap_add[i]);
dstconfig->cap_add_len++;
}
}
for (i = 0; i < dstconfig->cap_add_len; i++) {
// skip `all`
if (strcasecmp(dstconfig->cap_add[i], "all") == 0) {
continue;
}
if (!util_valid_cap(dstconfig->cap_add[i])) {
COMMAND_ERROR("Unknown capability to add: '%s'", dstconfig->cap_add[i]);
ret = -1;
goto out;
}
}
out:
return ret;
}
static int pack_host_config_cap_drop(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* cap-drops */
if (srcconfig->cap_drop_len != 0 && srcconfig->cap_drop != NULL) {
if (srcconfig->cap_drop_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many capabilities to drop!");
ret = -1;
goto out;
}
dstconfig->cap_drop = util_common_calloc_s(srcconfig->cap_drop_len * sizeof(char *));
if (dstconfig->cap_drop == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->cap_drop_len; i++) {
dstconfig->cap_drop[dstconfig->cap_drop_len] = util_strdup_s(srcconfig->cap_drop[i]);
dstconfig->cap_drop_len++;
}
}
for (i = 0; i < dstconfig->cap_drop_len; i++) {
// skip `all`
if (strcasecmp(dstconfig->cap_drop[i], "all") == 0) {
continue;
}
if (!util_valid_cap(dstconfig->cap_drop[i])) {
COMMAND_ERROR("Unknown capability to drop: '%s'", dstconfig->cap_drop[i]);
ret = -1;
goto out;
}
}
out:
return ret;
}
static int pack_host_config_caps(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
ret = pack_host_config_cap_add(dstconfig, srcconfig);
if (ret != 0) {
ret = -1;
goto out;
}
ret = pack_host_config_cap_drop(dstconfig, srcconfig);
if (ret != 0) {
ret = -1;
goto out;
}
out:
return ret;
}
static int pack_host_network_extra_hosts(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* extra hosts */
if (srcconfig->extra_hosts_len != 0 && srcconfig->extra_hosts != NULL) {
if (srcconfig->extra_hosts_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many extra hosts to add!");
ret = -1;
goto out;
}
dstconfig->extra_hosts = util_common_calloc_s(srcconfig->extra_hosts_len * sizeof(char *));
if (dstconfig->extra_hosts == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->extra_hosts_len; i++) {
dstconfig->extra_hosts[dstconfig->extra_hosts_len] = util_strdup_s(srcconfig->extra_hosts[i]);
dstconfig->extra_hosts_len++;
}
}
out:
return ret;
}
static int pack_host_network_dns(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* dns */
if (srcconfig->dns_len != 0 && srcconfig->dns != NULL) {
if (srcconfig->dns_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many dns to add!");
ret = -1;
goto out;
}
dstconfig->dns = util_common_calloc_s(srcconfig->dns_len * sizeof(char *));
if (dstconfig->dns == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->dns_len; i++) {
dstconfig->dns[dstconfig->dns_len] = util_strdup_s(srcconfig->dns[i]);
dstconfig->dns_len++;
}
}
out:
return ret;
}
static int pack_host_network_dns_options(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* dns options */
if (srcconfig->dns_options_len != 0 && srcconfig->dns_options != NULL) {
if (srcconfig->dns_options_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many dns options to add!");
ret = -1;
goto out;
}
dstconfig->dns_options = util_common_calloc_s(srcconfig->dns_options_len * sizeof(char *));
if (dstconfig->dns_options == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->dns_options_len; i++) {
dstconfig->dns_options[dstconfig->dns_options_len] = util_strdup_s(srcconfig->dns_options[i]);
dstconfig->dns_options_len++;
}
}
out:
return ret;
}
static int pack_host_network_dns_search(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
/* dns search */
if (srcconfig->dns_search_len != 0 && srcconfig->dns_search != NULL) {
if (srcconfig->dns_search_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many dns search to add!");
ret = -1;
goto out;
}
dstconfig->dns_search = util_common_calloc_s(srcconfig->dns_search_len * sizeof(char *));
if (dstconfig->dns_search == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->dns_search_len; i++) {
dstconfig->dns_search[dstconfig->dns_search_len] = util_strdup_s(srcconfig->dns_search[i]);
dstconfig->dns_search_len++;
}
}
out:
return ret;
}
static int pack_host_config_network(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
if (dstconfig == NULL) {
return -1;
}
ret = pack_host_network_extra_hosts(dstconfig, srcconfig);
if (ret != 0) {
goto out;
}
ret = pack_host_network_dns(dstconfig, srcconfig);
if (ret != 0) {
goto out;
}
ret = pack_host_network_dns_options(dstconfig, srcconfig);
if (ret != 0) {
goto out;
}
ret = pack_host_network_dns_search(dstconfig, srcconfig);
if (ret != 0) {
goto out;
}
out:
return ret;
}
static int check_parsed_device(const host_config_devices_element *device_map)
{
int ret = 0;
if (device_map->path_on_host == NULL || device_map->path_in_container == NULL ||
device_map->cgroup_permissions == NULL) {
ret = -1;
goto out;
}
if (!util_file_exists(device_map->path_on_host)) {
COMMAND_ERROR(
"Error gathering device information while adding device \"%s\",stat %s:no such file or directory",
device_map->path_on_host, device_map->path_on_host);
ret = -1;
goto out;
}
out:
return ret;
}
static host_config_devices_element *parse_device(const char *devices)
{
char **tmp_str = NULL;
size_t tmp_str_len = 0;
host_config_devices_element *device_map = NULL;
if (devices == NULL || !strcmp(devices, "")) {
ERROR("devices can't be empty");
return NULL;
}
device_map = util_common_calloc_s(sizeof(host_config_devices_element));
if (device_map == NULL) {
ERROR("Out of memory");
return NULL;
}
tmp_str = util_string_split(devices, ':');
tmp_str_len = util_array_len((const char **)tmp_str);
switch (tmp_str_len) {
case 3:
device_map->path_on_host = util_strdup_s(tmp_str[0]);
device_map->path_in_container = util_strdup_s(tmp_str[1]);
if (util_valid_device_mode(tmp_str[2])) {
device_map->cgroup_permissions = util_strdup_s(tmp_str[2]);
}
break;
case 2:
device_map->path_on_host = util_strdup_s(tmp_str[0]);
if (util_valid_device_mode(tmp_str[1])) {
device_map->path_in_container = util_strdup_s(tmp_str[0]);
device_map->cgroup_permissions = util_strdup_s(tmp_str[1]);
} else {
device_map->path_in_container = util_strdup_s(tmp_str[1]);
device_map->cgroup_permissions = util_strdup_s("rwm");
}
break;
case 1:
device_map->path_on_host = util_strdup_s(tmp_str[0]);
device_map->path_in_container = util_strdup_s(tmp_str[0]);
device_map->cgroup_permissions = util_strdup_s("rwm");
break;
default:
ERROR("Invalid parament %s", devices);
break;
}
util_free_array(tmp_str);
if (check_parsed_device(device_map) != 0) {
goto erro_out;
}
return device_map;
erro_out:
free_host_config_devices_element(device_map);
return NULL;
}
static int check_ulimit_input(const char *val)
{
int ret = 0;
if (val == NULL || strcmp(val, "") == 0) {
COMMAND_ERROR("ulimit argument can't be empty");
ret = -1;
goto out;
}
if (val[0] == '=' || val[strlen(val) - 1] == '=') {
COMMAND_ERROR("Invalid ulimit argument: \"%s\", delimiter '=' can't"
" be the first or the last character", val);
ret = -1;
}
out:
return ret;
}
static void get_ulimit_split_parts(const char *val, char ***parts, size_t *parts_len, char deli)
{
*parts = util_string_split_multi(val, deli);
if (*parts == NULL) {
COMMAND_ERROR("Out of memory");
return;
}
*parts_len = util_array_len((const char **)(*parts));
}
static int parse_soft_hard_ulimit(const char *val, char **limitvals, size_t limitvals_len, int64_t *soft, int64_t *hard)
{
int ret = 0;
// parse soft
ret = util_safe_llong(limitvals[0], (long long *)soft);
if (ret < 0) {
COMMAND_ERROR("Invalid ulimit soft value: \"%s\", parse int64 failed: %s", val, strerror(-ret));
ret = -1;
goto out;
}
// parse hard if exists
if (limitvals_len > 1) {
ret = util_safe_llong(limitvals[1], (long long *)hard);
if (ret < 0) {
COMMAND_ERROR("Invalid ulimit hard value: \"%s\", parse int64 failed: %s", val, strerror(-ret));
ret = -1;
goto out;
}
if (*soft > *hard) {
COMMAND_ERROR("Ulimit soft limit must be less than or equal to hard limit: %lld > %lld",
(long long int)(*soft), (long long int)(*hard));
ret = -1;
goto out;
}
} else {
*hard = *soft; // default to soft in case no hard was set
}
out:
return ret;
}
static int check_ulimit_type(const char *type)
{
int ret = 0;
char **tmptype = NULL;
char *ulimit_valid_type[] = {
// "as", // Disabled since this doesn't seem usable with the way Docker inits a container.
"core", "cpu", "data", "fsize", "locks", "memlock", "msgqueue", "nice",
"nofile", "nproc", "rss", "rtprio", "rttime", "sigpending", "stack", NULL
};
for (tmptype = ulimit_valid_type; *tmptype != NULL; tmptype++) {
if (strcmp(type, *tmptype) == 0) {
break;
}
}
if (*tmptype == NULL) {
COMMAND_ERROR("Invalid ulimit type: %s", type);
ret = -1;
}
return ret;
}
static host_config_ulimits_element *parse_ulimit(const char *val)
{
int ret = 0;
int64_t soft = 0;
int64_t hard = 0;
size_t parts_len = 0;
size_t limitvals_len = 0;
char **parts = NULL;
char **limitvals = NULL;
host_config_ulimits_element *ulimit = NULL;
ret = check_ulimit_input(val);
if (ret != 0) {
return NULL;
}
get_ulimit_split_parts(val, &parts, &parts_len, '=');
if (parts == NULL) {
ERROR("Out of memory");
return NULL;
} else if (parts_len != 2) {
COMMAND_ERROR("Invalid ulimit argument: %s", val);
ret = -1;
goto out;
}
ret = check_ulimit_type(parts[0]);
if (ret != 0) {
ret = -1;
goto out;
}
if (parts[1][0] == ':' || parts[1][strlen(parts[1]) - 1] == ':') {
COMMAND_ERROR("Invalid ulimit value: \"%s\", delimiter ':' can't be the first"
" or the last character", val);
ret = -1;
goto out;
}
// parse value
get_ulimit_split_parts(parts[1], &limitvals, &limitvals_len, ':');
if (limitvals == NULL) {
ret = -1;
goto out;
}
if (limitvals_len > 2) {
COMMAND_ERROR("Too many limit value arguments - %s, can only have up to two, `soft[:hard]`",
parts[1]);
ret = -1;
goto out;
}
ret = parse_soft_hard_ulimit(val, limitvals, limitvals_len, &soft, &hard);
if (ret < 0) {
goto out;
}
ulimit = util_common_calloc_s(sizeof(host_config_ulimits_element));
if (ulimit == NULL) {
ret = -1;
goto out;
}
ulimit->name = util_strdup_s(parts[0]);
ulimit->hard = hard;
ulimit->soft = soft;
out:
util_free_array(parts);
util_free_array(limitvals);
if (ret != 0) {
free_host_config_ulimits_element(ulimit);
ulimit = NULL;
}
return ulimit;
}
static void pack_cgroup_resources_cpu(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
/* cgroup blkio weight */
if (srcconfig->cr->blkio_weight) {
dstconfig->blkio_weight = srcconfig->cr->blkio_weight;
}
/* cpu shares */
if (srcconfig->cr->cpu_shares) {
dstconfig->cpu_shares = srcconfig->cr->cpu_shares;
}
/* cpu period */
if (srcconfig->cr->cpu_period) {
dstconfig->cpu_period = srcconfig->cr->cpu_period;
}
/* cpu quota */
if (srcconfig->cr->cpu_quota) {
dstconfig->cpu_quota = srcconfig->cr->cpu_quota;
}
/* cpuset-cpus */
if (util_valid_str(srcconfig->cr->cpuset_cpus)) {
dstconfig->cpuset_cpus = util_strdup_s(srcconfig->cr->cpuset_cpus);
}
/* cpuset mems */
if (util_valid_str(srcconfig->cr->cpuset_mems)) {
dstconfig->cpuset_mems = util_strdup_s(srcconfig->cr->cpuset_mems);
}
if (srcconfig->cr->cpu_realtime_period) {
dstconfig->cpu_realtime_period = srcconfig->cr->cpu_realtime_period;
}
if (srcconfig->cr->cpu_realtime_runtime) {
dstconfig->cpu_realtime_runtime = srcconfig->cr->cpu_realtime_runtime;
}
}
static void pack_cgroup_resources_mem(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
/* memory limit */
if (srcconfig->cr->memory) {
dstconfig->memory = srcconfig->cr->memory;
}
/* memory swap */
if (srcconfig->cr->memory_swap) {
dstconfig->memory_swap = srcconfig->cr->memory_swap;
}
/* memory reservation */
if (srcconfig->cr->memory_reservation) {
dstconfig->memory_reservation = srcconfig->cr->memory_reservation;
}
/* kernel memory limit */
if (srcconfig->cr->kernel_memory) {
dstconfig->kernel_memory = srcconfig->cr->kernel_memory;
}
// swappiness
if (srcconfig->cr->swappiness != -1) {
dstconfig->memory_swappiness = util_common_calloc_s(sizeof(uint64_t));
if (dstconfig->memory_swappiness != NULL) {
*(dstconfig->memory_swappiness) = (uint64_t)(srcconfig->cr->swappiness);
}
}
}
static void pack_cgroup_resources(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
pack_cgroup_resources_cpu(dstconfig, srcconfig);
pack_cgroup_resources_mem(dstconfig, srcconfig);
/* cgroup limit */
dstconfig->pids_limit = srcconfig->cr->pids_limit;
dstconfig->files_limit = srcconfig->cr->files_limit;
/* oom score adj */
dstconfig->oom_score_adj = (int)srcconfig->cr->oom_score_adj;
}
static int pack_hostconfig_ulimits(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i;
if (srcconfig->ulimits == NULL || srcconfig->ulimits_len == 0) {
goto out;
}
if (srcconfig->ulimits_len > SIZE_MAX / sizeof(host_config_ulimits_element *)) {
COMMAND_ERROR("Too many ulimit elements in host config");
ret = -1;
goto out;
}
dstconfig->ulimits = util_common_calloc_s(srcconfig->ulimits_len * sizeof(host_config_ulimits_element *));
if (dstconfig->ulimits == NULL) {
COMMAND_ERROR("Out of memory");
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->ulimits_len; i++) {
size_t j;
bool exists = false;
host_config_ulimits_element *tmp = NULL;
tmp = parse_ulimit(srcconfig->ulimits[i]);
if (tmp == NULL) {
ret = -1;
goto out;
}
for (j = 0; j < dstconfig->ulimits_len; j++) {
if (strcmp(dstconfig->ulimits[j]->name, tmp->name) == 0) {
exists = true;
break;
}
}
if (exists) {
free_host_config_ulimits_element(dstconfig->ulimits[j]);
dstconfig->ulimits[j] = tmp;
} else {
dstconfig->ulimits[dstconfig->ulimits_len] = tmp;
dstconfig->ulimits_len++;
}
}
out:
return ret;
}
static int pack_hostconfig_cgroup(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
if (srcconfig->cr != NULL) {
pack_cgroup_resources(dstconfig, srcconfig);
}
ret = pack_hostconfig_ulimits(dstconfig, srcconfig);
return ret;
}
static host_config_blkio_weight_device_element *pack_blkio_weight_devices(const char *devices)
{
char **tmp_str = NULL;
unsigned int weight = 0;
size_t tmp_str_len = 0;
host_config_blkio_weight_device_element *weight_dev = NULL;
if (devices == NULL || !strcmp(devices, "")) {
COMMAND_ERROR("Weight devices can't be empty");
return NULL;
}
weight_dev = util_common_calloc_s(sizeof(host_config_blkio_weight_device_element));
if (weight_dev == NULL) {
ERROR("Out of memory");
return NULL;
}
tmp_str = util_string_split(devices, ':');
if (tmp_str == NULL) {
COMMAND_ERROR("String split failed");
goto erro_out;
}
tmp_str_len = util_array_len((const char **)tmp_str);
if (tmp_str_len != 2) {
COMMAND_ERROR("Bad blkio weight device format: %s", devices);
goto erro_out;
}
if (strncmp("/dev/", tmp_str[0], strlen("/dev/")) != 0) {
COMMAND_ERROR("Bad format for device path: %s", devices);
goto erro_out;
}
if (util_safe_uint(tmp_str[1], &weight)) {
COMMAND_ERROR("Invalid weight for device: %s", devices);
goto erro_out;
}
if (weight > 0 && (weight < 10 || weight > 1000)) {
COMMAND_ERROR("Invalid weight for device: %s", devices);
goto erro_out;
}
weight_dev->path = util_strdup_s(tmp_str[0]);
weight_dev->weight = (uint16_t)weight;
util_free_array(tmp_str);
return weight_dev;
erro_out:
util_free_array(tmp_str);
free_host_config_blkio_weight_device_element(weight_dev);
return NULL;
}
static int parse_blkio_throttle_bps_device(const char *device, char **path, const uint64_t *rate)
{
int ret = 0;
char **split = NULL;
split = util_string_split_multi(device, ':');
if (split == NULL || util_array_len((const char **)split) != 2) {
COMMAND_ERROR("bad format: %s", device);
ret = -1;
goto out;
}
if (strncmp(split[0], "/dev/", strlen("/dev/")) != 0) {
COMMAND_ERROR("bad format for device path: %s", device);
ret = -1;
goto out;
}
if (util_parse_byte_size_string(split[1], (int64_t *)rate) != 0) {
COMMAND_ERROR("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]."
" Number must be a positive integer. Unit is optional and can be kb, mb, or gb", device);
ret = -1;
goto out;
}
*path = util_strdup_s(split[0]);
out:
util_free_array(split);
return ret;
}
// validate that the specified string has a valid device-rate format.
static host_config_blkio_device_read_bps_element *pack_throttle_read_bps_device(const char *device)
{
char *path = NULL;
uint64_t rate = 0;
host_config_blkio_device_read_bps_element *read_bps_dev = NULL;
if (device == NULL || !strcmp(device, "")) {
COMMAND_ERROR("blkio throttle read bps device can't be empty");
return NULL;
}
read_bps_dev = util_common_calloc_s(sizeof(host_config_blkio_device_read_bps_element));
if (read_bps_dev == NULL) {
ERROR("Out of memory");
return NULL;
}
if (parse_blkio_throttle_bps_device(device, &path, &rate) != 0) {
goto error_out;
}
read_bps_dev->path = path;
read_bps_dev->rate = rate;
return read_bps_dev;
error_out:
free(path);
free_host_config_blkio_device_read_bps_element(read_bps_dev);
return NULL;
}
// validate that the specified string has a valid device-rate format.
static host_config_blkio_device_write_bps_element *pack_throttle_write_bps_device(const char *device)
{
char *path = NULL;
uint64_t rate = 0;
host_config_blkio_device_write_bps_element *write_bps_dev = NULL;
if (device == NULL || !strcmp(device, "")) {
COMMAND_ERROR("blkio throttle write bps device can't be empty");
return NULL;
}
write_bps_dev = util_common_calloc_s(sizeof(host_config_blkio_device_write_bps_element));
if (write_bps_dev == NULL) {
ERROR("Out of memory");
return NULL;
}
if (parse_blkio_throttle_bps_device(device, &path, &rate) != 0) {
goto error_out;
}
write_bps_dev->path = path;
write_bps_dev->rate = rate;
return write_bps_dev;
error_out:
free(path);
free_host_config_blkio_device_write_bps_element(write_bps_dev);
return NULL;
}
static int split_hugetlb_limit(char *temp, char **pagesize, char **limit_value)
{
int ret = 0;
char *saveptr = NULL;
if (strchr(temp, ':') == NULL) {
*limit_value = temp;
goto out;
} else if (temp[0] != ':') {
*pagesize = strtok_r(temp, ":", &saveptr);
if ((*pagesize) == NULL) {
ret = -1;
goto out;
}
*limit_value = strtok_r(NULL, ":", &saveptr);
if ((*limit_value) == NULL) {
ret = -1;
goto out;
}
} else {
*limit_value = strtok_r(temp, ":", &saveptr);
if ((*limit_value) == NULL) {
ret = -1;
goto out;
}
}
out:
return ret;
}
static host_config_hugetlbs_element *pase_hugetlb_limit(const char *input)
{
int ret;
char *temp = NULL;
char *pagesize = NULL;
char *limit_value = NULL;
char *trans_page = NULL;
uint64_t limit = 0;
uint64_t page = 0;
host_config_hugetlbs_element *limit_element = NULL;
temp = util_strdup_s(input);
if (temp == NULL) {
ERROR("Out of memory");
goto free_out;
}
ret = split_hugetlb_limit(temp, &pagesize, &limit_value);
if (ret != 0) {
goto free_out;
}
ret = util_parse_byte_size_string(limit_value, (int64_t *)(&limit));
if (ret != 0) {
COMMAND_ERROR("Parse limit value: %s failed:%s", limit_value, strerror(-ret));
goto free_out;
}
if (pagesize != NULL) {
ret = util_parse_byte_size_string(pagesize, (int64_t *)(&page));
if (ret != 0) {
COMMAND_ERROR("Parse pagesize error.Invalid hugepage size: %s: %s", pagesize, strerror(-ret));
goto free_out;
}
trans_page = util_human_size(page);
if (trans_page == NULL) {
COMMAND_ERROR("Failed to translate page size");
goto free_out;
}
}
limit_element = util_common_calloc_s(sizeof(host_config_hugetlbs_element));
if (limit_element == NULL) {
ERROR("Out of memory");
goto free_out;
}
limit_element->limit = limit;
limit_element->page_size = trans_page ? util_strdup_s(trans_page) : util_strdup_s("");
free_out:
free(temp);
free(trans_page);
return limit_element;
}
uint64_t get_proc_mem_size(const char *item)
{
uint64_t sysmem_limit = 0;
FILE *fp = NULL;
size_t len = 0;
char *line = NULL;
char *p = NULL;
fp = util_fopen("/proc/meminfo", "r");
if (fp == NULL) {
ERROR("Failed to open /proc/meminfo: %s", strerror(errno));
return sysmem_limit;
}
while (getline(&line, &len, fp) != -1) {
p = strchr(line, ' ');
if (p == NULL) {
goto out;
}
*p = '\0';
p++;
if (strcmp(line, item) == 0) {
while (*p == ' ' || *p == '\t') {
p++;
}
if (*p == '\0') {
goto out;
}
sysmem_limit = strtoull(p, NULL, 0);
break;
}
}
out:
fclose(fp);
free(line);
return sysmem_limit * SIZE_KB;
}
static bool parse_host_path(const char *input, const char *token, host_config_host_channel *host_channel)
{
char real_path[PATH_MAX] = { 0 };
if (strcmp(token, "") == 0) {
COMMAND_ERROR("Bad host channel format: %s", input);
return false;
}
if (token[0] != '/') {
COMMAND_ERROR("Host channel host path should be absolute: %s", token);
return false;
}
if (cleanpath(token, real_path, sizeof(real_path)) == NULL) {
ERROR("Failed to clean path: '%s'", token);
return false;
}
if (util_dir_exists(real_path)) {
COMMAND_ERROR("Host path '%s' already exists", real_path);
return false;
}
host_channel->path_on_host = util_strdup_s(real_path);
return true;
}
static bool parse_container_path(const char *input, const char *token, host_config_host_channel *host_channel)
{
char real_path[PATH_MAX] = { 0 };
if (strcmp(token, "") == 0) {
COMMAND_ERROR("Bad host channel format: %s", input);
return false;
}
if (token[0] != '/') {
COMMAND_ERROR("Host channel container path should be absolute: %s", token);
return false;
}
if (cleanpath(token, real_path, sizeof(real_path)) == NULL) {
ERROR("Failed to clean path: '%s'", token);
return false;
}
host_channel->path_in_container = util_strdup_s(real_path);
return true;
}
static bool parse_mode(const char *input, const char *token, host_config_host_channel *host_channel)
{
if (strcmp(token, "") == 0) {
host_channel->permissions = util_strdup_s("rw");
return true;
}
if (!util_valid_mount_mode(token)) {
COMMAND_ERROR("Invalid mount mode for host channel: %s", input);
return false;
}
host_channel->permissions = util_strdup_s(token);
return true;
}
static bool parse_size(const char *input, const char *token, host_config_host_channel *host_channel)
{
uint64_t size = 0;
uint64_t mem_total_size = 0;
uint64_t mem_available_size = 0;
if (strcmp(token, "") == 0) {
host_channel->size = 64 * SIZE_MB;
return true;
}
if (util_parse_byte_size_string(token, (int64_t *)(&size))) {
COMMAND_ERROR("Invalid size limit for host channel: %s", input);
return false;
}
if (size < HOST_CHANNLE_MIN_SIZE) {
COMMAND_ERROR("Invalid size, larger than 4KB is allowed");
return false;
}
mem_total_size = get_proc_mem_size("MemTotal:");
if (size > mem_total_size / 2) {
COMMAND_ERROR("Required host channel size %llu is larger than half of total memory %llu",
(unsigned long long)size, (unsigned long long)mem_total_size);
return false;
}
mem_available_size = get_proc_mem_size("MemAvailable:");
if (size > mem_available_size) {
COMMAND_ERROR("Required host channel size %llu is larger than available memory %llu", (unsigned long long)size,
(unsigned long long)mem_available_size);
return false;
}
host_channel->size = size;
return true;
}
host_config_host_channel *parse_host_channel(const char *input)
{
int count = 0;
char *tmp_str = NULL;
char *save_ptr = NULL;
char *token = NULL;
host_config_host_channel *host_channel = NULL;
host_channel = util_common_calloc_s(sizeof(host_config_host_channel));
if (host_channel == NULL) {
ERROR("Out of memory");
return NULL;
}
tmp_str = util_strdup_s(input);
save_ptr = tmp_str;
token = util_str_token(&tmp_str, ":");
while (token != NULL) {
count++;
if (count > 4) {
COMMAND_ERROR("Bad host channel format: %s", input);
goto erro_out;
}
switch (count) {
case 1:
if (!parse_host_path(input, token, host_channel)) {
goto erro_out;
}
break;
case 2:
if (!parse_container_path(input, token, host_channel)) {
goto erro_out;
}
break;
case 3:
if (!parse_mode(input, token, host_channel)) {
goto erro_out;
}
break;
case 4:
if (!parse_size(input, token, host_channel)) {
goto erro_out;
}
break;
default:
break;
}
free(token);
token = util_str_token(&tmp_str, ":");
}
if (count < 4) {
COMMAND_ERROR("Bad host channel format: %s", input);
goto erro_out;
}
free(save_ptr);
return host_channel;
erro_out:
free(token);
free(save_ptr);
free_host_config_host_channel(host_channel);
return NULL;
}
static int append_no_new_privileges_to_security_opts(host_config *dstconfig)
{
int ret = 0;
size_t new_size, old_size;
char **tmp_security_opt = NULL;
if (dstconfig->security_opt_len > (SIZE_MAX / sizeof(char *)) - 1) {
COMMAND_ERROR("Out of memory");
return -1;
}
new_size = (dstconfig->security_opt_len + 1) * sizeof(char *);
old_size = dstconfig->security_opt_len * sizeof(char *);
ret = mem_realloc((void **)(&tmp_security_opt), new_size, (void *)dstconfig->security_opt, old_size);
if (ret != 0) {
COMMAND_ERROR("Out of memory");
return ret;
}
dstconfig->security_opt = tmp_security_opt;
dstconfig->security_opt[dstconfig->security_opt_len++] = util_strdup_s("no-new-privileges");
return ret;
}
static int append_seccomp_to_security_opts(const char *full_opt, const char *seccomp_file, host_config *dstconfig)
{
int ret = 0;
int nret = 0;
size_t size = 0;
char *seccomp_json = NULL;
char *tmp_str = NULL;
docker_seccomp *seccomp_spec = NULL;
parser_error err = NULL;
struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 };
if (strcmp(seccomp_file, "unconfined") == 0) {
dstconfig->security_opt[dstconfig->security_opt_len] = util_strdup_s(full_opt);
dstconfig->security_opt_len++;
return 0;
}
seccomp_spec = get_seccomp_security_opt_spec(seccomp_file);
if (seccomp_spec == NULL) {
ERROR("Failed to parse docker format seccomp specification file \"%s\", error message: %s",
seccomp_file, err);
COMMAND_ERROR("failed to parse seccomp file: %s", seccomp_file);
ret = -1;
goto out;
}
seccomp_json = docker_seccomp_generate_json(seccomp_spec, &ctx, &err);
if (seccomp_json == NULL) {
COMMAND_ERROR("failed to generate seccomp json!");
ret = -1;
goto out;
}
if (strlen(seccomp_json) > (SIZE_MAX - strlen("seccomp=")) - 1) {
COMMAND_ERROR("seccomp json is too big!");
ret = -1;
goto out;
}
size = strlen("seccomp=") + strlen(seccomp_json) + 1;
tmp_str = util_common_calloc_s(size);
if (tmp_str == NULL) {
COMMAND_ERROR("out of memory");
ret = -1;
goto out;
}
nret = snprintf(tmp_str, size, "seccomp=%s", seccomp_json);
if (nret < 0 || nret >= size) {
COMMAND_ERROR("failed to sprintf buffer!");
ret = -1;
goto out;
}
dstconfig->security_opt[dstconfig->security_opt_len] = util_strdup_s(tmp_str);
dstconfig->security_opt_len++;
out:
free(seccomp_json);
free(tmp_str);
free_docker_seccomp(seccomp_spec);
free(err);
return ret;
}
static int append_selinux_label_to_security_opts(const char *selinux_label, host_config *dstconfig)
{
int ret = 0;
size_t new_size;
size_t old_size;
char **tmp_security_opt = NULL;
if (dstconfig->security_opt_len > (SIZE_MAX / sizeof(char *)) - 1) {
COMMAND_ERROR("Too large security options");
return -1;
}
new_size = (dstconfig->security_opt_len + 1) * sizeof(char *);
old_size = dstconfig->security_opt_len * sizeof(char *);
ret = mem_realloc((void **)(&tmp_security_opt), new_size, (void *)dstconfig->security_opt, old_size);
if (ret != 0) {
COMMAND_ERROR("Out of memory");
return ret;
}
dstconfig->security_opt = tmp_security_opt;
dstconfig->security_opt[dstconfig->security_opt_len++] = util_strdup_s(selinux_label);
return ret;
}
static int parse_security_opts(const isula_host_config_t *srcconfig, host_config *dstconfig)
{
int ret = 0;
size_t i;
char **items = NULL;
for (i = 0; i < srcconfig->security_len; i++) {
items = util_string_split_n(srcconfig->security[i], '=', 2);
if (util_array_len((const char **)items) == 1) {
if (strcmp(items[0], "no-new-privileges") != 0) {
ret = -1;
} else {
ret = append_no_new_privileges_to_security_opts(dstconfig);
}
} else {
if (strcmp(items[0], "seccomp") == 0) {
ret = append_seccomp_to_security_opts(srcconfig->security[i], items[1], dstconfig);
} else if (strcmp(items[0], "label") == 0) {
ret = append_selinux_label_to_security_opts(srcconfig->security[i], dstconfig);
} else {
ret = -1;
}
}
if (ret != 0) {
COMMAND_ERROR("Invalid --security-opt: %s", srcconfig->security[i]);
ret = -1;
goto out;
}
util_free_array(items);
items = NULL;
}
out:
util_free_array(items);
return ret;
}
int generate_storage_opts(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t j;
if (srcconfig->storage_opts == NULL || dstconfig == NULL) {
goto out;
}
(*dstconfig)->storage_opt = util_common_calloc_s(sizeof(json_map_string_string));
if ((*dstconfig)->storage_opt == NULL) {
ret = -1;
goto out;
}
for (j = 0; j < srcconfig->storage_opts->len; j++) {
ret = append_json_map_string_string((*dstconfig)->storage_opt,
srcconfig->storage_opts->keys[j],
srcconfig->storage_opts->values[j]);
if (ret != 0) {
ERROR("Append map failed");
ret = -1;
goto out;
}
}
out:
return ret;
}
static int generate_sysctls(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t j;
if (srcconfig->sysctls == NULL || dstconfig == NULL) {
goto out;
}
(*dstconfig)->sysctls = util_common_calloc_s(sizeof(json_map_string_string));
if ((*dstconfig)->sysctls == NULL) {
ret = -1;
goto out;
}
for (j = 0; j < srcconfig->sysctls->len; j++) {
ret = append_json_map_string_string((*dstconfig)->sysctls, srcconfig->sysctls->keys[j],
srcconfig->sysctls->values[j]);
if (ret < 0) {
goto out;
}
}
out:
return ret;
}
int generate_devices(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (srcconfig->devices == NULL || srcconfig->devices_len == 0) {
goto out;
}
if (srcconfig->devices_len > SIZE_MAX / sizeof(host_config_devices_element *)) {
ERROR("Too many devices to be populated into container");
ret = -1;
goto out;
}
(*dstconfig)->devices = util_common_calloc_s(sizeof(host_config_devices_element *) * srcconfig->devices_len);
if ((*dstconfig)->devices == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->devices_len; i++) {
(*dstconfig)->devices[i] = parse_device(srcconfig->devices[i]);
if ((*dstconfig)->devices[i] == NULL) {
ERROR("Failed to parse devices:%s", srcconfig->devices[i]);
ret = -1;
goto out;
}
(*dstconfig)->devices_len++;
}
out:
return ret;
}
static int generate_blkio_weight_device(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (srcconfig->blkio_weight_device == NULL || srcconfig->blkio_weight_device_len == 0) {
goto out;
}
if (srcconfig->blkio_weight_device_len > SIZE_MAX / sizeof(host_config_blkio_weight_device_element *)) {
ERROR("Too many blkio weight devies to get!");
ret = -1;
goto out;
}
(*dstconfig)->blkio_weight_device =
util_common_calloc_s(srcconfig->blkio_weight_device_len * sizeof(host_config_blkio_weight_device_element *));
if ((*dstconfig)->blkio_weight_device == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->blkio_weight_device_len; i++) {
(*dstconfig)->blkio_weight_device[(*dstconfig)->blkio_weight_device_len] =
pack_blkio_weight_devices(srcconfig->blkio_weight_device[i]);
if ((*dstconfig)->blkio_weight_device[(*dstconfig)->blkio_weight_device_len] == NULL) {
ERROR("Failed to get blkio weight devies");
ret = -1;
goto out;
}
(*dstconfig)->blkio_weight_device_len++;
}
out:
return ret;
}
static int generate_blkio_throttle_read_bps_device(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (dstconfig == NULL || *dstconfig == NULL) {
goto out;
}
if (srcconfig->blkio_throttle_read_bps_device == NULL || srcconfig->blkio_throttle_read_bps_device_len == 0) {
goto out;
}
if (srcconfig->blkio_throttle_read_bps_device_len >
SIZE_MAX / sizeof(host_config_blkio_device_read_bps_element *)) {
ERROR("Too many blkio throttle read bps devies to get!");
ret = -1;
goto out;
}
(*dstconfig)->blkio_device_read_bps =
util_common_calloc_s(srcconfig->blkio_throttle_read_bps_device_len *
sizeof(host_config_blkio_device_read_bps_element *));
if ((*dstconfig)->blkio_device_read_bps == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->blkio_throttle_read_bps_device_len; i++) {
(*dstconfig)->blkio_device_read_bps[(*dstconfig)->blkio_device_read_bps_len] =
pack_throttle_read_bps_device(srcconfig->blkio_throttle_read_bps_device[i]);
if ((*dstconfig)->blkio_device_read_bps[(*dstconfig)->blkio_device_read_bps_len] == NULL) {
ERROR("Failed to get blkio throttle read bps devices");
ret = -1;
goto out;
}
(*dstconfig)->blkio_device_read_bps_len++;
}
out:
return ret;
}
static int generate_blkio_throttle_write_bps_device(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (dstconfig == NULL || *dstconfig == NULL) {
goto out;
}
if (srcconfig->blkio_throttle_write_bps_device == NULL || srcconfig->blkio_throttle_write_bps_device_len == 0) {
goto out;
}
if (srcconfig->blkio_throttle_write_bps_device_len >
SIZE_MAX / sizeof(host_config_blkio_device_write_bps_element *)) {
ERROR("Too many blkio throttle write bps devies to get!");
ret = -1;
goto out;
}
(*dstconfig)->blkio_device_write_bps = util_common_calloc_s(srcconfig->blkio_throttle_write_bps_device_len *
sizeof(host_config_blkio_device_write_bps_element *));
if ((*dstconfig)->blkio_device_write_bps == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->blkio_throttle_write_bps_device_len; i++) {
(*dstconfig)->blkio_device_write_bps[(*dstconfig)->blkio_device_write_bps_len] =
pack_throttle_write_bps_device(srcconfig->blkio_throttle_write_bps_device[i]);
if ((*dstconfig)->blkio_device_write_bps[(*dstconfig)->blkio_device_write_bps_len] == NULL) {
ERROR("Failed to get blkio throttle write bps devices");
ret = -1;
goto out;
}
(*dstconfig)->blkio_device_write_bps_len++;
}
out:
return ret;
}
static int generate_blkio(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret;
/* blkio weight devies */
ret = generate_blkio_weight_device(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* blkio throttle read bps devies */
ret = generate_blkio_throttle_read_bps_device(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* blkio throttle write bps devies */
ret = generate_blkio_throttle_write_bps_device(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
out:
return ret;
}
int generate_hugetlb_limits(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (srcconfig->hugetlbs_len == 0 || srcconfig->hugetlbs == NULL) {
goto out;
}
if (srcconfig->hugetlbs_len > SIZE_MAX / sizeof(host_config_hugetlbs_element *)) {
ERROR("Too many hugepage limits to get!");
ret = -1;
goto out;
}
(*dstconfig)->hugetlbs = util_common_calloc_s(srcconfig->hugetlbs_len * sizeof(host_config_hugetlbs_element *));
if ((*dstconfig)->hugetlbs == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->hugetlbs_len; i++) {
(*dstconfig)->hugetlbs[(*dstconfig)->hugetlbs_len] = pase_hugetlb_limit(srcconfig->hugetlbs[i]);
if ((*dstconfig)->hugetlbs[(*dstconfig)->hugetlbs_len] == NULL) {
ERROR("Failed to get hugepage limits");
ret = -1;
goto out;
}
(*dstconfig)->hugetlbs_len++;
}
out:
return ret;
}
int generate_binds(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (srcconfig->binds == NULL || srcconfig->binds_len == 0) {
goto out;
}
if (srcconfig->binds_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many binds to mount!");
ret = -1;
goto out;
}
(*dstconfig)->binds = util_common_calloc_s(srcconfig->binds_len * sizeof(char *));
if ((*dstconfig)->binds == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->binds_len; i++) {
(*dstconfig)->binds[(*dstconfig)->binds_len] = util_strdup_s(srcconfig->binds[i]);
(*dstconfig)->binds_len++;
}
out:
return ret;
}
int generate_groups(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
size_t i = 0;
if (srcconfig->group_add == NULL || srcconfig->group_add_len == 0 || dstconfig == NULL) {
goto out;
}
if (srcconfig->group_add_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many groups to add!");
ret = -1;
goto out;
}
(*dstconfig)->group_add = util_common_calloc_s(srcconfig->group_add_len * sizeof(char *));
if ((*dstconfig)->group_add == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < srcconfig->group_add_len; i++) {
(*dstconfig)->group_add[(*dstconfig)->group_add_len] = util_strdup_s(srcconfig->group_add[i]);
(*dstconfig)->group_add_len++;
}
out:
return ret;
}
int generate_security(host_config **dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
if (srcconfig->security == NULL || srcconfig->security_len == 0) {
goto out;
}
if (srcconfig->security_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many security opts!");
ret = -1;
goto out;
}
(*dstconfig)->security_opt = util_common_calloc_s(srcconfig->security_len * sizeof(char *));
if ((*dstconfig)->security_opt == NULL) {
ret = -1;
goto out;
}
if (parse_security_opts(srcconfig, (*dstconfig)) != 0) {
ret = -1;
goto out;
}
out:
return ret;
}
static inline void check_and_strdup_s(char **dst_item, const char *src_item)
{
if (src_item != NULL && dst_item != NULL) {
(*dst_item) = util_strdup_s((src_item));
}
}
static int pack_host_config_common(host_config *dstconfig, const isula_host_config_t *srcconfig)
{
int ret = 0;
ret = pack_host_config_ns_change_files(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
ret = pack_hostconfig_cgroup(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
ret = pack_host_config_caps(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
ret = generate_storage_opts(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
ret = generate_sysctls(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
ret = pack_host_config_network(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* devices which will be populated into container */
ret = generate_devices(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* blkio device */
ret = generate_blkio(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* hugepage limits */
ret = generate_hugetlb_limits(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* binds to mount */
ret = generate_binds(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* groups to add */
ret = generate_groups(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
/* security opt */
ret = generate_security(&dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
out:
return ret;
}
int generate_hostconfig(const isula_host_config_t *srcconfig, char **hostconfigstr)
{
int ret = 0;
parser_error err = NULL;
host_config *dstconfig = NULL;
struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 };
dstconfig = util_common_calloc_s(sizeof(*dstconfig));
if (dstconfig == NULL) {
ret = -1;
goto out;
}
dstconfig->privileged = srcconfig->privileged;
dstconfig->system_container = srcconfig->system_container;
dstconfig->auto_remove = srcconfig->auto_remove;
dstconfig->auto_remove_bak = srcconfig->auto_remove;
dstconfig->readonly_rootfs = srcconfig->readonly_rootfs;
dstconfig->oom_kill_disable = srcconfig->oom_kill_disable;
dstconfig->shm_size = srcconfig->shm_size;
ret = pack_host_config_common(dstconfig, srcconfig);
if (ret < 0) {
goto out;
}
check_and_strdup_s(&dstconfig->network_mode, srcconfig->network_mode);
check_and_strdup_s(&dstconfig->ipc_mode, srcconfig->ipc_mode);
check_and_strdup_s(&dstconfig->userns_mode, srcconfig->userns_mode);
check_and_strdup_s(&dstconfig->user_remap, srcconfig->user_remap);
check_and_strdup_s(&dstconfig->uts_mode, srcconfig->uts_mode);
check_and_strdup_s(&dstconfig->pid_mode, srcconfig->pid_mode);
/* hook-spec file */
check_and_strdup_s(&dstconfig->hook_spec, srcconfig->hook_spec);
/* env target file */
check_and_strdup_s(&dstconfig->env_target_file, srcconfig->env_target_file);
/* cgroup parent */
check_and_strdup_s(&dstconfig->cgroup_parent, srcconfig->cgroup_parent);
if (!parse_restart_policy(srcconfig->restart_policy, &dstconfig->restart_policy)) {
ERROR("Invalid restart policy");
ret = -1;
goto out;
}
if (srcconfig->host_channel != NULL) {
dstconfig->host_channel = parse_host_channel(srcconfig->host_channel);
if (dstconfig->host_channel == NULL) {
ERROR("Invalid host channel");
ret = -1;
goto out;
}
}
*hostconfigstr = host_config_generate_json(dstconfig, &ctx, &err);
if (*hostconfigstr == NULL) {
COMMAND_ERROR("Failed to generate hostconfig json:%s", err);
ret = -1;
goto out;
}
out:
free(err);
free_host_config(dstconfig);
return ret;
}
static int pack_container_custom_config_log(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
/* log config */
container_spec->log_config = util_common_calloc_s(sizeof(container_config_log_config));
if (container_spec->log_config == NULL) {
ret = -1;
goto out;
}
if (custom_conf->log_file != NULL) {
container_spec->log_config->log_file = util_strdup_s(custom_conf->log_file);
}
if (custom_conf->log_file_size != NULL) {
container_spec->log_config->log_file_size = util_strdup_s(custom_conf->log_file_size);
}
if (custom_conf->log_file_rotate) {
container_spec->log_config->log_file_rotate = custom_conf->log_file_rotate;
}
out:
return ret;
}
static int pack_container_custom_config_args(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
int i;
/* entrypoint */
if (util_valid_str(custom_conf->entrypoint)) {
container_spec->entrypoint = util_common_calloc_s(sizeof(char *));
if (container_spec->entrypoint == NULL) {
ret = -1;
goto out;
}
container_spec->entrypoint[0] = util_strdup_s(custom_conf->entrypoint);
container_spec->entrypoint_len++;
}
/* commands */
if ((custom_conf->cmd_len != 0 && custom_conf->cmd)) {
if (custom_conf->cmd_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("The length of cmd is too long!");
ret = -1;
goto out;
}
container_spec->cmd = util_common_calloc_s(custom_conf->cmd_len * sizeof(char *));
if (container_spec->cmd == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < (int)custom_conf->cmd_len; i++) {
container_spec->cmd[container_spec->cmd_len] = util_strdup_s(custom_conf->cmd[i]);
container_spec->cmd_len++;
}
}
out:
return ret;
}
static int pack_container_custom_config_mounts(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
int i = 0;
/* mounts to mount filesystem */
if (custom_conf->mounts != NULL && custom_conf->mounts_len > 0) {
if (custom_conf->mounts_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many mounts to mount filesystem!");
ret = -1;
goto out;
}
container_spec->mounts = util_common_calloc_s(custom_conf->mounts_len * sizeof(char *));
if (container_spec->mounts == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < (int)custom_conf->mounts_len; i++) {
container_spec->mounts[container_spec->mounts_len] = util_strdup_s(custom_conf->mounts[i]);
container_spec->mounts_len++;
}
}
out:
return ret;
}
static int pack_container_custom_config_array(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
int i = 0;
/* environment variables */
if (custom_conf->env_len != 0 && custom_conf->env) {
if (custom_conf->env_len > SIZE_MAX / sizeof(char *)) {
COMMAND_ERROR("Too many environment variables");
return -1;
}
container_spec->env = util_common_calloc_s(custom_conf->env_len * sizeof(char *));
if (container_spec->env == NULL) {
ret = -1;
goto out;
}
for (i = 0; i < (int)custom_conf->env_len; i++) {
container_spec->env[container_spec->env_len] = util_strdup_s(custom_conf->env[i]);
container_spec->env_len++;
}
}
out:
return ret;
}
static int get_label_key_value(const char *label, char **key, char **value)
{
int ret = 0;
char **arr = util_string_split_n(label, '=', 2);
if (arr == NULL) {
ERROR("Failed to split input label");
ret = -1;
goto out;
}
*key = util_strdup_s(arr[0]);
if (util_array_len((const char **)arr) == 1) {
*value = util_strdup_s("");
} else {
*value = util_strdup_s(arr[1]);
}
out:
util_free_array(arr);
return ret;
}
static int pack_container_custom_config_labels(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
int i;
char *key = NULL;
char *value = NULL;
if (custom_conf->label_len == 0 || custom_conf->label == NULL) {
return 0;
}
/* labels */
container_spec->labels = util_common_calloc_s(sizeof(json_map_string_string));
if (container_spec->labels == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
for (i = 0; i < custom_conf->label_len; i++) {
if (get_label_key_value(custom_conf->label[i], &key, &value) != 0) {
ERROR("Failed to get key and value of label");
ret = -1;
goto out;
}
if (append_json_map_string_string(container_spec->labels, key, value)) {
ERROR("Append map failed");
ret = -1;
goto out;
}
free(key);
key = NULL;
free(value);
value = NULL;
}
out:
free(key);
free(value);
return ret;
}
static bool have_health_check(const isula_container_config_t *custom_conf)
{
bool have_health_settings = false;
if ((custom_conf->health_cmd != NULL && strlen(custom_conf->health_cmd) != 0) ||
custom_conf->health_interval != 0 || custom_conf->health_timeout != 0 ||
custom_conf->health_start_period != 0 || custom_conf->health_retries != 0) {
have_health_settings = true;
}
return have_health_settings;
}
static int pack_custom_no_health_check(container_config *container_spec, bool have_health_settings,
defs_health_check *health_config)
{
int ret = 0;
if (have_health_settings) {
COMMAND_ERROR("--no-healthcheck conflicts with --health-* options");
ret = -1;
goto out;
}
health_config->test = util_common_calloc_s(sizeof(char *));
if (health_config->test == NULL) {
ret = -1;
goto out;
}
health_config->test[health_config->test_len++] = util_strdup_s("NONE");
container_spec->health_check = health_config;
out:
return ret;
}
static int pack_custom_with_health_check(container_config *container_spec,
const isula_container_config_t *custom_conf, bool have_health_settings,
defs_health_check *health_config)
{
int ret = 0;
if (custom_conf->health_cmd != NULL && strlen(custom_conf->health_cmd) != 0) {
health_config->test = util_common_calloc_s(2 * sizeof(char *));
if (health_config->test == NULL) {
ret = -1;
goto out;
}
health_config->test[health_config->test_len++] = util_strdup_s("CMD-SHELL");
health_config->test[health_config->test_len++] = util_strdup_s(custom_conf->health_cmd);
} else {
COMMAND_ERROR("--health-cmd required!");
ret = -1;
goto out;
}
health_config->interval = custom_conf->health_interval;
health_config->timeout = custom_conf->health_timeout;
health_config->start_period = custom_conf->health_start_period;
health_config->retries = custom_conf->health_retries;
health_config->exit_on_unhealthy = custom_conf->exit_on_unhealthy;
if (container_spec->health_check != NULL) {
free_defs_health_check(container_spec->health_check);
}
container_spec->health_check = health_config;
out:
return ret;
}
static int pack_container_custom_config_health(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
bool have_health_settings = false;
defs_health_check *health_config = NULL;
if (container_spec == NULL || custom_conf == NULL) {
return 0;
}
have_health_settings = have_health_check(custom_conf);
health_config = util_common_calloc_s(sizeof(defs_health_check));
if (health_config == NULL) {
ret = -1;
goto out;
}
if (custom_conf->no_healthcheck) {
ret = pack_custom_no_health_check(container_spec, have_health_settings, health_config);
if (ret != 0) {
goto out;
}
} else if (have_health_settings) {
ret = pack_custom_with_health_check(container_spec, custom_conf, have_health_settings, health_config);
if (ret != 0) {
goto out;
}
} else {
goto out;
}
return ret;
out:
free_defs_health_check(health_config);
return ret;
}
static int pack_container_custom_config_annotation(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
size_t j;
container_spec->annotations = util_common_calloc_s(sizeof(json_map_string_string));
if (container_spec->annotations == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
if (custom_conf->annotations != NULL) {
for (j = 0; j < custom_conf->annotations->len; j++) {
if (append_json_map_string_string(container_spec->annotations, custom_conf->annotations->keys[j],
custom_conf->annotations->values[j])) {
ERROR("Append map failed");
ret = -1;
goto out;
}
}
}
out:
return ret;
}
static int pack_container_custom_config_pre(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = 0;
ret = pack_container_custom_config_log(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
ret = pack_container_custom_config_args(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
ret = pack_container_custom_config_mounts(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
ret = pack_container_custom_config_array(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
ret = pack_container_custom_config_labels(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
ret = pack_container_custom_config_health(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
out:
return ret;
}
/* translate create_custom_config to container_config */
static int pack_container_custom_config(container_config *container_spec,
const isula_container_config_t *custom_conf)
{
int ret = -1;
if (container_spec == NULL || custom_conf == NULL) {
return ret;
}
ret = pack_container_custom_config_pre(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
if (custom_conf->hostname != NULL) {
container_spec->hostname = util_strdup_s(custom_conf->hostname);
}
/* console config */
container_spec->tty = custom_conf->tty;
container_spec->open_stdin = custom_conf->open_stdin;
container_spec->attach_stdin = custom_conf->attach_stdin;
container_spec->attach_stdout = custom_conf->attach_stdout;
container_spec->attach_stderr = custom_conf->attach_stderr;
/* user and group */
if (custom_conf->user != NULL) {
container_spec->user = util_strdup_s(custom_conf->user);
}
/* settings for system container */
if (custom_conf->system_container) {
container_spec->system_container = custom_conf->system_container;
}
if (custom_conf->ns_change_opt != NULL) {
container_spec->ns_change_opt = util_strdup_s(custom_conf->ns_change_opt);
}
ret = pack_container_custom_config_annotation(container_spec, custom_conf);
if (ret != 0) {
goto out;
}
if (custom_conf->workdir != NULL) {
container_spec->working_dir = util_strdup_s(custom_conf->workdir);
}
out:
return ret;
}
int generate_container_config(const isula_container_config_t *custom_conf, char **container_config_str)
{
int ret = 0;
container_config *container_spec = NULL;
struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 };
parser_error err = NULL;
/* step 1: malloc the container config */
container_spec = util_common_calloc_s(sizeof(container_config));
if (container_spec == NULL) {
ERROR("Memory out");
ret = -1;
goto out;
}
/* step 2: pack the container custom config */
ret = pack_container_custom_config(container_spec, custom_conf);
if (ret != 0) {
ERROR("Failed to pack the container custom config");
ret = -1;
goto out;
}
/* step 3: generate the config string */
*container_config_str = container_config_generate_json(container_spec, &ctx, &err);
if (*container_config_str == NULL) {
ERROR("Failed to generate OCI specification json string");
ret = -1;
goto out;
}
out:
free_container_config(container_spec);
free(err);
return ret;
}