/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2017-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: 2017-11-22 * Description: provide container specs functions ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "log.h" #include "oci_runtime_spec.h" #include "host_config.h" #include "container_custom_config.h" #include "utils.h" #include "config.h" #include "path.h" #include "lcrd_config.h" #include "specs_extend.h" #define MINUID 0 #define MAXUID (((1LL << 31) - 1)) #define DEFAULT_UID 0 #define UnixPasswdPath "/etc/passwd" #define UnixGroupPath "/etc/group" #define MERGE_HOOKS_ITEM_DEF(item) \ int merge_##item##_conf(oci_runtime_spec_hooks *dest, oci_runtime_spec_hooks * src) \ {\ size_t old_size = 0; \ size_t new_size = 0; \ int ret = 0; \ size_t i = 0; \ if (src->item##_len) { \ defs_hook **item = NULL; \ if (dest->item##_len > (LIST_SIZE_MAX - src->item##_len) - 1) { \ ERROR("the length of item element is too long!"); \ ret = -1; \ goto out; \ } \ old_size = dest->item##_len * sizeof(defs_hook *); \ new_size = (dest->item##_len + src->item##_len + 1) * sizeof(defs_hook *); \ ret = mem_realloc((void **)&(item), new_size, dest->item, old_size); \ if (ret != 0) { \ ERROR("Failed to realloc memory for hooks_"#item" variables"); \ ret = -1; \ goto out; \ }\ dest->item = item; \ for (; i < src->item##_len; i++) { \ dest->item[dest->item##_len] = src->item[i]; \ dest->item##_len++; \ src->item[i] = NULL; \ } \ src->item##_len = 0; \ free(src->item); \ src->item = NULL; \ } \ out: \ return ret; \ } MERGE_HOOKS_ITEM_DEF(prestart) MERGE_HOOKS_ITEM_DEF(poststart) MERGE_HOOKS_ITEM_DEF(poststop) int merge_hooks(oci_runtime_spec_hooks *dest, oci_runtime_spec_hooks *src) { if (merge_prestart_conf(dest, src) || merge_poststart_conf(dest, src) || merge_poststop_conf(dest, src)) { return -1; } return 0; } int merge_global_hook(oci_runtime_spec *oci_spec) { int ret = 0; oci_runtime_spec_hooks *hooks = NULL; oci_runtime_spec_hooks *tmp = NULL; if (conf_get_lcrd_hooks(&hooks)) { ERROR("Failed to get lcrd hooks"); ret = -1; goto out; } if (oci_spec->hooks != NULL) { if (hooks != NULL) { if (merge_hooks(hooks, oci_spec->hooks)) { ret = -1; goto out; } tmp = hooks; hooks = oci_spec->hooks; oci_spec->hooks = tmp; } } else { oci_spec->hooks = hooks; hooks = NULL; } out: free_oci_runtime_spec_hooks(hooks); return ret; } static int make_one_id_mapping(defs_id_mapping ***mappings, unsigned int id, unsigned int size) { *mappings = util_common_calloc_s(sizeof(defs_id_mapping *)); if (*mappings == NULL) { return -1; } (*mappings)[0] = util_common_calloc_s(sizeof(defs_id_mapping)); if ((*mappings)[0] == NULL) { return -1; } (*mappings)[0]->host_id = id; (*mappings)[0]->container_id = 0; (*mappings)[0]->size = size; return 0; } static int make_linux_uid_gid_mappings(oci_runtime_spec *container, unsigned int host_uid, unsigned int host_gid, unsigned int size) { int ret = 0; ret = make_sure_oci_spec_linux(container); if (ret < 0) { goto out; } if (container->linux->uid_mappings == NULL) { ret = make_one_id_mapping(&(container->linux->uid_mappings), host_uid, size); if (ret < 0) { goto out; } container->linux->uid_mappings_len++; } if (container->linux->gid_mappings == NULL) { ret = make_one_id_mapping(&(container->linux->gid_mappings), host_gid, size); if (ret < 0) { goto out; } container->linux->gid_mappings_len++; } out: return ret; } int make_userns_remap(oci_runtime_spec *container, const char *user_remap) { int ret = 0; unsigned int host_uid = 0; unsigned int host_gid = 0; unsigned int size = 0; if (user_remap == NULL) { return 0; } ret = util_parse_user_remap(user_remap, &host_uid, &host_gid, &size); if (ret) { ERROR("User remap '%s' format error", user_remap); return ret; } if (host_uid == 0 && host_gid == 0) { return 0; } ret = make_linux_uid_gid_mappings(container, host_uid, host_gid, size); if (ret) { ERROR("Make linux uid and gid mappings failed"); return ret; } return ret; } static int generate_env_map_from_file(FILE *fp, json_map_string_string *env_map) { int ret = 0; char *key = NULL; char *value = NULL; char *pline = NULL; size_t length = 0; char *saveptr = NULL; while (getline(&pline, &length, fp) != -1) { util_trim_newline(pline); pline = util_trim_space(pline); if (pline == NULL || pline[0] == '#') { continue; } key = strtok_r(pline, "=", &saveptr); value = strtok_r(NULL, "=", &saveptr); if (key != NULL && value != NULL) { key = util_trim_space(key); value = util_trim_space(value); if ((size_t)(MAX_BUFFER_SIZE - 1) - strlen(key) < strlen(value)) { ERROR("env length exceed %lld bytes", MAX_BUFFER_SIZE); ret = -1; goto out; } ret = append_json_map_string_string(env_map, key, value); if (ret < 0) { ERROR("append env to map failed"); goto out; } } } out: free(pline); return ret; } static json_map_string_string *parse_env_target_file(const char *env_path) { int ret = 0; FILE *fp = NULL; json_map_string_string *env_map = (json_map_string_string *)util_common_calloc_s(sizeof(json_map_string_string)); if (env_map == NULL) { ERROR("Out of memory"); return NULL; } fp = util_fopen(env_path, "r"); if (fp == NULL) { SYSERROR("Failed to open env target file '%s'", env_path); goto out; } ret = generate_env_map_from_file(fp, env_map); if (ret != 0) { ERROR("Failed to generate env map from file"); goto out; } fclose(fp); return env_map; out: if (fp != NULL) { fclose(fp); } free_json_map_string_string(env_map); return NULL; } static int do_append_env(char ***env, size_t *env_len, const char *key, const char *value) { char *tmp_env = NULL; size_t tmp_env_len = 0; if (strlen(value) > ((SIZE_MAX - 2) - strlen(key))) { ERROR("env value length too big"); return -1; } tmp_env_len = strlen(key) + strlen(value) + 2; tmp_env = util_common_calloc_s(tmp_env_len); if (tmp_env == NULL) { ERROR("Out of memory"); return -1; } int nret = snprintf(tmp_env, tmp_env_len, "%s=%s", key, value); if (nret < 0 || (size_t)nret >= tmp_env_len) { ERROR("Out of memory"); free(tmp_env); return -1; } if (util_array_append(env, tmp_env) < 0) { ERROR("Failed to append env"); free(tmp_env); return -1; } free(tmp_env); (*env_len)++; return 0; } static int check_env_need_append(const oci_runtime_spec *oci_spec, const char *env_key, bool *is_append) { size_t i = 0; char *key = NULL; char *value = NULL; char *saveptr = NULL; for (i = 0; i < oci_spec->process->env_len; i++) { char *tmp_env = NULL; tmp_env = util_strdup_s(oci_spec->process->env[i]); key = strtok_r(tmp_env, "=", &saveptr); value = strtok_r(NULL, "=", &saveptr); if (key == NULL || value == NULL) { ERROR("Bad env format"); free(tmp_env); tmp_env = NULL; return -1; } if (strcmp(key, env_key) == 0) { *is_append = false; free(tmp_env); tmp_env = NULL; return 0; } free(tmp_env); tmp_env = NULL; } return 0; } static int do_merge_env_target(oci_runtime_spec *oci_spec, const json_map_string_string *env_map) { int ret = 0; size_t i = 0; char **env = NULL; size_t env_len = 0; env = (char **)util_common_calloc_s(sizeof(char *)); if (env == NULL) { ERROR("Out of memory"); return -1; } for (i = 0; i < env_map->len; i++) { bool is_append = true; ret = check_env_need_append(oci_spec, env_map->keys[i], &is_append); if (ret < 0) { goto out; } if (!is_append) { continue; } if (do_append_env(&env, &env_len, (const char *)env_map->keys[i], (const char *)env_map->values[i]) < 0) { ERROR("Failed to append env"); ret = -1; goto out; } } ret = merge_env(oci_spec, (const char **)env, env_len); out: util_free_array(env); return ret; } static char *get_env_abs_file_path(const oci_runtime_spec *oci_spec, const char *env_target_file) { char *env_path = NULL; int64_t file_size = 0; if (oci_spec->root == NULL || oci_spec->root->path == NULL) { return NULL; } if (realpath_in_scope(oci_spec->root->path, env_target_file, &env_path) < 0) { ERROR("env target file '%s' real path must be under rootfs '%s'", env_target_file, oci_spec->root->path); goto out; } if (!util_file_exists(env_path)) { return env_path; } file_size = util_file_size(env_path); if (file_size > REGULAR_FILE_SIZE) { ERROR("env target file %s, size exceed limit: %lld", env_target_file, REGULAR_FILE_SIZE); goto out; } return env_path; out: free(env_path); return NULL; } int merge_env_target_file(oci_runtime_spec *oci_spec, const char *env_target_file) { int ret = 0; char *env_path = NULL; json_map_string_string *env_map = NULL; if (env_target_file == NULL) { return 0; } env_path = get_env_abs_file_path(oci_spec, env_target_file); if (env_path == NULL) { ret = -1; goto out; } if (!util_file_exists(env_path)) { goto out; } env_map = parse_env_target_file(env_path); if (env_map == NULL) { ERROR("Failed to parse env target file"); ret = -1; goto out; } ret = do_merge_env_target(oci_spec, (const json_map_string_string *)env_map); if (ret != 0) { ERROR("Failed to merge env target file"); goto out; } out: free(env_path); free_json_map_string_string(env_map); return ret; } int merge_env(oci_runtime_spec *oci_spec, const char **env, size_t env_len) { int ret = 0; size_t new_size = 0; size_t old_size = 0; size_t i = 0; char **temp = NULL; if (env_len == 0 || env == NULL) { return 0; } ret = make_sure_oci_spec_process(oci_spec); if (ret < 0) { goto out; } if (env_len > LIST_ENV_SIZE_MAX - oci_spec->process->env_len) { ERROR("The length of envionment variables is too long, the limit is %d", LIST_ENV_SIZE_MAX); lcrd_set_error_message("The length of envionment variables is too long, the limit is %d", LIST_ENV_SIZE_MAX); ret = -1; goto out; } new_size = (oci_spec->process->env_len + env_len) * sizeof(char *); old_size = oci_spec->process->env_len * sizeof(char *); ret = mem_realloc((void **)&temp, new_size, oci_spec->process->env, old_size); if (ret != 0) { ERROR("Failed to realloc memory for envionment variables"); ret = -1; goto out; } oci_spec->process->env = temp; for (i = 0; i < env_len; i++) { oci_spec->process->env[oci_spec->process->env_len] = util_strdup_s(env[i]); oci_spec->process->env_len++; } out: return ret; } static int read_user_file(const char *basefs, const char *user_path, FILE **stream) { int nret; int64_t filesize = 0; char path[PATH_MAX] = {0}; char real_path[PATH_MAX] = {0}; nret = snprintf(path, sizeof(path), "%s/%s", basefs, user_path); if (nret < 0 || (size_t)nret >= sizeof(path)) { ERROR("Path is too long"); return -1; } if (cleanpath(path, real_path, sizeof(real_path)) == NULL) { ERROR("Failed to clean path"); return -1; } filesize = util_file_size(real_path); if (filesize > REGULAR_FILE_SIZE) { ERROR("File %s is more than %lld", real_path, (long long)REGULAR_FILE_SIZE); lcrd_set_error_message("File %s is more than %lld", real_path, (long long)REGULAR_FILE_SIZE); return -1; } *stream = util_fopen(real_path, "r"); if (*stream == NULL) { ERROR("Failed to open %s: %s", real_path, strerror(errno)); return 0; } return 0; } static void parse_user_group(const char *username, char **user, char **group, char **tmp_dup) { char *tmp = NULL; char *pdot = NULL; if (user == NULL || group == NULL || tmp_dup == NULL) { return; } if (username != NULL) { tmp = util_strdup_s(username); // for free tmp in caller *tmp_dup = tmp; pdot = strstr(tmp, ":"); if (pdot != NULL) { *pdot = '\0'; if (pdot != tmp) { // User found *user = tmp; } if (*(pdot + 1) != '\0') { // group found *group = pdot + 1; } } else { // No : found if (*tmp != '\0') { *user = tmp; } } } return; } static void uids_gids_range_err_log() { ERROR("uids and gids must be in range 0-%d", MAXUID); lcrd_set_error_message("uids and gids must be in range 0-%d", MAXUID); return; } static bool b_user_found(const char *user, const struct passwd *pwbufp) { int uret = -1; long long n_user = 0; bool userfound = false; if (pwbufp == NULL) { return false; } if (user != NULL) { uret = util_safe_llong(user, &n_user); } if (user == NULL && pwbufp->pw_uid == DEFAULT_UID) { userfound = true; } // Treat numeric usename as valid UID if (uret == 0 && (long long)pwbufp->pw_uid == n_user) { userfound = true; } if (uret != 0 && user != NULL && strcmp(user, pwbufp->pw_name) == 0) { userfound = true; } return userfound; } static int proc_by_fpasswd(FILE *f_passwd, const char *user, oci_runtime_spec_process_user *puser, char **matched_username) { int ret = 0; int errval = 0; int uret = -1; bool userfound = false; long long n_user = 0; char buf[BUFSIZ]; struct passwd pw, *pwbufp = NULL; if (f_passwd != NULL) { errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); while (errval == 0 && pwbufp != NULL) { userfound = b_user_found(user, pwbufp); // Take the first match as valid user if (userfound) { free(puser->username); puser->username = util_strdup_s(pwbufp->pw_name); puser->uid = pwbufp->pw_uid; puser->gid = pwbufp->pw_gid; *matched_username = puser->username; break; } errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); } } if (errval != 0 && errval != ENOENT) { ERROR("Failed to parse passwd file: Insufficient buffer space supplied"); lcrd_set_error_message("Failed to parse passwd file: Insufficient buffer space supplied"); ret = -1; goto out; } if (!userfound && user != NULL) { uret = util_safe_llong(user, &n_user); // user is not a valid numeric UID if (uret != 0) { ERROR("Unable to find user '%s'", user); lcrd_set_error_message("Unable to find user '%s': no matching entries in passwd file", user); ret = -1; goto out; } if (n_user < MINUID || n_user > MAXUID) { uids_gids_range_err_log(); ret = -1; goto out; } puser->uid = (uid_t)n_user; } out: return ret; } static int append_additional_gids(gid_t gid, gid_t **additional_gids, size_t *len) { int ret = 0; size_t new_len = 0; size_t i; gid_t *new_gids = NULL; if (*len > (SIZE_MAX / sizeof(gid_t)) - 1) { ERROR("Out of memory"); return -1; } new_len = *len + 1; for (i = 0; i < *len; i++) { if ((*additional_gids)[i] == gid) { return 0; } } ret = mem_realloc((void **)&new_gids, new_len * sizeof(gid_t), *additional_gids, (*len) * sizeof(gid_t)); if (ret != 0) { ERROR("Out of memory"); return -1; } *additional_gids = new_gids; (*additional_gids)[*len] = gid; *len = new_len; return 0; } static int search_group_list(struct group *gbufp, const char *username, oci_runtime_spec_process_user *puser) { char **username_list = gbufp->gr_mem; while (username_list != NULL && *username_list != NULL) { if (strcmp(*username_list, username) == 0) { if (append_additional_gids(gbufp->gr_gid, &puser->additional_gids, &puser->additional_gids_len)) { ERROR("Failed to append additional groups"); return -1; } break; } username_list++; } return 0; } static bool check_group_found(const char *group, const struct group *gbufp) { int gret = -1; long long n_grp = 0; if (group != NULL) { gret = util_safe_llong(group, &n_grp); } if (gret == 0 && n_grp == (long long)gbufp->gr_gid) { return true; } if (gret != 0 && group != NULL && strcmp(group, gbufp->gr_name) == 0) { return true; } return false; } static int do_proc_by_froup(FILE *f_group, const char *group, oci_runtime_spec_process_user *puser, const char *matched_username, int *groupcnt) { int errval = 0; char buf[BUFSIZ] = { 0 }; bool groupfound = false; struct group grp, *gbufp = NULL; if (f_group == NULL) { return 0; } errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); while (errval == 0 && gbufp != NULL) { // Treat numeric group as valid GID if (group == NULL) { if (search_group_list(gbufp, matched_username, puser) != 0) { return -1; } errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); continue; } groupfound = check_group_found(group, gbufp); if (groupfound && *groupcnt != 1) { // Continue search group list, but only take first found group puser->gid = gbufp->gr_gid; *groupcnt = 1; } errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); } return 0; } static int proc_by_fgroup(FILE *f_group, const char *group, oci_runtime_spec_process_user *puser, const char *matched_username) { int ret = 0; int gret = -1; int groupcnt = 0; long long n_grp = 0; if (group != NULL || matched_username != NULL) { if (do_proc_by_froup(f_group, group, puser, matched_username, &groupcnt) != 0) { goto out; } if (group != NULL && groupcnt == 0) { gret = util_safe_llong(group, &n_grp); // group is not a valid numeric GID if (gret != 0) { ERROR("Unable to find group '%s'", group); lcrd_set_error_message("Unable to find group '%s': no matching entries in group file", group); ret = -1; goto out; } if (n_grp < MINUID || n_grp > MAXUID) { uids_gids_range_err_log(); ret = -1; goto out; } puser->gid = (gid_t)n_grp; } } out: return ret; } static int get_exec_user(const char *username, FILE *f_passwd, FILE *f_group, oci_runtime_spec_process_user *puser) { int ret = 0; char *tmp = NULL; char *user = NULL; char *group = NULL; char *matched_username = NULL; // parse user and group by username parse_user_group(username, &user, &group, &tmp); // proc by f_passwd ret = proc_by_fpasswd(f_passwd, user, puser, &matched_username); if (ret != 0) { ret = -1; goto cleanup; } // proc by f_group ret = proc_by_fgroup(f_group, group, puser, matched_username); if (ret != 0) { ret = -1; goto cleanup; } cleanup: free(tmp); return ret; } static int append_additional_groups(const struct group *grp, struct group **groups, size_t *len) { int ret = 0; struct group *new_groups = NULL; size_t new_len = *len + 1; ret = mem_realloc((void **)&new_groups, new_len * sizeof(struct group), *groups, (*len) * sizeof(struct group)); if (ret != 0) { ERROR("Out of memory"); return -1; } *groups = new_groups; (*groups)[*len].gr_name = util_strdup_s(grp->gr_name); (*groups)[*len].gr_gid = grp->gr_gid; *len = new_len; return 0; } static bool group_matched(const char *group, const struct group *gbufp) { bool matched = false; long long n_gid = 0; int gret = -1; if (group == NULL || gbufp == NULL) { return false; } gret = util_safe_llong(group, &n_gid); if (strcmp(group, gbufp->gr_name) == 0 || (gret == 0 && n_gid == gbufp->gr_gid)) { matched = true; } return matched; } static int get_one_additional_group(const char *additional_group, struct group *groups, size_t groups_len, oci_runtime_spec_process_user *puser) { int ret = 0; int gret = -1; long long n_gid = 0; bool found = false; size_t j; for (j = 0; groups != NULL && j < groups_len; j++) { // Only take the first founded group if (group_matched(additional_group, &groups[j])) { found = true; if (append_additional_gids(groups[j].gr_gid, &puser->additional_gids, &puser->additional_gids_len)) { ERROR("Failed to append additional groups"); ret = -1; goto out; } break; } } if (!found) { gret = util_safe_llong(additional_group, &n_gid); if (gret != 0) { ERROR("Unable to find group %s", additional_group); lcrd_set_error_message("Unable to find group %s", additional_group); ret = -1; goto out; } if (n_gid < MINUID || n_gid > MAXUID) { uids_gids_range_err_log(); ret = -1; goto out; } if (append_additional_gids((gid_t)n_gid, &puser->additional_gids, &puser->additional_gids_len)) { ERROR("Failed to append additional groups"); ret = -1; goto out; } } out: return ret; } int get_additional_groups(char **additional_groups, size_t additional_groups_len, FILE *f_group, oci_runtime_spec_process_user *puser) { int ret = 0; size_t i; size_t groups_len = 0; char buf[BUFSIZ] = { 0 }; struct group grp; struct group *gbufp = NULL; struct group *groups = NULL; while (f_group != NULL && fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp) == 0) { for (i = 0; i < additional_groups_len; i++) { if (!group_matched(additional_groups[i], gbufp)) { continue; } if (append_additional_groups(gbufp, &groups, &groups_len)) { ret = -1; goto cleanup; } } } for (i = 0; i < additional_groups_len; i++) { ret = get_one_additional_group(additional_groups[i], groups, groups_len, puser); if (ret != 0) { ret = -1; goto cleanup; } } cleanup: for (i = 0; groups != NULL && i < groups_len; i++) { free(groups[i].gr_name); } free(groups); return ret; } static int resolve_basefs(const char *basefs, char **resolved_basefs) { struct stat s; char real_path[PATH_MAX + 1] = { 0 }; if (strlen(basefs) > PATH_MAX || !realpath(basefs, real_path)) { ERROR("invalid file path %s", basefs); return -1; } if (stat(real_path, &s) < 0) { ERROR("stat failed, error: %s", strerror(errno)); return -1; } if ((s.st_mode & S_IFMT) == S_IFDIR) { *resolved_basefs = util_strdup_s(real_path); } else { *resolved_basefs = util_strdup_s("/"); } return 0; } int get_user(const char *basefs, const host_config *hc, const char *userstr, oci_runtime_spec_process_user *puser) { int ret = 0; FILE *f_passwd = NULL; FILE *f_group = NULL; char *resolved_basefs = NULL; if (basefs == NULL || puser == NULL || hc == NULL) { return -1; } ret = resolve_basefs(basefs, &resolved_basefs); if (ret != 0) { goto cleanup; } ret = read_user_file(resolved_basefs, UnixPasswdPath, &f_passwd); if (ret != 0) { goto cleanup; } ret = read_user_file(resolved_basefs, UnixGroupPath, &f_group); if (ret != 0) { goto cleanup; } ret = get_exec_user(userstr, f_passwd, f_group, puser); if (ret != 0) { goto cleanup; } if (hc->group_add != NULL && hc->group_add_len > 0) { if (hc->group_add_len > LIST_SIZE_MAX) { ERROR("Too many groups to add, the limit is %d", LIST_SIZE_MAX); lcrd_set_error_message("Too many groups to add, the limit is %d", LIST_SIZE_MAX); ret = -1; goto cleanup; } // Rewind f_group to serach from beginning again. if (f_group != NULL) { rewind(f_group); } ret = get_additional_groups(hc->group_add, hc->group_add_len, f_group, puser); if (ret != 0) { goto cleanup; } } cleanup: if (f_passwd != NULL) { fclose(f_passwd); } if (f_group != NULL) { fclose(f_group); } free(resolved_basefs); return ret; } static int make_sure_oci_spec_porcess_user(oci_runtime_spec *oci_spec) { int ret = 0; ret = make_sure_oci_spec_process(oci_spec); if (ret < 0) { return -1; } if (oci_spec->process->user == NULL) { oci_spec->process->user = util_common_calloc_s(sizeof(oci_runtime_spec_process_user)); if (oci_spec->process->user == NULL) { return -1; } } return 0; } int merge_user(const char *basefs, oci_runtime_spec *oci_spec, const host_config *hc, const char *user) { int ret = 0; ret = make_sure_oci_spec_porcess_user(oci_spec); if (ret < 0) { goto out; } if (get_user(basefs, hc, user, oci_spec->process->user)) { ERROR("Failed to get user with '%s'", user ? user : ""); ret = -1; goto out; } out: return ret; } char *oci_container_get_env(const oci_runtime_spec *oci_spec, const char *key) { const oci_runtime_spec_process *op = NULL; if (oci_spec == NULL) { ERROR("nil oci_spec"); return NULL; } if (oci_spec->process == NULL) { ERROR("nil oci_spec->process"); return NULL; } op = oci_spec->process; return util_env_get_val(op->env, op->env_len, key, strlen(key)); } int make_sure_oci_spec_linux(oci_runtime_spec *oci_spec) { if (oci_spec->linux == NULL) { oci_spec->linux = util_common_calloc_s(sizeof(oci_runtime_config_linux)); if (oci_spec->linux == NULL) { return -1; } } return 0; } int make_sure_oci_spec_process(oci_runtime_spec *oci_spec) { if (oci_spec->process == NULL) { oci_spec->process = util_common_calloc_s(sizeof(oci_runtime_spec_process)); if (oci_spec->process == NULL) { return -1; } } return 0; } int make_sure_oci_spec_linux_resources(oci_runtime_spec *oci_spec) { int ret = 0; ret = make_sure_oci_spec_linux(oci_spec); if (ret < 0) { return -1; } if (oci_spec->linux->resources == NULL) { oci_spec->linux->resources = util_common_calloc_s(sizeof(oci_runtime_config_linux_resources)); if (oci_spec->linux->resources == NULL) { return -1; } } return 0; } int make_sure_oci_spec_linux_resources_blkio(oci_runtime_spec *oci_spec) { int ret; ret = make_sure_oci_spec_linux_resources(oci_spec); if (ret < 0) { return -1; } if (oci_spec->linux->resources->block_io == NULL) { oci_spec->linux->resources->block_io = util_common_calloc_s(sizeof(oci_runtime_config_linux_resources_block_io)); if (oci_spec->linux->resources->block_io == NULL) { return -1; } } return 0; } int merge_ulimits_pre(oci_runtime_spec *oci_spec, size_t host_ulimits_len) { int ret; size_t new_size, old_size, tmp; oci_runtime_spec_process_rlimits_element **rlimits_temp = NULL; ret = make_sure_oci_spec_process(oci_spec); if (ret < 0) { goto out; } tmp = SIZE_MAX / sizeof(oci_runtime_spec_process_rlimits_element *) - oci_spec->process->rlimits_len; if (host_ulimits_len > tmp) { ERROR("Too many rlimits to merge!"); ret = -1; goto out; } old_size = oci_spec->process->rlimits_len * sizeof(oci_runtime_spec_process_rlimits_element *); new_size = (oci_spec->process->rlimits_len + host_ulimits_len) * sizeof(oci_runtime_spec_process_rlimits_element *); ret = mem_realloc((void **)&rlimits_temp, new_size, oci_spec->process->rlimits, old_size); if (ret != 0) { ERROR("Failed to realloc memory for rlimits"); ret = -1; goto out; } oci_spec->process->rlimits = rlimits_temp; out: return ret; } int trans_ulimit_to_rlimit(oci_runtime_spec_process_rlimits_element **rlimit_dst, const host_config_ulimits_element *ulimit) { #define RLIMIT_PRE "RLIMIT_" int ret = 0; size_t j, namelen; char *typename = NULL; oci_runtime_spec_process_rlimits_element *rlimit = NULL; // name + "RLIMIT_" + '\0' if (strlen(ulimit->name) > ((SIZE_MAX - strlen(RLIMIT_PRE)) - 1)) { ERROR("Invalid ulimit name"); return -1; } namelen = strlen(ulimit->name) + strlen(RLIMIT_PRE) + 1; typename = util_common_calloc_s(namelen); if (typename == NULL) { ERROR("Out of memory"); ret = -1; goto out; } (void)strcat(typename, RLIMIT_PRE); for (j = 0; j < strlen(ulimit->name); j++) { typename[j + strlen(RLIMIT_PRE)] = (char)toupper((int)(ulimit->name[j])); } rlimit = util_common_calloc_s(sizeof(oci_runtime_spec_process_rlimits_element)); if (rlimit == NULL) { ERROR("Failed to malloc memory for rlimit"); ret = -1; goto out; } rlimit->type = typename; rlimit->soft = (uint64_t)ulimit->soft; rlimit->hard = (uint64_t)ulimit->hard; *rlimit_dst = rlimit; out: if (ret < 0) { free(typename); } return ret; } static int do_merge_one_ulimit(const oci_runtime_spec *oci_spec, oci_runtime_spec_process_rlimits_element *rlimit) { size_t j; bool exists = false; for (j = 0; j < oci_spec->process->rlimits_len; j++) { if (oci_spec->process->rlimits[j]->type == NULL) { ERROR("rlimit type is empty"); UTIL_FREE_AND_SET_NULL(rlimit->type); free(rlimit); return -1; } if (strcmp(oci_spec->process->rlimits[j]->type, rlimit->type) == 0) { exists = true; break; } } if (exists) { /* ulimit exist, discard default ulimit */ UTIL_FREE_AND_SET_NULL(rlimit->type); free(rlimit); } else { oci_spec->process->rlimits[oci_spec->process->rlimits_len] = rlimit; oci_spec->process->rlimits_len++; } return 0; } static int merge_one_ulimit(const oci_runtime_spec *oci_spec, const host_config_ulimits_element *ulimit) { oci_runtime_spec_process_rlimits_element *rlimit = NULL; if (trans_ulimit_to_rlimit(&rlimit, ulimit) != 0) { return -1; } return do_merge_one_ulimit(oci_spec, rlimit); } static int merge_ulimits(oci_runtime_spec *oci_spec, host_config_ulimits_element **ulimits, size_t ulimits_len) { int ret = 0; size_t i = 0; if (oci_spec == NULL || ulimits == NULL || ulimits_len == 0) { return -1; } ret = merge_ulimits_pre(oci_spec, ulimits_len); if (ret < 0) { goto out; } for (i = 0; i < ulimits_len; i++) { ret = merge_one_ulimit(oci_spec, ulimits[i]); if (ret != 0) { ret = -1; goto out; } } out: return ret; } int merge_global_ulimit(oci_runtime_spec *oci_spec) { int ret = 0; host_config_ulimits_element **ulimits = NULL; size_t ulimits_len; if (conf_get_lcrd_default_ulimit(&ulimits) != 0) { ERROR("Failed to get lcrd default ulimit"); ret = -1; goto out; } if (ulimits != NULL) { ulimits_len = ulimit_array_len(ulimits); if (merge_ulimits(oci_spec, ulimits, ulimits_len)) { ret = -1; goto out; } } out: free_default_ulimit(ulimits); return ret; }