/****************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #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; } } 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 :[]." " 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 parse_seccomp(const isula_host_config_t *srcconfig, host_config *dstconfig) { int ret = 0; int nret = 0; int seccomp_count = 0; size_t i = 0; size_t size = 0; char *seccomp_file = NULL; 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 }; for (i = 0; i < srcconfig->security_len; i++) { // add seccomp if (strncmp(srcconfig->security[i], "seccomp=", strlen("seccomp=")) == 0) { seccomp_file = srcconfig->security[i] + strlen("seccomp") + 1; if (strcmp(seccomp_file, "unconfined") == 0) { dstconfig->security_opt[seccomp_count++] = util_strdup_s(srcconfig->security[i]); dstconfig->security_opt_len++; continue; } 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; } free_docker_seccomp(seccomp_spec); seccomp_spec = NULL; 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 || (size_t)nret >= size) { COMMAND_ERROR("failed to sprintf buffer!"); ret = -1; goto out; } dstconfig->security_opt[seccomp_count++] = util_strdup_s(tmp_str); dstconfig->security_opt_len++; free(tmp_str); tmp_str = NULL; free(err); err = NULL; free(seccomp_json); seccomp_json = NULL; } } out: free(seccomp_json); free(tmp_str); free_docker_seccomp(seccomp_spec); free(err); return ret; } static int parse_no_new_privileges(const isula_host_config_t *srcconfig, host_config *dstconfig) { int ret = 0; size_t i = 0; size_t new_size; size_t old_size; char **tmp_security_opt = NULL; for (i = 0; i < srcconfig->security_len; i++) { // no new privileges if (strcmp(srcconfig->security[i], "no-new-privileges") == 0) { 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(srcconfig->security[i]); break; } } 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; } ret = parse_seccomp(srcconfig, (*dstconfig)); if (ret < 0) { goto out; } ret = parse_no_new_privileges(srcconfig, (*dstconfig)); if (ret < 0) { 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 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_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; }