642 lines
18 KiB
C
642 lines
18 KiB
C
/******************************************************************************
|
|
* 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 <linux/limits.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <read_file.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#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);
|
|
}
|
|
}
|
|
|