| /*****************************************************************************\ |
| * parsers.c - Slurm data parsers |
| ***************************************************************************** |
| * 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/log.h" |
| #include "src/common/read_config.h" |
| #include "src/common/xassert.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/xstring.h" |
| |
| #include "alloc.h" |
| #include "api.h" |
| #include "events.h" |
| #include "openapi.h" |
| #include "parsers.h" |
| #include "parsing.h" |
| #include "slurmdb_helpers.h" |
| |
| #define MAGIC_FOREACH_LIST_FLAG 0xa1d4acd2 |
| #define MAGIC_FOREACH_LIST 0xaefa2af3 |
| #define MAGIC_FOREACH_NT_ARRAY 0xaba1be2b |
| #define MAGIC_FOREACH_PARSE_MARRAY 0xa081be2b |
| |
| typedef struct { |
| int magic; |
| ssize_t index; |
| args_t *args; |
| const parser_t *const parser; |
| list_t *list; |
| data_t *dlist; |
| data_t *parent_path; |
| } foreach_list_t; |
| |
| typedef struct { |
| int magic; /* MAGIC_FOREACH_LIST_FLAG */ |
| args_t *args; |
| const parser_t *const parser; |
| void *dst; /* already has offset applied */ |
| data_t *parent_path; |
| ssize_t index; |
| uint64_t set; |
| } foreach_flag_parser_args_t; |
| |
| typedef struct { |
| int magic; /* MAGIC_FOREACH_NT_ARRAY */ |
| void **array; /* array of pointers to objects */ |
| void *sarray; /* array of objects */ |
| int index; |
| const parser_t *const parser; |
| const parser_t *const array_parser; |
| args_t *args; |
| data_t *parent_path; |
| } foreach_nt_array_t; |
| |
| typedef struct { |
| int magic; /* MAGIC_FOREACH_PARSE_MARRAY */ |
| args_t *args; |
| const parser_t *const array; |
| data_t *parent_path; |
| data_t *path; |
| } parse_marray_args_t; |
| |
| static void _set_flag_bit(const parser_t *const parser, void *dst, |
| const flag_bit_t *bit, bool matched, const char *path, |
| data_t *src) |
| { |
| /* C allows compiler to choose a size for the enum */ |
| if (parser->size == sizeof(uint64_t)) { |
| uint64_t *flags = dst; |
| if (matched) |
| *flags |= bit->mask & bit->value; |
| else |
| *flags &= ~bit->mask | (bit->mask & ~bit->value); |
| } else if (parser->size == sizeof(uint32_t)) { |
| uint32_t *flags = dst; |
| if (matched) |
| *flags |= bit->mask & bit->value; |
| else |
| *flags &= ~bit->mask | (bit->mask & ~bit->value); |
| } else if (parser->size == sizeof(uint16_t)) { |
| uint16_t *flags = dst; |
| if (matched) |
| *flags |= bit->mask & bit->value; |
| else |
| *flags &= ~bit->mask | (bit->mask & ~bit->value); |
| } else if (parser->size == sizeof(uint8_t)) { |
| uint8_t *flags = dst; |
| if (matched) |
| *flags |= bit->mask & bit->value; |
| else |
| *flags &= ~bit->mask | (bit->mask & ~bit->value); |
| } else { |
| fatal_abort("%s: unexpected enum size: %zu", |
| __func__, parser->size); |
| } |
| } |
| |
| static void _set_flag_bit_equal(const parser_t *const parser, void *dst, |
| const flag_bit_t *bit, bool matched, |
| const char *path, data_t *src) |
| { |
| /* C allows compiler to choose a size for the enum |
| * |
| * If the comparison is all or nothing, then clear all the masked bits |
| * if it doesn't match which means parser order matters with these. |
| */ |
| if (parser->size == sizeof(uint64_t)) { |
| uint64_t *flags = dst; |
| if (matched) |
| *flags = (*flags & ~bit->mask) | |
| (bit->mask & bit->value); |
| else |
| *flags &= ~bit->mask; |
| } else if (parser->size == sizeof(uint32_t)) { |
| uint32_t *flags = dst; |
| if (matched) |
| *flags = (*flags & ~bit->mask) | |
| (bit->mask & bit->value); |
| else |
| *flags &= ~bit->mask; |
| } else if (parser->size == sizeof(uint16_t)) { |
| uint16_t *flags = dst; |
| if (matched) |
| *flags = (*flags & ~bit->mask) | |
| (bit->mask & bit->value); |
| else |
| *flags &= ~bit->mask; |
| } else if (parser->size == sizeof(uint8_t)) { |
| uint8_t *flags = dst; |
| if (matched) |
| *flags = (*flags & ~bit->mask) | |
| (bit->mask & bit->value); |
| else |
| *flags &= ~bit->mask; |
| } else { |
| fatal_abort("%s: unexpected enum size: %zu", |
| __func__, parser->size); |
| } |
| } |
| |
| static char *_flag_parent_path(char **path_ptr, |
| foreach_flag_parser_args_t *args) |
| { |
| data_t *ppath; |
| |
| if (*path_ptr) |
| return *path_ptr; |
| |
| if (is_fast_mode(args->args)) |
| return NULL; |
| |
| ppath = clone_source_path_index(args->parent_path, args->index); |
| set_source_path(path_ptr, args->args, ppath); |
| FREE_NULL_DATA(ppath); |
| |
| return *path_ptr; |
| } |
| |
| static data_for_each_cmd_t _foreach_flag_parser(data_t *src, void *arg) |
| { |
| foreach_flag_parser_args_t *args = arg; |
| void *dst = args->dst; |
| const parser_t *const parser = args->parser; |
| char *path = NULL; |
| bool matched_any = false; |
| data_for_each_cmd_t rc = DATA_FOR_EACH_CONT; |
| |
| xassert(args->magic == MAGIC_FOREACH_LIST_FLAG); |
| xassert(args->args->magic == MAGIC_ARGS); |
| xassert(parser->magic == MAGIC_PARSER); |
| |
| path = _flag_parent_path(&path, args); |
| |
| if (data_convert_type(src, DATA_TYPE_STRING) != DATA_TYPE_STRING) { |
| on_warn(PARSING, parser->type, args->args, path, |
| __func__, "Ignoring unexpected field of type %s", |
| data_get_type_string(src)); |
| goto cleanup; |
| } |
| |
| for (int8_t i = 0; (i < parser->flag_bit_array_count); i++) { |
| const flag_bit_t *bit = &parser->flag_bit_array[i]; |
| bool matched = !xstrcasecmp(data_get_string(src), bit->name); |
| |
| if (matched) |
| matched_any = true; |
| |
| if (bit->type == FLAG_BIT_TYPE_BIT) { |
| uint64_t value = (bit->mask & bit->value); |
| |
| if (matched || (~args->set & value) == value) |
| _set_flag_bit(parser, dst, bit, matched, path, src); |
| args->set |= value; |
| } else if (bit->type == FLAG_BIT_TYPE_EQUAL) { |
| if (matched || ((~args->set & bit->mask) == bit->mask)) |
| _set_flag_bit_equal(parser, dst, bit, matched, |
| path, src); |
| args->set |= bit->mask; |
| } else if (bit->type == FLAG_BIT_TYPE_REMOVED) { |
| if (matched) |
| on_warn(PARSING, parser->type, args->args, path, |
| __func__, |
| "Ignoring deprecated flag: %s", |
| bit->name); |
| } else |
| fatal_abort("%s: invalid bit_flag_t", __func__); |
| |
| } |
| |
| if (!matched_any) |
| on_error(PARSING, parser->type, args->args, |
| ESLURM_DATA_FLAGS_INVALID, path, __func__, |
| "Unknown flag \"%s\"", data_get_string(src)); |
| |
| cleanup: |
| xfree(path); |
| args->index++; |
| return rc; |
| } |
| |
| static int _parse_flag(void *dst, const parser_t *const parser, data_t *src, |
| args_t *args, data_t *parent_path) |
| { |
| int rc = SLURM_SUCCESS; |
| char *path = NULL; |
| foreach_flag_parser_args_t fargs = { |
| .magic = MAGIC_FOREACH_LIST_FLAG, |
| .args = args, |
| .parser = parser, |
| .dst = dst, |
| .parent_path = parent_path, |
| }; |
| |
| xassert(args->magic == MAGIC_ARGS); |
| xassert(parser->magic == MAGIC_PARSER); |
| xassert(parser->ptr_offset == NO_VAL); |
| xassert(parser->model == PARSER_MODEL_FLAG_ARRAY); |
| |
| if (data_get_type(src) == DATA_TYPE_STRING) { |
| /* List item may just be a single flag */ |
| if (_foreach_flag_parser(src, &fargs) != DATA_FOR_EACH_CONT) { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_FLAGS_INVALID, |
| set_source_path(&path, args, parent_path), |
| __func__, |
| "Parsing single flag \"%s\" failed", |
| data_get_string(src)); |
| |
| goto cleanup; |
| } |
| } else if (data_get_type(src) != DATA_TYPE_LIST) { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_FLAGS_INVALID_TYPE, |
| set_source_path(&path, args, parent_path), |
| __func__, "Expected a List but found a %s", |
| data_get_type_string(src)); |
| |
| goto cleanup; |
| /* |
| * Flags need special handling as they are always a LIST with a |
| * matching string value. This requires that each possible flag |
| * must be searched for in the list to know if it is there or |
| * not. |
| */ |
| } else if (data_list_for_each(src, _foreach_flag_parser, &fargs) < 0) { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_FLAGS_INVALID, |
| set_source_path(&path, args, parent_path), |
| __func__, "Parsing flags failed"); |
| goto cleanup; |
| } |
| |
| cleanup: |
| xfree(path); |
| return rc; |
| } |
| |
| static data_for_each_cmd_t _foreach_parse_list(data_t *src, void *arg) |
| { |
| int rc; |
| foreach_list_t *args = arg; |
| const parser_t *const parser = args->parser; |
| const parser_t *const lparser = find_parser_by_type(parser->list_type); |
| void *obj = NULL; |
| data_t *ppath = NULL; |
| |
| xassert(args->magic == MAGIC_FOREACH_LIST); |
| check_parser(parser); |
| check_parser(lparser); |
| xassert(!args->dlist); /* only for dumping */ |
| xassert((args->index > 0) || (args->index == -1)); |
| |
| if (args->index < 0) |
| args->index = 0; |
| |
| if (!is_fast_mode(args->args)) { |
| data_t *ppath_last; |
| |
| ppath = data_copy(NULL, args->parent_path); |
| ppath_last = data_get_list_last(ppath); |
| |
| /* Use jq style array zero based array notation */ |
| data_set_string_fmt(ppath_last, "%s[%zu]", |
| data_get_string(ppath_last), args->index); |
| } |
| |
| if ((rc = parse(&obj, NO_VAL, lparser, src, args->args, ppath))) { |
| FREE_NULL_DATA(ppath); |
| return DATA_FOR_EACH_FAIL; |
| } |
| |
| args->index++; |
| list_append(args->list, obj); |
| |
| FREE_NULL_DATA(ppath); |
| return DATA_FOR_EACH_CONT; |
| } |
| |
| static int _parse_list(const parser_t *const parser, void *dst, data_t *src, |
| args_t *args, data_t *parent_path) |
| { |
| int rc = SLURM_SUCCESS; |
| char *path = NULL; |
| list_t **list_ptr = dst; |
| foreach_list_t list_args = { |
| .magic = MAGIC_FOREACH_LIST, |
| .dlist = NULL, |
| .args = args, |
| .parser = parser, |
| .parent_path = parent_path, |
| .index = -1, |
| }; |
| |
| xassert(!*list_ptr || (list_count(*list_ptr) >= 0)); |
| xassert(args->magic == MAGIC_ARGS); |
| check_parser(parser); |
| |
| SWAP(*list_ptr, list_args.list); |
| |
| log_flag(DATA, "%s: BEGIN: list parsing %s{%s(0x%"PRIxPTR")} to List 0x%"PRIxPTR" via parser %s(0x%"PRIxPTR")", |
| __func__, set_source_path(&path, args, parent_path), |
| data_get_type_string(src), (uintptr_t) src, |
| (uintptr_t) dst, parser->type_string, (uintptr_t) parser |
| ); |
| |
| if (!list_args.list) { |
| const parser_t *const lparser = |
| find_parser_by_type(parser->list_type); |
| list_args.list = list_create((ListDelF) lparser->free); |
| } |
| |
| xassert(list_count(list_args.list) >= 0); |
| |
| if (data_get_type(src) == DATA_TYPE_LIST) { |
| if (data_list_for_each(src, _foreach_parse_list, |
| &list_args) < 0) |
| rc = ESLURM_REST_FAIL_PARSING; |
| } else if (data_convert_type(src, DATA_TYPE_STRING) == |
| DATA_TYPE_STRING) { |
| /* Assume List is just a single entry */ |
| if (_foreach_parse_list(src, &list_args) != DATA_FOR_EACH_CONT) |
| rc = ESLURM_REST_FAIL_PARSING; |
| } else { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_EXPECTED_LIST, |
| set_source_path(&path, args, parent_path), |
| __func__, "Expected List but found a %s", |
| data_get_type_string(src)); |
| } |
| |
| if (!rc) |
| SWAP(*list_ptr, list_args.list); |
| else |
| FREE_NULL_LIST(list_args.list); |
| |
| log_flag(DATA, "%s: END: list parsing %s{%s(0x%"PRIxPTR")} to List 0x%"PRIxPTR" via parser %s(0x%"PRIxPTR") rc[%d]:%s", |
| __func__, path, data_get_type_string(src), |
| (uintptr_t) src, (uintptr_t) dst, parser->type_string, |
| (uintptr_t) parser, rc, slurm_strerror(rc) |
| ); |
| |
| xfree(path); |
| return rc; |
| } |
| |
| static int _parse_pointer(const parser_t *const parser, void *dst, data_t *src, |
| args_t *args, data_t *parent_path) |
| { |
| int rc; |
| void **ptr = dst; |
| const parser_t *const pt = find_parser_by_type(parser->pointer_type); |
| bool is_empty_dict = (pt->obj_openapi == OPENAPI_FORMAT_OBJECT) && |
| (data_get_type(src) == DATA_TYPE_DICT) && |
| !data_get_dict_length(src); |
| bool is_empty_list = (pt->obj_openapi == OPENAPI_FORMAT_ARRAY) && |
| (data_get_type(src) == DATA_TYPE_LIST) && |
| !data_get_list_length(src); |
| |
| xassert(!*ptr); |
| *ptr = alloc_parser_obj(parser); |
| |
| if (is_empty_dict || is_empty_list) { |
| /* |
| * Detect work around for OpenAPI clients being unable to handle |
| * a null in place of a object/array by placing an empty |
| * dict/array. Place the default allocated object pointer but |
| + * skip attempting to parse. |
| */ |
| return SLURM_SUCCESS; |
| } |
| |
| if ((rc = parse(*ptr, NO_VAL, pt, src, args, parent_path))) { |
| log_flag(DATA, "%s object at 0x%"PRIxPTR" freed due to parser error: %s", |
| pt->obj_type_string, (uintptr_t) ptr, |
| slurm_strerror(rc)); |
| free_parser_obj(parser, *ptr); |
| *ptr = NULL; |
| } |
| |
| return rc; |
| } |
| |
| static data_for_each_cmd_t _foreach_array_entry(data_t *src, void *arg) |
| { |
| int rc; |
| foreach_nt_array_t *args = arg; |
| void *obj = NULL; |
| data_t *ppath = NULL; |
| const parser_t *const parser = args->parser; |
| const parser_t *const array_parser = args->array_parser; |
| |
| xassert(args->magic == MAGIC_FOREACH_NT_ARRAY); |
| xassert((args->index > 0) || (args->index == -1)); |
| |
| if (args->index < 0) |
| args->index = 0; |
| |
| if (!is_fast_mode(args->args)) { |
| data_t *ppath_last; |
| |
| ppath = data_copy(NULL, args->parent_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), args->index); |
| } |
| |
| if (array_parser->model == PARSER_MODEL_NT_PTR_ARRAY) |
| obj = alloc_parser_obj(parser); |
| else if (array_parser->model == PARSER_MODEL_NT_ARRAY) |
| obj = args->sarray + (parser->size * args->index); |
| |
| if ((rc = parse(obj, NO_VAL, parser, src, args->args, ppath))) { |
| log_flag(DATA, "%s object at 0x%"PRIxPTR" freed due to parser error: %s", |
| parser->obj_type_string, (uintptr_t) obj, |
| slurm_strerror(rc)); |
| if (array_parser->model == PARSER_MODEL_NT_PTR_ARRAY) |
| free_parser_obj(parser, obj); |
| FREE_NULL_DATA(ppath); |
| return DATA_FOR_EACH_FAIL; |
| } |
| |
| if (array_parser->model == PARSER_MODEL_NT_PTR_ARRAY) { |
| xassert(!args->array[args->index]); |
| args->array[args->index] = obj; |
| } |
| |
| args->index++; |
| |
| FREE_NULL_DATA(ppath); |
| return DATA_FOR_EACH_CONT; |
| } |
| |
| static int _parse_nt_array(const parser_t *const parser, void *dst, data_t *src, |
| args_t *args, data_t *parent_path) |
| { |
| int rc = SLURM_SUCCESS; |
| foreach_nt_array_t fargs = { |
| .magic = MAGIC_FOREACH_NT_ARRAY, |
| .array_parser = parser, |
| .parser = find_parser_by_type(parser->array_type), |
| .args = args, |
| .parent_path = parent_path, |
| .index = -1, |
| }; |
| char *path = NULL; |
| |
| xassert(args->magic == MAGIC_ARGS); |
| |
| if ((data_get_type(src) != DATA_TYPE_LIST) && |
| (data_convert_type(src, DATA_TYPE_STRING) != DATA_TYPE_STRING)) { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_EXPECTED_LIST, |
| set_source_path(&path, args, parent_path), |
| __func__, "Expected List but found a %s", |
| data_get_type_string(src)); |
| goto cleanup; |
| } |
| |
| /* assume list can parse all entries */ |
| if (parser->model == PARSER_MODEL_NT_PTR_ARRAY) |
| fargs.array = xcalloc(data_get_list_length(src) + 1, |
| sizeof(*fargs.array)); |
| else if (parser->model == PARSER_MODEL_NT_ARRAY) |
| fargs.sarray = xcalloc(data_get_list_length(src) + 1, |
| fargs.parser->size); |
| |
| /* verify new array actually allocated */; |
| xassert((fargs.array && (xsize(fargs.array) > 0)) ^ |
| (fargs.sarray && (xsize(fargs.sarray) > 0))); |
| |
| if (data_get_type(src) == DATA_TYPE_LIST) { |
| if (data_list_for_each(src, _foreach_array_entry, &fargs) < 0) |
| goto cleanup; |
| } else if (data_get_type(src) == DATA_TYPE_STRING) { |
| /* Assume List is just a single entry */ |
| if (_foreach_array_entry(src, &fargs) != DATA_FOR_EACH_CONT) |
| rc = ESLURM_REST_FAIL_PARSING; |
| } |
| |
| if (parser->model == PARSER_MODEL_NT_PTR_ARRAY) { |
| void ***array_ptr = dst; |
| xassert(!*array_ptr); |
| SWAP(*array_ptr, fargs.array); |
| } else if (parser->model == PARSER_MODEL_NT_ARRAY) { |
| void **array_ptr = dst; |
| xassert(!*array_ptr); |
| SWAP(*array_ptr, fargs.sarray); |
| } |
| |
| cleanup: |
| xfree(path); |
| |
| if (fargs.array) { |
| /* must have failed - cleanup the memory */ |
| for (int i = 0; fargs.array[i]; i++) |
| free_parser_obj(parser, &fargs.array[i]); |
| xfree(fargs.array); |
| } else if (fargs.sarray) { |
| xfree(fargs.sarray); |
| } |
| |
| return rc; |
| } |
| |
| static void _parser_linked_flag(args_t *args, const parser_t *const array, |
| const parser_t *const parser, data_t *src, |
| void *dst, data_t *parent_path, |
| const flag_bit_t *bit, uint64_t *set) |
| { |
| char *path = NULL; |
| data_t *ppath = NULL; |
| bool matched; |
| |
| src = data_resolve_dict_path(src, bit->name); |
| |
| if (!is_fast_mode(args)) { |
| ppath = data_copy(NULL, parent_path); |
| openapi_append_rel_path(ppath, bit->name); |
| set_source_path(&path, args, ppath); |
| } |
| |
| if (!src) { |
| matched = false; |
| } else if (data_convert_type(src, DATA_TYPE_BOOL) != DATA_TYPE_BOOL) { |
| matched = false; |
| |
| on_warn(PARSING, parser->type, args, path, __func__, |
| "Unable to convert to boolean from %s. Flag %s is being treated as false.", |
| data_get_type_string(src), bit->name); |
| |
| } else { |
| matched = data_get_bool(src); |
| } |
| |
| if (bit->type == FLAG_BIT_TYPE_BIT) |
| _set_flag_bit(parser, dst, bit, matched, path, src); |
| else if (bit->type == FLAG_BIT_TYPE_EQUAL) { |
| if (matched || ((~(*set) & bit->mask) == bit->mask)) |
| _set_flag_bit_equal(parser, dst, bit, matched, path, |
| src); |
| *set |= bit->mask; |
| } else if (bit->type == FLAG_BIT_TYPE_REMOVED) { |
| if (matched && !is_fast_mode(args)) |
| on_warn(PARSING, parser->type, args, path, __func__, |
| "Ignoring deprecated flag: %s", bit->name); |
| } else { |
| fatal_abort("%s: invalid bit_flag_t", __func__); |
| } |
| |
| log_flag(DATA, "%s: parsed flag %s{%s(0x%" PRIxPTR ")} to %s(0x%" PRIxPTR "+%zd)->%s & 0x%"PRIx64" & %s=0x%"PRIx64" via array parser %s(0x%" PRIxPTR ")=%s(0x%" PRIxPTR ")", |
| __func__, path, data_get_type_string(src), |
| (uintptr_t) src, array->obj_type_string, (uintptr_t) dst, |
| parser->ptr_offset, parser->field_name, bit->mask, |
| bit->flag_name, bit->value, parser->obj_type_string, |
| (uintptr_t) parser, array->type_string, (uintptr_t) array); |
| |
| xfree(path); |
| FREE_NULL_DATA(ppath); |
| } |
| |
| static data_for_each_cmd_t _foreach_parse_marray(const char *key, data_t *data, |
| void *arg) |
| { |
| parse_marray_args_t *aargs = arg; |
| parse_marray_args_t cargs = *aargs; |
| args_t *args = aargs->args; |
| const parser_t *const array = aargs->array; |
| char *path = NULL; |
| |
| xassert(aargs->magic == MAGIC_FOREACH_PARSE_MARRAY); |
| xassert(cargs.magic == MAGIC_FOREACH_PARSE_MARRAY); |
| xassert(array->model == PARSER_MODEL_ARRAY); |
| xassert(!is_fast_mode(args)); |
| |
| cargs.parent_path = data_copy(NULL, aargs->parent_path); |
| openapi_append_rel_path(cargs.parent_path, key); |
| |
| cargs.path = data_copy(NULL, aargs->path); |
| data_set_string(data_list_append(cargs.path), key); |
| |
| for (int i = 0; i < array->field_count; i++) { |
| bool match; |
| const parser_t *const parser = &array->fields[i]; |
| data_t *fpath; |
| |
| if (parser->model == PARSER_MODEL_ARRAY_SKIP_FIELD) |
| continue; |
| |
| if (parser->model == |
| PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD) { |
| const parser_t *const fp = |
| find_parser_by_type(parser->type); |
| |
| for (int i = 0; i < fp->flag_bit_array_count; i++) { |
| const flag_bit_t *bit = &fp->flag_bit_array[i]; |
| |
| if (!xstrcasecmp(key, bit->name)) { |
| if (slurm_conf.debug_flags & |
| DEBUG_FLAG_DATA) { |
| char *p = NULL; |
| data_list_join_str(&p, |
| cargs.path, |
| "/"); |
| |
| log_flag(DATA, "%s: matched %s as bitflag %s", |
| __func__, p, bit->name); |
| |
| xfree(p); |
| } |
| goto cleanup; |
| } |
| } |
| } |
| |
| fpath = data_new(); |
| (void) data_list_split_str(fpath, parser->key, "/"); |
| match = data_check_match(fpath, cargs.path, false); |
| FREE_NULL_DATA(fpath); |
| |
| if (match) { |
| if (slurm_conf.debug_flags & DEBUG_FLAG_DATA) { |
| char *p = NULL; |
| data_list_join_str(&p, cargs.path, "/"); |
| log_flag(DATA, "%s: matched %s to %s", |
| __func__, p, parser->key); |
| xfree(p); |
| } |
| goto cleanup; |
| } |
| } |
| |
| if (data_get_type(data) == DATA_TYPE_DICT) { |
| /* still unknown, so try next level of tree */ |
| (void) data_dict_for_each(data, _foreach_parse_marray, &cargs); |
| goto cleanup; |
| } |
| |
| on_warn(PARSING, array->type, args, |
| set_source_path(&path, args, cargs.parent_path), |
| __func__, "Ignoring unknown field \"%s\" of type %s in %s", key, |
| data_get_type_string(data), array->type_string); |
| |
| cleanup: |
| FREE_NULL_DATA(cargs.path); |
| FREE_NULL_DATA(cargs.parent_path); |
| xfree(path); |
| return DATA_FOR_EACH_CONT; |
| } |
| |
| /* |
| * Try to guess if the user actually set the value or if the value was dumped |
| * with the other oversubsribed values which means it doesn't need to be logged |
| */ |
| static int _is_duplicate_linked_parser_value(args_t *args, |
| const parser_t *const array, |
| const parser_t *const parser, |
| data_t *src_obj, data_t *src, |
| data_t *parent_path) |
| { |
| if (parser->field_name_overloads == 1) |
| return false; |
| |
| for (int i = 0; i < array->field_count; i++) { |
| if ((array->fields[i].field_name_overloads != 1) && |
| !xstrcmp(array->fields[i].field_name, parser->field_name) && |
| !data_check_match(src, |
| data_key_get(src_obj, |
| array->fields[i].key), |
| false)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* parser linked parser inside of parser array */ |
| static int _parser_linked(args_t *args, const parser_t *const array, |
| const parser_t *const parser, data_t *src, void *dst, |
| data_t *parent_path) |
| { |
| int rc = SLURM_ERROR; |
| data_t *ppath = NULL, *src_obj = src; |
| char *path = NULL; |
| |
| check_parser(parser); |
| verify_parser_sliced(parser); |
| |
| if (parser->model == |
| PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD) { |
| const parser_t *const fp = find_parser_by_type(parser->type); |
| uint64_t set = 0; |
| |
| rc = SLURM_SUCCESS; |
| |
| if (parser->ptr_offset != NO_VAL) |
| dst += parser->ptr_offset; |
| |
| for (int i = 0; i < fp->flag_bit_array_count; i++) { |
| const flag_bit_t *bit = &fp->flag_bit_array[i]; |
| |
| _parser_linked_flag(args, array, fp, src, dst, |
| parent_path, bit, &set); |
| } |
| |
| goto cleanup; |
| } |
| |
| if (!is_fast_mode(args)) |
| ppath = data_copy(NULL, parent_path); |
| |
| /* only look for child via key if there was one defined */ |
| if (parser->key) { |
| src = data_resolve_dict_path(src, parser->key); |
| |
| if (!is_fast_mode(args)) |
| openapi_append_rel_path(ppath, parser->key); |
| } |
| |
| if (parser->model == PARSER_MODEL_ARRAY_REMOVED_FIELD) { |
| log_flag(DATA, "%s: skip parsing removed %s object %s(0x%" PRIxPTR ") via parser %s(0x%" PRIxPTR ")", |
| __func__, set_source_path(&path, args, ppath), |
| parser->obj_type_string, (uintptr_t) dst, |
| parser->type_string, (uintptr_t) src); |
| rc = SLURM_SUCCESS; |
| goto cleanup; |
| } |
| |
| if (!src) { |
| if (parser->required) { |
| if ((rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_PATH_NOT_FOUND, |
| set_source_path(&path, args, ppath), |
| __func__, |
| "Missing required field '%s' in dictionary", |
| parser->key))) |
| goto cleanup; |
| } else { |
| /* field is missing but not required */ |
| log_flag(DATA, "%s: skip parsing missing %s to object %s(0x%" PRIxPTR "+%zd)%s%s via parser %s(0x%" PRIxPTR ")", |
| __func__, path, parser->obj_type_string, |
| (uintptr_t) dst, |
| (parser->ptr_offset == NO_VAL ? |
| 0 : |
| parser->ptr_offset), |
| (parser->field_name ? "->" : ""), |
| (parser->field_name ? parser->field_name : ""), |
| parser->type_string, (uintptr_t) src); |
| |
| rc = SLURM_SUCCESS; |
| goto cleanup; |
| } |
| } |
| |
| if (parser->ptr_offset != NO_VAL) |
| dst += parser->ptr_offset; |
| |
| if (parser->model == PARSER_MODEL_ARRAY_SKIP_FIELD) { |
| log_flag(DATA, "%s: SKIP: parsing %s{%s(0x%" PRIxPTR ")} to %s(0x%" PRIxPTR "+%zd)%s%s=%s(0x%" PRIxPTR ") via array parser %s(0x%" PRIxPTR ")=%s(0x%" PRIxPTR ")", |
| __func__, parser->field_name, |
| data_get_type_string(src), |
| (uintptr_t) src, parser->obj_type_string, |
| (uintptr_t) dst, parser->ptr_offset, |
| (parser->field_name ? "->" : ""), |
| (parser->field_name ? parser->field_name : ""), |
| parser->obj_type_string, (uintptr_t) src, |
| parser->type_string, (uintptr_t) array, |
| parser->type_string, (uintptr_t) parser); |
| rc = SLURM_SUCCESS; |
| goto cleanup; |
| } |
| |
| xassert(parser->model == PARSER_MODEL_ARRAY_LINKED_FIELD); |
| |
| if (!is_fast_mode(args) && parser->deprecated && |
| (parser->deprecated <= SLURM_MIN_PROTOCOL_VERSION) && |
| !_is_duplicate_linked_parser_value(args, array, parser, src_obj, |
| src, parent_path)) { |
| on_warn(PARSING, parser->type, args, |
| set_source_path(&path, args, ppath), __func__, |
| "Field \"%s\" is deprecated", parser->key); |
| } |
| |
| log_flag(DATA, "%s: BEGIN: parsing %s{%s(0x%" PRIxPTR ")} to %s(0x%" PRIxPTR "+%zd)%s%s=%s(0x%" PRIxPTR ") via array parser %s(0x%" PRIxPTR ")=%s(0x%" PRIxPTR ")", |
| __func__, path, data_get_type_string(src), |
| (uintptr_t) src, array->obj_type_string, (uintptr_t) dst, |
| array->ptr_offset, (array->field_name ? "->" : ""), |
| (array->field_name ? array->field_name : ""), |
| parser->obj_type_string, (uintptr_t) src, array->type_string, |
| (uintptr_t) array, parser->type_string, (uintptr_t) parser); |
| |
| rc = parse(dst, NO_VAL, find_parser_by_type(parser->type), src, args, |
| ppath); |
| |
| log_flag(DATA, "%s: END: parsing %s{%s(0x%" PRIxPTR ")} to %s(0x%" PRIxPTR "+%zd)%s%s=%s(0x%" PRIxPTR ") via array parser %s(0x%" PRIxPTR ")=%s(0x%" PRIxPTR ") rc[%d]:%s", |
| __func__, path, data_get_type_string(src), |
| (uintptr_t) src, array->obj_type_string, (uintptr_t) dst, |
| array->ptr_offset, (array->field_name ? "->" : ""), |
| (array->field_name ? array->field_name : ""), |
| parser->obj_type_string, (uintptr_t) parser, |
| array->type_string, (uintptr_t) array, parser->type_string, |
| (uintptr_t) parser, rc, slurm_strerror(rc)); |
| |
| cleanup: |
| FREE_NULL_DATA(ppath); |
| xfree(path); |
| return rc; |
| } |
| |
| static void _parse_check_openapi(const parser_t *const parser, data_t *src, |
| args_t *args, data_t *parent_path) |
| { |
| char *path = NULL; |
| const char *oas_type, *oas_format, *found_type, *found_format; |
| data_type_t oas_data_type; |
| openapi_type_format_t found; |
| |
| if (data_get_type(src) == DATA_TYPE_NULL) |
| return; |
| |
| if (parser->obj_openapi == OPENAPI_FORMAT_INVALID) |
| return; |
| |
| if (data_get_type(src) == |
| openapi_type_format_to_data_type(parser->obj_openapi)) |
| return; |
| |
| oas_type = openapi_type_format_to_type_string(parser->obj_openapi); |
| oas_format = |
| openapi_type_format_to_format_string(parser->obj_openapi); |
| found = openapi_data_type_to_type_format(data_get_type(src)); |
| found_type = openapi_type_format_to_type_string(found); |
| found_format = openapi_type_format_to_format_string(found); |
| oas_data_type = openapi_type_format_to_data_type(parser->obj_openapi); |
| |
| xassert(!is_complex_mode(args)); |
| |
| /* |
| * Warn as this is user provided data and the parser may accept |
| * the format anyway. Steer the user towards the formats given |
| * in the OpenAPI specification as set in the parser. |
| */ |
| on_warn(PARSING, parser->type, args, |
| set_source_path(&path, args, parent_path), __func__, |
| "Expected OpenAPI type=%s%s%s (Slurm type=%s) but got OpenAPI type=%s%s%s (Slurm type=%s): %pd", |
| oas_type, (oas_format ? " format=" : ""), |
| (oas_format ? oas_format : ""), |
| data_type_to_string(oas_data_type), found_type, |
| (found_format ? " format=" : ""), |
| (found_format ? found_format : ""), data_get_type_string(src), |
| src); |
| |
| xfree(path); |
| } |
| |
| extern int parse(void *dst, ssize_t dst_bytes, const parser_t *const parser, |
| data_t *src, args_t *args, data_t *parent_path) |
| { |
| int rc = SLURM_SUCCESS; |
| char *path = NULL; |
| |
| check_parser(parser); |
| xassert(parser->model != PARSER_MODEL_ARRAY_SKIP_FIELD); |
| xassert(args->magic == MAGIC_ARGS); |
| xassert(data_get_type(src) != DATA_TYPE_NONE); |
| xassert(dst); |
| /* |
| * Make sure the target object is the same size since there is no |
| * way to dump value of __typeof__ as a value in C99. |
| */ |
| xassert((dst_bytes == NO_VAL) || (dst_bytes == parser->size) || |
| (parser->model == PARSER_MODEL_ALIAS)); |
| |
| if ((rc = load_prereqs(PARSING, parser, args))) |
| goto cleanup; |
| |
| if (!src) { |
| if (parser->required) { |
| if ((rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_PATH_NOT_FOUND, |
| set_source_path(&path, args, |
| parent_path), |
| __func__, |
| "Missing required field '%s' in dictionary", |
| parser->key))) |
| goto cleanup; |
| } else { |
| /* field is missing but not required */ |
| log_flag(DATA, "%s: skip parsing missing %s to %zd byte object %s(0x%" PRIxPTR "+%zd)%s%s via parser %s(0x%" PRIxPTR ")", |
| __func__, set_source_path(&path, args, |
| parent_path), |
| (dst_bytes == NO_VAL ? -1 : dst_bytes), |
| parser->obj_type_string, (uintptr_t) dst, |
| (parser->ptr_offset == NO_VAL ? |
| 0 : |
| parser->ptr_offset), |
| (parser->field_name ? "->" : ""), |
| (parser->field_name ? parser->field_name : ""), |
| parser->type_string, (uintptr_t) parser); |
| |
| rc = SLURM_SUCCESS; |
| goto cleanup; |
| } |
| } |
| |
| log_flag(DATA, "%s: BEGIN: parsing %s{%s(0x%" PRIxPTR ")} to %zd byte object %s(0x%" PRIxPTR "+%zd)%s%s via parser %s(0x%" PRIxPTR ")", |
| __func__, set_source_path(&path, args, parent_path), |
| data_get_type_string(src), |
| (uintptr_t) src, (dst_bytes == NO_VAL ? -1 : dst_bytes), |
| parser->obj_type_string, (uintptr_t) dst, |
| (parser->ptr_offset == NO_VAL ? 0 : parser->ptr_offset), |
| (parser->field_name ? "->" : ""), |
| (parser->field_name ? parser->field_name : ""), |
| parser->type_string, (uintptr_t) parser); |
| |
| switch (parser->model) { |
| case PARSER_MODEL_REMOVED: |
| if (data_get_type(src) != DATA_TYPE_NULL) |
| on_warn(PARSING, parser->type, args, path, __func__, |
| "Ignoring value for removed parser"); |
| rc = SLURM_SUCCESS; |
| break; |
| case PARSER_MODEL_FLAG_ARRAY: |
| verify_parser_not_sliced(parser); |
| rc = _parse_flag(dst, parser, src, args, parent_path); |
| break; |
| case PARSER_MODEL_LIST: |
| xassert(parser->list_type > DATA_PARSER_TYPE_INVALID); |
| xassert(parser->list_type < DATA_PARSER_TYPE_MAX); |
| verify_parser_not_sliced(parser); |
| xassert((dst_bytes == NO_VAL) || (dst_bytes == sizeof(list_t *))); |
| xassert(!parser->parse); |
| rc = _parse_list(parser, dst, src, args, parent_path); |
| break; |
| case PARSER_MODEL_ARRAY: |
| { |
| xassert(parser->fields); |
| verify_parser_not_sliced(parser); |
| |
| if (data_get_type(src) != DATA_TYPE_DICT) { |
| rc = on_error(PARSING, parser->type, args, |
| ESLURM_DATA_EXPECTED_DICT, |
| set_source_path(&path, args, parent_path), |
| __func__, |
| "Rejecting %s when dictionary expected", |
| data_get_type_string(src)); |
| } else { |
| /* recursively run the child parsers */ |
| for (int i = 0; !rc && (i < parser->field_count); i++) |
| rc = _parser_linked(args, parser, |
| &parser->fields[i], src, |
| dst, parent_path); |
| |
| if (!is_fast_mode(args)) { |
| parse_marray_args_t aargs = { |
| .magic = MAGIC_FOREACH_PARSE_MARRAY, |
| .args = args, |
| .array = parser, |
| .parent_path = parent_path, |
| }; |
| |
| aargs.path = data_set_list(data_new()); |
| (void) data_dict_for_each(src, |
| _foreach_parse_marray, |
| &aargs); |
| FREE_NULL_DATA(aargs.path); |
| } |
| } |
| break; |
| } |
| case PARSER_MODEL_PTR: |
| verify_parser_not_sliced(parser); |
| rc = _parse_pointer(parser, dst, src, args, parent_path); |
| break; |
| case PARSER_MODEL_NT_PTR_ARRAY: |
| case PARSER_MODEL_NT_ARRAY: |
| verify_parser_not_sliced(parser); |
| rc = _parse_nt_array(parser, dst, src, args, parent_path); |
| break; |
| case PARSER_MODEL_SIMPLE: |
| case PARSER_MODEL_COMPLEX: |
| xassert(parser->parse != _parse_list); |
| verify_parser_not_sliced(parser); |
| |
| if (!is_fast_mode(args) && !is_complex_mode(args)) |
| _parse_check_openapi(parser, src, args, parent_path); |
| |
| rc = parser->parse(parser, dst, src, args, parent_path); |
| break; |
| case PARSER_MODEL_ALIAS: |
| rc = parse(dst, dst_bytes, |
| find_parser_by_type(parser->alias_type), src, args, |
| parent_path); |
| break; |
| case PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD: |
| case PARSER_MODEL_ARRAY_LINKED_FIELD: |
| fatal_abort("%s: link model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_ARRAY_SKIP_FIELD: |
| fatal_abort("%s: skip model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_ARRAY_REMOVED_FIELD: |
| fatal_abort("%s: removed model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_INVALID: |
| case PARSER_MODEL_MAX: |
| fatal_abort("%s: invalid model %u", __func__, parser->model); |
| } |
| |
| cleanup: |
| log_flag(DATA, "%s: END: parsing %s{%s(0x%" PRIxPTR ")} to %zd byte object %s(0x%" PRIxPTR "+%zd)%s%s via parser %s(0x%" PRIxPTR ") rc[%d]:%s", |
| __func__, set_source_path(&path, args, parent_path), |
| data_get_type_string(src), (uintptr_t) src, |
| (dst_bytes == NO_VAL ? -1 : dst_bytes), |
| parser->obj_type_string, (uintptr_t) dst, (parser->ptr_offset |
| == NO_VAL ? 0 : |
| parser->ptr_offset), |
| (parser->field_name ? "->" : ""), (parser->field_name ? |
| parser->field_name : ""), |
| parser->type_string, (uintptr_t) parser, rc, |
| slurm_strerror(rc)); |
| |
| xfree(path); |
| |
| return rc; |
| } |
| |
| static bool _match_flag_bit(const parser_t *const parser, void *src, |
| const flag_bit_t *bit, uint64_t used_equal_bits) |
| { |
| const uint64_t v = bit->mask & bit->value; |
| |
| if (used_equal_bits & bit->mask) |
| return false; |
| |
| /* C allows compiler to choose a size for the enum */ |
| if (parser->size == sizeof(uint64_t)) { |
| uint64_t *flags = src; |
| return ((*flags & v) == v); |
| } else if (parser->size == sizeof(uint32_t)) { |
| uint32_t *flags = src; |
| return ((*flags & v) == v); |
| } else if (parser->size == sizeof(uint16_t)) { |
| uint16_t *flags = src; |
| return ((*flags & v) == v); |
| } else if (parser->size == sizeof(uint8_t)) { |
| uint8_t *flags = src; |
| return ((*flags & v) == v); |
| } |
| |
| fatal("%s: unexpected enum size: %zu", __func__, parser->size); |
| } |
| |
| static bool _match_flag_equal(const parser_t *const parser, void *src, |
| const flag_bit_t *bit, |
| uint64_t *used_equal_bits_ptr) |
| { |
| bool found; |
| const uint64_t v = bit->mask & bit->value; |
| |
| /* C allows compiler to choose a size for the enum */ |
| if (parser->size == sizeof(uint64_t)) { |
| uint64_t *flags = src; |
| found = ((*flags & bit->mask) == v); |
| } else if (parser->size == sizeof(uint32_t)) { |
| uint32_t *flags = src; |
| found = ((*flags & bit->mask) == v); |
| } else if (parser->size == sizeof(uint16_t)) { |
| uint16_t *flags = src; |
| found = ((*flags & bit->mask) == v); |
| } else if (parser->size == sizeof(uint8_t)) { |
| uint8_t *flags = src; |
| found = ((*flags & bit->mask) == v); |
| } else { |
| fatal("%s: unexpected enum size: %zu", __func__, parser->size); |
| } |
| |
| if (found) |
| *used_equal_bits_ptr |= bit->mask; |
| |
| return found; |
| } |
| |
| static void _dump_flag_bit_array_flag(args_t *args, void *src, data_t *dst, |
| const parser_t *const parser, |
| const flag_bit_t *bit, bool set_bool, |
| uint64_t *used_equal_bits) |
| { |
| bool found; |
| |
| if (bit->hidden) |
| return; |
| |
| if (bit->type == FLAG_BIT_TYPE_BIT) |
| found = _match_flag_bit(parser, src, bit, *used_equal_bits); |
| else if (bit->type == FLAG_BIT_TYPE_EQUAL) |
| found = _match_flag_equal(parser, src, bit, used_equal_bits); |
| else if (bit->type == FLAG_BIT_TYPE_REMOVED) |
| found = false; |
| else |
| fatal_abort("%s: invalid bit_flag_t", __func__); |
| |
| if (set_bool) |
| data_set_bool(dst, found); |
| else if (found) { |
| data_t *dst_flag; |
| |
| if (parser->single_flag) |
| dst_flag = dst; |
| else |
| dst_flag = data_list_append(dst); |
| |
| data_set_string(dst_flag, bit->name); |
| } |
| |
| if (slurm_conf.debug_flags & DEBUG_FLAG_DATA) { |
| const char *type; |
| uint64_t value; |
| |
| if (parser->size == sizeof(uint64_t)) { |
| uint64_t *flags = src; |
| value = *flags; |
| } else if (parser->size == sizeof(uint32_t)) { |
| uint32_t *flags = src; |
| value = *flags; |
| } else if (parser->size == sizeof(uint16_t)) { |
| uint16_t *flags = src; |
| value = *flags; |
| } else if (parser->size == sizeof(uint8_t)) { |
| uint8_t *flags = src; |
| value = *flags; |
| } else { |
| fatal_abort("invalid parser flag size: %zu", |
| parser->size); |
| } |
| |
| if (bit->type == FLAG_BIT_TYPE_BIT) |
| type = "bit"; |
| else if (bit->type == FLAG_BIT_TYPE_EQUAL) |
| type = "bit-equals"; |
| else if (bit->type == FLAG_BIT_TYPE_REMOVED) |
| type = "removed"; |
| else |
| type = "INVALID"; |
| |
| log_flag(DATA, "%s: %s \"%s\" flag %s %s(%s[0x%"PRIx64"] & %s[0x%"PRIx64"]) & 0x%"PRIx64" = 0x%"PRIx64" for %zd byte %s(0x%" PRIxPTR "+%zd)->%s with parser %s(0x%" PRIxPTR ") to data %s[0x%" PRIxPTR "]", |
| __func__, (found ? "appending matched" : "skipping"), |
| bit->name, type, bit->name, bit->mask_name, bit->mask, |
| bit->flag_name, bit->value, value, |
| (bit->mask & value & bit->value), parser->size, |
| parser->obj_type_string, (uintptr_t) src, |
| parser->ptr_offset, parser->field_name, |
| parser->type_string, (uintptr_t) parser, |
| data_get_type_string(dst), |
| (uintptr_t) dst); |
| } |
| } |
| |
| static int _dump_flag_bit_array(args_t *args, void *src, data_t *dst, |
| const parser_t *const parser) |
| { |
| int rc = SLURM_SUCCESS; |
| uint64_t used_equal_bits = 0; |
| |
| xassert(args->magic == MAGIC_ARGS); |
| check_parser(parser); |
| |
| if (!parser->single_flag) { |
| if (data_get_type(dst) == DATA_TYPE_NULL) |
| data_set_list(dst); |
| if (data_get_type(dst) != DATA_TYPE_LIST) |
| return ESLURM_DATA_CONV_FAILED; |
| } |
| |
| for (int8_t i = 0; !rc && (i < parser->flag_bit_array_count); i++) |
| _dump_flag_bit_array_flag(args, src, dst, parser, |
| &parser->flag_bit_array[i], false, |
| &used_equal_bits); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| static int _foreach_dump_list(void *obj, void *arg) |
| { |
| foreach_list_t *args = arg; |
| data_t *item = data_list_append(args->dlist); |
| |
| xassert(args->magic == MAGIC_FOREACH_LIST); |
| xassert(args->args->magic == MAGIC_ARGS); |
| check_parser(args->parser); |
| xassert(args->parser->ptr_offset == NO_VAL); |
| |
| /* we don't know the size of the items in the list */ |
| if (dump(&obj, NO_VAL, NULL, |
| find_parser_by_type(args->parser->list_type), item, |
| args->args)) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int _dump_list(const parser_t *const parser, void *src, data_t *dst, |
| args_t *args) |
| { |
| list_t **list_ptr = src; |
| foreach_list_t fargs = { |
| .magic = MAGIC_FOREACH_LIST, |
| .args = args, |
| .parser = parser, |
| .list = (list_ptr ? *list_ptr : NULL), |
| .dlist = dst, |
| }; |
| |
| xassert(args->magic == MAGIC_ARGS); |
| check_parser(parser); |
| xassert(!list_ptr || !*list_ptr || (list_count(*list_ptr) >= 0)); |
| xassert((data_get_type(dst) == DATA_TYPE_NULL) || |
| (data_get_type(dst) == DATA_TYPE_LIST)); |
| |
| if (data_get_type(dst) != DATA_TYPE_LIST) |
| data_set_list(dst); |
| |
| if (!fargs.list || list_is_empty(fargs.list)) { |
| /* list is empty */ |
| return SLURM_SUCCESS; |
| } |
| |
| if (list_for_each(fargs.list, _foreach_dump_list, &fargs) < 0) { |
| return on_error(DUMPING, parser->type, args, SLURM_ERROR, |
| "_foreach_dump_list", __func__, |
| "dumping list failed"); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| static int _dump_pointer(const parser_t *const field_parser, |
| const parser_t *const parser, void *src, data_t *dst, |
| args_t *args) |
| { |
| const parser_t *pt = find_parser_by_type(parser->pointer_type); |
| void **ptr = src; |
| |
| if (!*ptr) { |
| if (is_complex_mode(args)) { |
| xassert(data_get_type(dst) == DATA_TYPE_NULL); |
| return SLURM_SUCCESS; |
| } |
| |
| /* Fully resolve pointer on NULL to use correct model */ |
| pt = unalias_parser(pt); |
| |
| if (parser->allow_null_pointer || |
| (field_parser && !field_parser->required)) { |
| xassert(data_get_type(dst) == DATA_TYPE_NULL); |
| } else if ((pt->model == PARSER_MODEL_ARRAY) || |
| (pt->obj_openapi == OPENAPI_FORMAT_OBJECT)) { |
| /* |
| * OpenAPI clients can't handle a null instead of an |
| * object. Work around by placing an empty dictionary |
| * instead of null. |
| */ |
| data_set_dict(dst); |
| } else if ((pt->model == PARSER_MODEL_LIST) || |
| (pt->model == PARSER_MODEL_NT_ARRAY) || |
| (pt->model == PARSER_MODEL_NT_PTR_ARRAY) || |
| (pt->obj_openapi == OPENAPI_FORMAT_ARRAY)) { |
| /* |
| * OpenAPI clients can't handle a null instead of an |
| * array. Work around by placing an empty list instead |
| * of null. |
| */ |
| data_set_list(dst); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| return dump(*ptr, NO_VAL, NULL, pt, dst, args); |
| } |
| |
| static int _dump_nt_array(const parser_t *const parser, void *src, data_t *dst, |
| args_t *args) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| data_set_list(dst); |
| |
| if (parser->model == PARSER_MODEL_NT_PTR_ARRAY) { |
| void ***array_ptr = src; |
| void **array; |
| |
| if (!(array = *array_ptr)) |
| return SLURM_SUCCESS; |
| |
| for (int i = 0; !rc && array[i]; i++) { |
| rc = dump(array[i], NO_VAL, |
| NULL, find_parser_by_type(parser->array_type), |
| data_list_append(dst), args); |
| } |
| } else if (parser->model == PARSER_MODEL_NT_ARRAY) { |
| const parser_t *const ap = |
| find_parser_by_type(parser->array_type); |
| void **array = src; |
| |
| if (!*array) |
| return SLURM_SUCCESS; |
| |
| for (int i = 0; !rc; i++) { |
| bool done = true; |
| void *ptr = *array + (ap->size * i); |
| |
| /* check every byte of object is zero */ |
| for (int j = 0; j < ap->size; j++) |
| if (((char *) ptr)[j]) |
| done = false; |
| |
| if (done) |
| break; |
| |
| rc = dump(ptr, NO_VAL, NULL, |
| find_parser_by_type(parser->array_type), |
| data_list_append(dst), args); |
| } |
| } else { |
| fatal_abort("invalid model"); |
| } |
| |
| return rc; |
| } |
| |
| static void _dump_removed(const parser_t *parser, data_t *dst, args_t *args) |
| { |
| if (is_complex_mode(args)) { |
| data_set_null(dst); |
| return; |
| } |
| |
| while ((parser->model == PARSER_MODEL_ARRAY_REMOVED_FIELD) || |
| parser->pointer_type) { |
| parser = unalias_parser(parser); |
| |
| while (parser->model == PARSER_MODEL_ARRAY_REMOVED_FIELD) |
| parser = find_parser_by_type(parser->type); |
| } |
| |
| xassert(parser->model != PARSER_MODEL_ARRAY_REMOVED_FIELD); |
| xassert(parser->model != |
| PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD); |
| xassert(parser->model > PARSER_MODEL_INVALID); |
| xassert(parser->model < PARSER_MODEL_MAX); |
| |
| switch (parser->obj_openapi) { |
| case OPENAPI_FORMAT_INT: |
| case OPENAPI_FORMAT_INT32: |
| case OPENAPI_FORMAT_INT64: |
| data_set_int(dst, 0); |
| break; |
| case OPENAPI_FORMAT_NUMBER: |
| case OPENAPI_FORMAT_FLOAT: |
| case OPENAPI_FORMAT_DOUBLE: |
| data_set_float(dst, 0); |
| break; |
| case OPENAPI_FORMAT_STRING: |
| case OPENAPI_FORMAT_PASSWORD: |
| data_set_string(dst, ""); |
| break; |
| case OPENAPI_FORMAT_BOOL: |
| data_set_bool(dst, false); |
| case OPENAPI_FORMAT_OBJECT: |
| data_set_dict(dst); |
| break; |
| case OPENAPI_FORMAT_ARRAY: |
| data_set_list(dst); |
| break; |
| case OPENAPI_FORMAT_MAX: |
| case OPENAPI_FORMAT_INVALID: |
| /* Should never happen but avoid crashing clients */ |
| xassert(false); |
| data_set_null(dst); |
| }; |
| } |
| |
| static int _dump_linked(args_t *args, const parser_t *const array, |
| const parser_t *const parser, void *src, data_t *dst) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| check_parser(parser); |
| verify_parser_sliced(parser); |
| |
| if ((parser->ptr_offset != NO_VAL) && src) |
| src += parser->ptr_offset; |
| |
| /* |
| * Only look for child via key if there was one defined |
| */ |
| if (parser->key) { |
| /* |
| * Detect duplicate keys |
| */ |
| xassert(!data_resolve_dict_path(dst, parser->key)); |
| dst = data_define_dict_path(dst, parser->key); |
| } |
| |
| xassert(dst && (data_get_type(dst) != DATA_TYPE_NONE)); |
| |
| if (parser->model == PARSER_MODEL_ARRAY_SKIP_FIELD) { |
| log_flag(DATA, "SKIP: %s parser %s->%s(0x%" PRIxPTR ") for %s(0x%" PRIxPTR ")->%s(+%zd) for data(0x%" PRIxPTR ")/%s(0x%" PRIxPTR ")", |
| parser->obj_type_string, |
| array->type_string, |
| parser->type_string, (uintptr_t) |
| parser, array->obj_type_string, |
| (uintptr_t) src, array->field_name, |
| array->ptr_offset, (uintptr_t) dst, |
| array->key, (uintptr_t) dst); |
| return SLURM_SUCCESS; |
| } |
| |
| if (parser->model == PARSER_MODEL_ARRAY_REMOVED_FIELD) { |
| const parser_t *const rparser = |
| find_parser_by_type(parser->type); |
| |
| log_flag(DATA, "removed: %s parser %s->%s(0x%" PRIxPTR ") for %s(0x%" PRIxPTR ") for data(0x%" PRIxPTR ")/%s(0x%" PRIxPTR ")", |
| parser->obj_type_string, |
| array->type_string, |
| parser->type_string, (uintptr_t) |
| parser, array->obj_type_string, |
| (uintptr_t) src, (uintptr_t) dst, |
| array->key, (uintptr_t) dst); |
| |
| _dump_removed(rparser, dst, args); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| if (parser->model == |
| PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD) { |
| uint64_t used_equal_bits = 0; |
| |
| if (data_get_type(dst) == DATA_TYPE_NULL) |
| data_set_dict(dst); |
| |
| for (int i = 0; i < parser->flag_bit_array_count; i++) { |
| const flag_bit_t *bit = &parser->flag_bit_array[i]; |
| |
| if (bit->hidden) |
| continue; |
| |
| data_t *bit_dst = data_define_dict_path(dst, bit->name); |
| |
| xassert(src); |
| _dump_flag_bit_array_flag(args, src, bit_dst, parser, |
| bit, true, &used_equal_bits); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| xassert(parser->model == PARSER_MODEL_ARRAY_LINKED_FIELD); |
| |
| log_flag(DATA, "BEGIN: dumping %s parser %s->%s(0x%" PRIxPTR ") for %s(0x%" PRIxPTR ")->%s(+%zd) for data(0x%" PRIxPTR ")/%s(0x%" PRIxPTR ")", |
| parser->obj_type_string, array->type_string, |
| parser->type_string, (uintptr_t) parser, |
| parser->obj_type_string, (uintptr_t) src, array->field_name, |
| array->ptr_offset, (uintptr_t) dst, array->key, |
| (uintptr_t) dst); |
| |
| rc = dump(src, NO_VAL, parser, find_parser_by_type(parser->type), dst, |
| args); |
| |
| log_flag(DATA, "END: dumping %s parser %s->%s(0x%" PRIxPTR ") for %s(0x%" PRIxPTR ")->%s(+%zd) for data(0x%" PRIxPTR ")/%s(0x%" PRIxPTR ")", |
| parser->obj_type_string, array->type_string, |
| parser->type_string, (uintptr_t) parser, |
| array->obj_type_string, (uintptr_t) src, array->field_name, |
| array->ptr_offset, (uintptr_t) dst, array->key, |
| (uintptr_t) dst); |
| |
| return rc; |
| } |
| |
| static void _check_dump(const parser_t *const parser, data_t *dst, args_t *args) |
| { |
| /* |
| * Resultant dump must be the proscribed OpenAPI compatible data_t type. |
| * Anything else will break most generated OpenAPI clients. |
| */ |
| if (parser->obj_openapi == OPENAPI_FORMAT_INVALID) |
| return; |
| |
| if (!is_complex_mode(args)) { |
| xassert(data_get_type(dst) == |
| openapi_type_format_to_data_type(parser->obj_openapi)); |
| } |
| } |
| |
| extern int dump(void *src, ssize_t src_bytes, |
| const parser_t *const field_parser, |
| const parser_t *const parser, data_t *dst, args_t *args) |
| { |
| int rc; |
| |
| log_flag(DATA, "dump %zd byte %s object at 0x%" PRIxPTR " with parser %s(0x%" PRIxPTR ") to data 0x%" PRIxPTR, |
| (src_bytes == NO_VAL ? -1 : src_bytes), |
| parser->obj_type_string, (uintptr_t) src, parser->type_string, |
| (uintptr_t) parser, (uintptr_t) dst); |
| |
| check_parser(parser); |
| xassert(parser->model != PARSER_MODEL_ARRAY_SKIP_FIELD); |
| xassert(dst && (data_get_type(dst) != DATA_TYPE_NONE)); |
| xassert(args->magic == MAGIC_ARGS); |
| xassert((src_bytes == NO_VAL) || (src_bytes > 0)); |
| |
| /* |
| * Make sure the source object is the same size since there is no |
| * way to dump value of __typeof__ as a value in C. |
| */ |
| xassert((src_bytes == NO_VAL) || (src_bytes == parser->size) || |
| (parser->model == PARSER_MODEL_ALIAS)); |
| |
| if (args->flags & FLAG_SPEC_ONLY) { |
| set_openapi_schema(dst, parser, args); |
| return SLURM_SUCCESS; |
| } |
| |
| if ((rc = load_prereqs(DUMPING, parser, args))) |
| goto done; |
| |
| switch (parser->model) { |
| case PARSER_MODEL_REMOVED: |
| _dump_removed(parser, dst, args); |
| rc = SLURM_SUCCESS; |
| break; |
| case PARSER_MODEL_FLAG_ARRAY: |
| verify_parser_not_sliced(parser); |
| xassert((data_get_type(dst) == DATA_TYPE_NULL) || |
| (data_get_type(dst) == DATA_TYPE_LIST)); |
| xassert(parser->ptr_offset == NO_VAL); |
| |
| if (data_get_type(dst) != DATA_TYPE_LIST) |
| data_set_list(dst); |
| |
| rc = _dump_flag_bit_array(args, src, dst, parser); |
| break; |
| case PARSER_MODEL_ARRAY: |
| verify_parser_not_sliced(parser); |
| xassert(parser->fields); |
| xassert((data_get_type(dst) == DATA_TYPE_NULL) || |
| (data_get_type(dst) == DATA_TYPE_DICT)); |
| /* recursively run linked parsers for each struct field */ |
| for (int i = 0; !rc && (i < parser->field_count); i++) |
| rc = _dump_linked(args, parser, &parser->fields[i], src, |
| dst); |
| break; |
| case PARSER_MODEL_LIST: |
| xassert(parser->list_type > DATA_PARSER_TYPE_INVALID); |
| xassert(parser->list_type < DATA_PARSER_TYPE_MAX); |
| verify_parser_not_sliced(parser); |
| xassert((data_get_type(dst) == DATA_TYPE_NULL) || |
| (data_get_type(dst) == DATA_TYPE_LIST)); |
| xassert((src_bytes == NO_VAL) || (src_bytes == sizeof(list_t *))); |
| xassert(!parser->dump); |
| rc = _dump_list(parser, src, dst, args); |
| break; |
| case PARSER_MODEL_PTR: |
| xassert(parser->pointer_type > DATA_PARSER_TYPE_INVALID); |
| xassert(parser->pointer_type < DATA_PARSER_TYPE_MAX); |
| verify_parser_not_sliced(parser); |
| xassert(data_get_type(dst) == DATA_TYPE_NULL); |
| |
| rc = _dump_pointer(field_parser, parser, src, dst, args); |
| break; |
| case PARSER_MODEL_NT_PTR_ARRAY: |
| case PARSER_MODEL_NT_ARRAY: |
| xassert(parser->array_type > DATA_PARSER_TYPE_INVALID); |
| xassert(parser->array_type < DATA_PARSER_TYPE_MAX); |
| verify_parser_not_sliced(parser); |
| xassert(data_get_type(dst) == DATA_TYPE_NULL); |
| |
| rc = _dump_nt_array(parser, src, dst, args); |
| break; |
| case PARSER_MODEL_SIMPLE: |
| case PARSER_MODEL_COMPLEX: |
| xassert(data_get_type(dst) == DATA_TYPE_NULL); |
| verify_parser_not_sliced(parser); |
| |
| xassert(parser->dump != _dump_list); |
| |
| /* |
| * parser->pointer_type and parser->array_type may be set but |
| * they are only used to OpenAPI typing and are ignored here. |
| */ |
| |
| rc = parser->dump(parser, src, dst, args); |
| _check_dump(parser, dst, args); |
| break; |
| case PARSER_MODEL_ALIAS: |
| rc = dump(src, src_bytes, NULL, |
| find_parser_by_type(parser->alias_type), dst, args); |
| break; |
| case PARSER_MODEL_ARRAY_LINKED_EXPLODED_FLAG_ARRAY_FIELD: |
| case PARSER_MODEL_ARRAY_LINKED_FIELD: |
| fatal_abort("%s: link model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_ARRAY_SKIP_FIELD: |
| fatal_abort("%s: skip model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_ARRAY_REMOVED_FIELD: |
| fatal_abort("%s: removed model not allowed %u", |
| __func__, parser->model); |
| case PARSER_MODEL_INVALID: |
| case PARSER_MODEL_MAX: |
| fatal_abort("%s: invalid model %u", __func__, parser->model); |
| } |
| |
| done: |
| log_flag(DATA, "dump %zd byte %s object at 0x%" PRIxPTR " with parser %s(0x%" PRIxPTR ") to data 0x%" PRIxPTR " rc[%d]=%s", |
| (src_bytes == NO_VAL ? -1 : src_bytes), |
| parser->obj_type_string, (uintptr_t) src, parser->type_string, |
| (uintptr_t) parser, (uintptr_t) dst, rc, slurm_strerror(rc)); |
| |
| return rc; |
| } |