/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. * clibcni 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: 2019-04-25 * Description: provide conf functions *********************************************************************************/ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "conf.h" #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "log.h" #include "net_conf.h" #include "net_conf_list.h" #include "api.h" static int do_conf_from_bytes(const char *conf_str, struct network_config *config, char **err) { int ret = 0; parser_error jerr = NULL; config->network = net_conf_parse_data(conf_str, NULL, &jerr); if (config->network == NULL) { ret = asprintf(err, "Error parsing configuration: %s", jerr); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Error parsing configuration: %s", jerr); ret = -1; goto out; } if (config->network->name != NULL && util_validate_name(config->network->name) != 0) { ret = asprintf(err, "Invalid network name: %s", config->network->name); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Invalid network name: %s", config->network->name); ret = -1; goto out; } config->bytes = util_strdup_s(conf_str); out: free(jerr); return ret; } static inline bool check_conf_from_bytes_args(struct network_config * const *config, char * const *err) { return (config == NULL || err == NULL); } int conf_from_bytes(const char *conf_str, struct network_config **config, char **err) { int ret = -1; if (check_conf_from_bytes_args(config, err)) { ERROR("Invalid arguments"); return ret; } if (conf_str == NULL) { *err = util_strdup_s("Empty json"); ERROR("Empty json"); return ret; } *config = util_common_calloc_s(sizeof(struct network_config)); if (*config == NULL) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); goto free_out; } ret = do_conf_from_bytes(conf_str, *config, err); free_out: if (ret != 0) { free_network_config(*config); *config = NULL; } return ret; } static char *do_get_net_confs_json(const char *filename, char **err) { size_t filesize = 0; char *content = NULL; content = read_file(filename, &filesize); if (content == NULL) { if (asprintf(err, "Read file %s failed: %s", filename, strerror(errno)) < 0) { *err = util_strdup_s("Read file failed"); } ERROR("Read file %s failed: %s", filename, strerror(errno)); } return content; } static inline bool check_conf_from_file_args(const char *filename, struct network_config * const *config, char * const *err) { return (filename == NULL || config == NULL || err == NULL); } int conf_from_file(const char *filename, struct network_config **config, char **err) { char *content = NULL; int ret = -1; if (check_conf_from_file_args(filename, config, err)) { ERROR("Invalid arguments"); return -1; } content = do_get_net_confs_json(filename, err); if (content == NULL) { ERROR("Parse net conf file: %s failed: %s", filename, *err != NULL ? *err : ""); ret = -1; goto free_out; } ret = conf_from_bytes(content, config, err); free_out: free(content); return ret; } static int do_check_net_conf_list_plugins(const net_conf_list *tmp_list, char **err) { size_t i = 0; if (tmp_list->plugins == NULL) { *err = util_strdup_s("Error parsing configuration list: no 'plugins' key"); ERROR("Error parsing configuration list: no 'plugins' key"); return -1; } if (tmp_list->plugins_len == 0) { *err = util_strdup_s("Error parsing configuration list: no plugins in list"); ERROR("Error parsing configuration list: no plugins in list"); return -1; } for (i = 0; i < tmp_list->plugins_len; i++) { if (tmp_list->plugins[i]->name != NULL && util_validate_name(tmp_list->plugins[i]->name) != 0) { if (asprintf(err, "Invalid network name: %s", tmp_list->plugins[i]->name) < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Invalid network name: %s", tmp_list->plugins[i]->name); return -1; } } return 0; } static int check_net_conf_list(const net_conf_list *tmp_list, char **err) { if (tmp_list->name == NULL) { *err = util_strdup_s("Error parsing configuration list: no name"); ERROR("Name is NULL"); return -1; } if (util_validate_name(tmp_list->name) != 0) { if (asprintf(err, "Invalid network name: %s", tmp_list->name) < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Invalid network name: %s", tmp_list->name); return -1; } return do_check_net_conf_list_plugins(tmp_list, err); } static inline bool check_conflist_from_bytes_args(struct network_config_list * const *list, char * const *err) { return (list == NULL || err == NULL); } int conflist_from_bytes(const char *json_str, struct network_config_list **list, char **err) { int ret = -1; parser_error jerr = NULL; net_conf_list *tmp_list = NULL; if (check_conflist_from_bytes_args(list, err)) { ERROR("Invalid arguments"); return ret; } if (json_str == NULL) { *err = util_strdup_s("Empty json"); ERROR("Empty json"); return -1; } *list = util_common_calloc_s(sizeof(struct network_config_list)); if (*list == NULL) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); goto free_out; } tmp_list = net_conf_list_parse_data(json_str, NULL, &jerr); if (tmp_list == NULL) { ret = asprintf(err, "Error parsing configuration list: %s", jerr); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Error parsing configuration list: %s", jerr); ret = -1; goto free_out; } ret = check_net_conf_list(tmp_list, err); if (ret != 0) { goto free_out; } (*list)->bytes = util_strdup_s(json_str); (*list)->list = tmp_list; ret = 0; free_out: free(jerr); if (ret != 0) { free_net_conf_list(tmp_list); free_network_config_list(*list); *list = NULL; } return ret; } static inline bool check_conflist_from_file_args(const char *filename, struct network_config_list * const *list, char * const *err) { return (filename == NULL || list == NULL || err == NULL); } int conflist_from_file(const char *filename, struct network_config_list **list, char **err) { char *content = NULL; int ret = -1; if (check_conflist_from_file_args(filename, list, err)) { ERROR("Invalid arguments"); return -1; } content = do_get_net_confs_json(filename, err); if (content == NULL) { ERROR("Parse net conf file: %s failed: %s", filename, *err != NULL ? *err : ""); ret = -1; goto free_out; } ret = conflist_from_bytes(content, list, err); free_out: free(content); return ret; } static int get_ext(const char *fname) { int i = 0; int ret = -1; if (fname == NULL) { ERROR("File is NULL"); return -1; } for (i = (int)strlen(fname) - 1; i >= 0; i--) { if (fname[i] == '/') { break; } if (fname[i] == '.') { ret = i; break; } } return ret; } static int check_conf_dir(const char *dir, DIR **directory, char **err) { *directory = opendir(dir); if (*directory == NULL) { if (errno == ENOENT) { return 0; } if (asprintf(err, "Open dir failed: %s", strerror(errno)) < 0) { *err = util_strdup_s("Out of memory"); } SYSERROR("Open dir failed"); return -1; } return 1; } static int do_check_file_is_valid(const char *fname, int *result, char **err) { struct stat tmp_fstat; int nret = -1; nret = lstat(fname, &tmp_fstat); if (nret != 0) { nret = asprintf(err, "lstat %s failed: %s", fname, strerror(errno)); if (nret < 0) { *err = util_strdup_s("Out of memory"); } SYSERROR("lstat %s failed", fname); *result = -1; return -1; } if (S_ISDIR(tmp_fstat.st_mode)) { // ignore dir *result = 0; ERROR("conf file %s is dir", fname); return -1; } if (tmp_fstat.st_size > MB) { nret = asprintf(err, "Too large config file: %s", fname); if (nret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Too large config file: %s", fname); *result = -1; return -1; } return 0; } static int check_conf_file(const char *dir, const char * const *extensions, size_t ext_len, const struct dirent *pdirent, size_t *result_size, char ***result, char **err) { char fname[PATH_MAX] = { 0 }; size_t i = 0; const char *ext_name = NULL; int nret = -1; int ret = 0; size_t cap = *result_size; nret = snprintf(fname, PATH_MAX, "%s/%s", dir, pdirent->d_name); if (nret < 0 || nret >= PATH_MAX) { *err = util_strdup_s("Pathname too long"); ERROR("Pathname too long"); return -1; } nret = do_check_file_is_valid(fname, &ret, err); if (nret != 0) { return ret; } /* compare extension */ nret = get_ext(pdirent->d_name); if (nret < 0) { // ignore this error return 0; } ext_name = (pdirent->d_name) + nret; for (i = 0; i < ext_len; i++) { if (extensions[i] != NULL && strcmp(ext_name, extensions[i]) == 0) { if (util_grow_array(result, &cap, (*result_size) + 1, 2) != 0) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); return -1; } (*result)[(*result_size)++] = util_strdup_s(fname); break; } } return 0; } static inline bool check_conf_files_args(const char *dir, const char * const *extensions, char ** const *result, char * const *err) { return (dir == NULL || extensions == NULL || result == NULL || err == NULL); } int conf_files(const char *dir, const char * const *extensions, size_t ext_len, char ***result, char **err) { #define MAX_FILES 200 int ret = -1; int nret = -1; DIR *directory = NULL; struct dirent *pdirent = NULL; size_t size = 0; if (check_conf_files_args(dir, extensions, result, err)) { ERROR("Invalid arguments"); return -1; } nret = check_conf_dir(dir, &directory, err); if (nret != 1) { /* dir is not exist, just ignore, do not return error */ return nret; } pdirent = readdir(directory); while (pdirent != NULL) { if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) { pdirent = readdir(directory); continue; } nret = check_conf_file(dir, extensions, ext_len, pdirent, &size, result, err); if (nret < 0) { goto free_out; } pdirent = readdir(directory); } if (size > MAX_FILES) { nret = asprintf(err, "Too more config files, current support max count of config file is %d.", MAX_FILES); if (nret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Too more config files, current support max count of config file is %d.", MAX_FILES); ret = -1; goto free_out; } ret = 0; free_out: nret = closedir(directory); if (nret != 0) { if (*err == NULL) { *err = util_strdup_s("Failed to close directory"); SYSERROR("Failed to close directory"); } ret = -1; } if (ret != 0) { util_free_array(*result); *result = NULL; } return ret; } int cmpstr(const void *a, const void *b) { return strcmp(*((const char **)a), *((const char **)b)); } static inline bool check_load_conf_args(const char *dir, const char *name, struct network_config * const *conf, char * const *err) { return (dir == NULL || name == NULL || conf == NULL || err == NULL); } int load_conf(const char *dir, const char *name, struct network_config **conf, char **err) { char **files = NULL; const char *exts[] = { ".conf", ".json" }; int ret = 0; size_t len = 0; size_t i = 0; if (check_load_conf_args(dir, name, conf, err)) { ERROR("Invalid arguments"); return -1; } ret = conf_files(dir, exts, sizeof(exts) / sizeof(char *), &files, err); if (ret != 0) { return -1; } len = util_array_len((const char * const *)files); if (len == 0) { if (asprintf(err, "no net configurations found in %s", dir) < 0) { *err = util_strdup_s("Out of memory"); } ERROR("no net configurations found in %s", dir); goto free_out; } qsort((void *)files, len, sizeof(char *), cmpstr); for (i = 0; i < len; i++) { ret = conf_from_file(files[i], conf, err); if (ret != 0) { goto free_out; } if (((*conf)->network->name) != NULL && strcmp((*conf)->network->name, name) == 0) { ret = 0; goto free_out; } free_network_config(*conf); *conf = NULL; } ret = asprintf(err, "No net configuration with name \"%s\" in %s", name, dir); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("No net configuration with name \"%s\" in %s", name, dir); ret = -1; free_out: util_free_array(files); return ret; } static int generate_new_conflist(const net_conf_list *list, struct network_config_list **conf_list, char **err) { struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 }; parser_error jerr = NULL; char *net_conf_json_str = NULL; int ret = -1; net_conf_json_str = net_conf_list_generate_json(list, &ctx, &jerr); if (net_conf_json_str == NULL) { ret = asprintf(err, "Generate conf list json failed: %s", jerr); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Generate conf list json failed: %s", jerr); goto free_out; } free(jerr); jerr = NULL; (*conf_list)->bytes = net_conf_json_str; (*conf_list)->list = net_conf_list_parse_data(net_conf_json_str, &ctx, &jerr); if ((*conf_list)->list == NULL) { ret = asprintf(err, "Parse conf list from json failed: %s", jerr); if (ret < 0) { *err = util_strdup_s("Out of memory"); } ERROR("Parse conf list from json failed: %s", jerr); goto free_out; } ret = 0; free_out: free(jerr); return ret; } static inline bool check_conflist_from_conf_args(const struct network_config *conf, struct network_config_list * const *conf_list, char * const *err) { return (conf == NULL || conf->network == NULL || conf_list == NULL || err == NULL); } int conflist_from_conf(const struct network_config *conf, struct network_config_list **conf_list, char **err) { int ret = -1; net_conf_list *list = NULL; if (check_conflist_from_conf_args(conf, conf_list, err)) { ERROR("Invalid arguments"); return -1; } *conf_list = util_common_calloc_s(sizeof(struct network_config_list)); if (*conf_list == NULL) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); return -1; } list = util_common_calloc_s(sizeof(net_conf_list)); if (list == NULL) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); goto free_out; } list->plugins = util_common_calloc_s(sizeof(net_conf *) * (1 + 1)); if (list->plugins == NULL) { *err = util_strdup_s("Out of memory"); ERROR("Out of memory"); goto free_out; } list->plugins[0] = conf->network; list->plugins_len = 1; if (conf->network->cni_version != NULL) { list->cni_version = util_strdup_s(conf->network->cni_version); } if (conf->network->name != NULL) { list->name = util_strdup_s(conf->network->name); } ret = generate_new_conflist(list, conf_list, err); free_out: if (list != NULL && list->plugins != NULL) { list->plugins_len = 0; list->plugins[0] = NULL; } free_net_conf_list(list); if (ret != 0) { free_network_config_list(*conf_list); *conf_list = NULL; } return ret; } void free_network_config(struct network_config *config) { if (config != NULL) { free_net_conf(config->network); config->network = NULL; free(config->bytes); config->bytes = NULL; free(config); } } void free_network_config_list(struct network_config_list *conf_list) { if (conf_list != NULL) { free_net_conf_list(conf_list->list); conf_list->list = NULL; free(conf_list->bytes); conf_list->bytes = NULL; free(conf_list); } }