| /*****************************************************************************\ | 
 |  *  openapi.c - OpenAPI helpers | 
 |  ***************************************************************************** | 
 |  *  Copyright (C) SchedMD LLC. | 
 |  * | 
 |  *  This file is part of Slurm, a resource management program. | 
 |  *  For details, see <https://slurm.schedmd.com/>. | 
 |  *  Please also read the included file: DISCLAIMER. | 
 |  * | 
 |  *  Slurm is free software; you can redistribute it and/or modify it under | 
 |  *  the terms of the GNU General Public License as published by the Free | 
 |  *  Software Foundation; either version 2 of the License, or (at your option) | 
 |  *  any later version. | 
 |  * | 
 |  *  In addition, as a special exception, the copyright holders give permission | 
 |  *  to link the code of portions of this program with the OpenSSL library under | 
 |  *  certain conditions as described in each individual source file, and | 
 |  *  distribute linked combinations including the two. You must obey the GNU | 
 |  *  General Public License in all respects for all of the code used other than | 
 |  *  OpenSSL. If you modify file(s) with this exception, you may extend this | 
 |  *  exception to your version of the file(s), but you are not obligated to do | 
 |  *  so. If you do not wish to do so, delete this exception statement from your | 
 |  *  version.  If you delete this exception statement from all source files in | 
 |  *  the program, then also delete it here. | 
 |  * | 
 |  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY | 
 |  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 
 |  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more | 
 |  *  details. | 
 |  * | 
 |  *  You should have received a copy of the GNU General Public License along | 
 |  *  with Slurm; if not, write to the Free Software Foundation, Inc., | 
 |  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA. | 
 | \*****************************************************************************/ | 
 |  | 
 | #include "src/common/data.h" | 
 | #include "src/common/openapi.h" | 
 | #include "src/common/xassert.h" | 
 | #include "src/common/xmalloc.h" | 
 | #include "src/common/xstring.h" | 
 |  | 
 | #define MAGIC_FOREACH_PATH 0xaba1aaab | 
 |  | 
 | /* | 
 |  * Based on | 
 |  * https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#data-types | 
 |  */ | 
 | static const struct { | 
 | 	openapi_type_t type; | 
 | 	openapi_type_format_t format; | 
 | 	char *str_type; | 
 | 	char *str_format; | 
 | 	data_type_t data_type; /* compatible data type for this field */ | 
 | } openapi_types[] = { | 
 | 	{ OPENAPI_TYPE_INTEGER, OPENAPI_FORMAT_INT, "integer", NULL, | 
 | 	  DATA_TYPE_INT_64 }, | 
 | 	{ OPENAPI_TYPE_INTEGER, OPENAPI_FORMAT_INT32, "integer", "int32", | 
 | 	  DATA_TYPE_INT_64 }, | 
 | 	{ OPENAPI_TYPE_INTEGER, OPENAPI_FORMAT_INT64, "integer", "int64", | 
 | 	  DATA_TYPE_INT_64 }, | 
 | 	{ OPENAPI_TYPE_NUMBER, OPENAPI_FORMAT_NUMBER, "number", NULL, | 
 | 	  DATA_TYPE_FLOAT }, | 
 | 	{ OPENAPI_TYPE_NUMBER, OPENAPI_FORMAT_FLOAT, "number", "float", | 
 | 	  DATA_TYPE_FLOAT }, | 
 | 	{ OPENAPI_TYPE_NUMBER, OPENAPI_FORMAT_DOUBLE, "number", "double", | 
 | 	  DATA_TYPE_FLOAT }, | 
 | 	{ OPENAPI_TYPE_STRING, OPENAPI_FORMAT_STRING, "string", NULL, | 
 | 	  DATA_TYPE_STRING }, | 
 | 	{ OPENAPI_TYPE_STRING, OPENAPI_FORMAT_PASSWORD, "string", "password", | 
 | 	  DATA_TYPE_STRING }, | 
 | 	{ OPENAPI_TYPE_BOOL, OPENAPI_FORMAT_BOOL, "boolean", NULL, | 
 | 	  DATA_TYPE_BOOL }, | 
 | 	{ OPENAPI_TYPE_OBJECT, OPENAPI_FORMAT_OBJECT, "object", NULL, | 
 | 	  DATA_TYPE_DICT }, | 
 | 	{ OPENAPI_TYPE_ARRAY, OPENAPI_FORMAT_ARRAY, "array", NULL, | 
 | 	  DATA_TYPE_LIST }, | 
 | }; | 
 |  | 
 | typedef struct { | 
 | 	int magic; /* MAGIC_FOREACH_PATH */ | 
 | 	char *path; | 
 | 	char *at; | 
 | } merge_path_strings_t; | 
 |  | 
 | extern const char *openapi_type_format_to_format_string( | 
 | 	openapi_type_format_t format) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].format == format) | 
 | 			return openapi_types[i].str_format; | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | extern const char *openapi_type_format_to_type_string( | 
 | 	openapi_type_format_t format) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].format == format) | 
 | 			return openapi_types[i].str_type; | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | extern const char *openapi_type_to_string(openapi_type_t type) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].type == type) | 
 | 			return openapi_types[i].str_type; | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | extern openapi_type_t openapi_string_to_type(const char *str) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (!xstrcasecmp(openapi_types[i].str_type, str)) | 
 | 			return openapi_types[i].type; | 
 |  | 
 | 	return OPENAPI_TYPE_INVALID; | 
 | } | 
 |  | 
 | extern openapi_type_format_t openapi_string_to_type_format(const char *str) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (!xstrcasecmp(openapi_types[i].str_format, str)) | 
 | 			return openapi_types[i].format; | 
 |  | 
 | 	return OPENAPI_FORMAT_INVALID; | 
 | } | 
 |  | 
 | extern data_type_t openapi_type_format_to_data_type( | 
 | 	openapi_type_format_t format) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].format == format) | 
 | 			return openapi_types[i].data_type; | 
 |  | 
 | 	return DATA_TYPE_NONE; | 
 | } | 
 |  | 
 | extern openapi_type_format_t openapi_data_type_to_type_format(data_type_t type) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].data_type == type) | 
 | 			return openapi_types[i].format; | 
 |  | 
 | 	return OPENAPI_FORMAT_INVALID; | 
 | } | 
 |  | 
 | extern openapi_type_t openapi_type_format_to_type(openapi_type_format_t format) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(openapi_types); i++) | 
 | 		if (openapi_types[i].format == format) | 
 | 			return openapi_types[i].type; | 
 |  | 
 | 	return OPENAPI_TYPE_INVALID; | 
 | } | 
 |  | 
 | static data_for_each_cmd_t _foreach_join_path_str(data_t *data, void *arg) | 
 | { | 
 | 	merge_path_strings_t *args = arg; | 
 |  | 
 | 	xassert(args->magic == MAGIC_FOREACH_PATH); | 
 |  | 
 | 	if (data_convert_type(data, DATA_TYPE_STRING) != DATA_TYPE_STRING) | 
 | 		fatal_abort("%s: path must be a string", __func__); | 
 |  | 
 | 	/* path entry must not contain any of the separators */ | 
 | 	xassert(!xstrstr(data_get_string(data), OPENAPI_PATH_SEP)); | 
 | 	xassert(!xstrstr(data_get_string(data), OPENAPI_PATH_REL)); | 
 |  | 
 | 	xstrfmtcatat(args->path, &args->at, "%s%s", | 
 | 		     data_get_string(data), OPENAPI_PATH_SEP); | 
 |  | 
 | 	return DATA_FOR_EACH_CONT; | 
 | } | 
 |  | 
 | extern char *openapi_fmt_rel_path_str(char **str_ptr, data_t *relative_path) | 
 | { | 
 | 	merge_path_strings_t args = { | 
 | 		.magic = MAGIC_FOREACH_PATH, | 
 | 	}; | 
 |  | 
 | 	xassert(data_get_type(relative_path) == DATA_TYPE_LIST); | 
 | 	if (data_get_type(relative_path) != DATA_TYPE_LIST) | 
 | 		return NULL; | 
 |  | 
 | 	/* path always starts with "#/" */ | 
 | 	xstrfmtcatat(args.path, &args.at, "%s%s", | 
 | 		     OPENAPI_PATH_REL, OPENAPI_PATH_SEP); | 
 |  | 
 | 	(void) data_list_for_each(relative_path, _foreach_join_path_str, &args); | 
 |  | 
 | 	if (*str_ptr) | 
 | 		xfree(*str_ptr); | 
 | 	*str_ptr = args.path; | 
 |  | 
 | 	return args.path; | 
 | } | 
 |  | 
 | extern data_t *openapi_fork_rel_path_list(data_t *relative_path, int index) | 
 | { | 
 | 	data_t *ppath, *ppath_last; | 
 |  | 
 | 	ppath = data_copy(NULL, relative_path); | 
 | 	ppath_last = data_get_list_last(ppath); | 
 |  | 
 | 	/* Use jq style array zero based array notation */ | 
 | 	data_set_string_fmt(ppath_last, "%s[%d]", | 
 | 			    data_get_string(ppath_last), index); | 
 |  | 
 | 	return ppath; | 
 | } | 
 |  | 
 | extern int openapi_append_rel_path(data_t *relative_path, const char *sub_path) | 
 | { | 
 | 	if (data_get_type(relative_path) != DATA_TYPE_LIST) | 
 | 		return ESLURM_DATA_EXPECTED_LIST; | 
 |  | 
 | 	/* ignore empty sub paths */ | 
 | 	if (!sub_path || !sub_path[0]) | 
 | 		return SLURM_SUCCESS; | 
 |  | 
 | 	/* If string starts with # then just ignore it */ | 
 | 	if (sub_path[0] == OPENAPI_PATH_REL[0]) | 
 | 		sub_path = &sub_path[1]; | 
 |  | 
 | 	return data_list_split_str(relative_path, sub_path, OPENAPI_PATH_SEP); | 
 | } | 
 |  | 
 | extern int openapi_error_log_foreach(void *x, void *arg) | 
 | { | 
 | 	openapi_resp_error_t *error_ptr = x; | 
 | 	xassert(error_ptr); | 
 |  | 
 | 	error("source: %s, description: %s, rc: %d", error_ptr->source, | 
 | 	      error_ptr->description, error_ptr->num); | 
 |  | 
 | 	return SLURM_SUCCESS; | 
 | } | 
 |  | 
 | extern int openapi_warn_log_foreach(void *x, void *arg) | 
 | { | 
 | 	openapi_resp_warning_t *warn = x; | 
 | 	xassert(warn); | 
 |  | 
 | 	warning("source: %s, description: %s", warn->source, warn->description); | 
 |  | 
 | 	return SLURM_SUCCESS; | 
 | } | 
 |  | 
 | extern void free_openapi_resp_meta(void *obj) | 
 | { | 
 | 	openapi_resp_meta_t *x = obj; | 
 |  | 
 | 	if (!obj) | 
 | 		return; | 
 |  | 
 | 	xfree(x->client.source); | 
 | 	xfree(x->command); | 
 | 	xfree(x->plugin.accounting_storage); | 
 | 	xfree(x->plugin.data_parser); | 
 | 	xfree(x->plugin.name); | 
 | 	xfree(x->plugin.type); | 
 | 	xfree(x->slurm.cluster); | 
 | 	xfree(x->slurm.release); | 
 | 	xfree(x->slurm.version.major); | 
 | 	xfree(x->slurm.version.micro); | 
 | 	xfree(x->slurm.version.minor); | 
 | 	xfree(x); | 
 | } | 
 |  | 
 | extern void free_openapi_resp_error(void *obj) | 
 | { | 
 | 	openapi_resp_error_t *x = obj; | 
 |  | 
 | 	if (!obj) | 
 | 		return; | 
 |  | 
 | 	xfree(x->description); | 
 | 	xfree(x->source); | 
 | 	xfree(x); | 
 | } | 
 |  | 
 | extern void free_openapi_resp_warning(void *obj) | 
 | { | 
 | 	openapi_resp_warning_t *x = obj; | 
 |  | 
 | 	if (!obj) | 
 | 		return; | 
 |  | 
 | 	xfree(x->description); | 
 | 	xfree(x->source); | 
 | 	xfree(x); | 
 | } |