/*****************************************************************************\ * data.c - generic data_t structures ***************************************************************************** * Copyright (C) 2019 SchedMD LLC. * Written by Nathan Rini * * This file is part of Slurm, a resource management program. * For details, see . * 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 "config.h" #include #include #include "slurm/slurm.h" #include "src/common/list.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 "src/common/data.h" /* * Regex matches based on YAML 1.1 section 5.5. * Honors ~ as YAML 1.1 allows for null fields. */ static const char *bool_pattern_null = "^(\\~|[Nn][uU][lL][lL])$"; static regex_t bool_pattern_null_re; static const char *bool_pattern_true = "^([Yy](|[eE][sS])|[tT]([rR][uU][eE]|)|[Oo][nN])$"; static regex_t bool_pattern_true_re; static const char *bool_pattern_false = "^([nN]([Oo]|)|[fF](|[aA][lL][sS][eE])|[oO][fF][fF])$"; static regex_t bool_pattern_false_re; static const char *bool_pattern_int = "^([+-]?[0-9]+)$"; static regex_t bool_pattern_int_re; static const char *bool_pattern_float = "^([+-]?[0-9]*[.][0-9]*(|[eE][+-]?[0-9]+))$"; static regex_t bool_pattern_float_re; static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; static bool initialized = false; /* protected by init_mutex */ #define DATA_MAGIC 0x1992189F #define DATA_LIST_MAGIC 0x1992F89F #define DATA_LIST_NODE_MAGIC 0x1921F89F typedef struct data_list_node_s data_list_node_t; struct data_list_node_s { int magic; data_list_node_t *next; data_t *data; char *key; /* key for dictionary (only) */ }; /* single forward linked list */ struct data_list_s { int magic; size_t count; data_list_node_t *begin; data_list_node_t *end; }; static void _check_magic(const data_t *data); static void _release(data_t *data); static void _release_data_list_node(data_list_t *dl, data_list_node_t *dn); static int _dump_regex_error(int rc, const regex_t *preg) { char *buffer = NULL; size_t len = regerror(rc, preg, NULL, 0); if (len == 0) { error("%s: unknown regex error code: %d", __func__, rc); return SLURM_ERROR; } buffer = xmalloc(len); len = regerror(rc, preg, buffer, len); if (len) error("%s: regex error: %s", __func__, buffer); else error("%s: unexpected failure to get regex error", __func__); xfree(buffer); return SLURM_ERROR; } static bool _regex_quick_match(const char *str, const regex_t *preg) { int rc; // TODO: nmatch may safely be able to be 0 for this function size_t nmatch = 1; regmatch_t pmatch[nmatch]; /* not possible to match a NULL string */ if (!str) return false; rc = regexec(preg, str, nmatch, pmatch, 0); if (!rc) { /* matched */ return true; } else if (rc == REG_NOMATCH) { return false; } else { /* other error */ _dump_regex_error(rc, preg); return false; } } extern void data_destroy_static(void) { slurm_mutex_lock(&init_mutex); if (initialized) { regfree(&bool_pattern_null_re); regfree(&bool_pattern_true_re); regfree(&bool_pattern_false_re); regfree(&bool_pattern_int_re); regfree(&bool_pattern_float_re); } slurm_mutex_unlock(&init_mutex); } extern int data_init_static(void) { int rc = SLURM_SUCCESS; int reg_rc; /* regex rc */ slurm_mutex_lock(&init_mutex); if (initialized) goto cleanup; initialized = true; if (!rc && (reg_rc = regcomp(&bool_pattern_null_re, bool_pattern_null, REG_EXTENDED)) != 0) { rc = _dump_regex_error(reg_rc, &bool_pattern_null_re); } if (!rc && (reg_rc = regcomp(&bool_pattern_true_re, bool_pattern_true, REG_EXTENDED)) != 0) { rc = _dump_regex_error(reg_rc, &bool_pattern_true_re); } if (!rc && (reg_rc = regcomp(&bool_pattern_false_re, bool_pattern_false, REG_EXTENDED)) != 0) { rc = _dump_regex_error(reg_rc, &bool_pattern_false_re); } if (!rc && (reg_rc = regcomp(&bool_pattern_int_re, bool_pattern_int, REG_EXTENDED)) != 0) { rc = _dump_regex_error(reg_rc, &bool_pattern_int_re); } if (!rc && (reg_rc = regcomp(&bool_pattern_float_re, bool_pattern_float, REG_EXTENDED)) != 0) { rc = _dump_regex_error(reg_rc, &bool_pattern_float_re); } cleanup: slurm_mutex_unlock(&init_mutex); return rc; } static data_list_t *_data_list_new(void) { data_list_t *dl = xmalloc(sizeof(*dl)); xassert((dl->magic = DATA_LIST_MAGIC)); log_flag(DATA, "%s: new data list (0x%"PRIXPTR")", __func__, (uintptr_t) dl); return dl; } static void _check_data_list_node_magic(const data_list_node_t *dn) { xassert(dn); xassert(dn->magic == DATA_LIST_NODE_MAGIC); /* make sure not linking to self */ xassert(dn->next != dn); /* key can be NULL for list, but not NULL length string */ xassert(!dn->key || dn->key[0]); } static void _check_data_list_magic(const data_list_t *dl) { #ifndef NDEBUG data_list_node_t *end = NULL; xassert(dl); xassert(dl->magic == DATA_LIST_MAGIC); if (dl->begin) { /* walk forwards verify */ int c = 0; data_list_node_t *i = dl->begin; while (i) { c++; _check_data_list_node_magic(i); end = i; i = i->next; } xassert(c == dl->count); } xassert(end == dl->end); #endif /* !NDEBUG */ } /* verify node is in parent list */ static void _check_data_list_node_parent(const data_list_t *dl, const data_list_node_t *dn) { #ifndef NDEBUG data_list_node_t *i = dl->begin; while (i) { if (i == dn) return; i = i->next; } /* found an orphan? */ fatal_abort("%s: unexpected orphan node", __func__); #endif /* !NDEBUG */ } static void _release_data_list_node(data_list_t *dl, data_list_node_t *dn) { _check_data_list_magic(dl); _check_data_list_node_magic(dn); _check_data_list_node_parent(dl, dn); data_list_node_t *prev; /* walk list to find new previous */ for (prev = dl->begin; prev && prev->next != dn; ) { _check_data_list_node_magic(prev); prev = prev->next; if (prev) _check_data_list_node_magic(prev); } if (dn == dl->begin) { /* at the beginning */ dl->begin = dn->next; if (dl->end == dn) { dl->end = NULL; xassert(!dn->next); } } else if (dn == dl->end) { /* at the end */ xassert(!dn->next); dl->end = prev; prev->next = NULL; } else { /* somewhere in middle */ xassert(prev != dn); xassert(prev->next == dn); xassert(dl->begin != dn); xassert(dl->end != dn); prev->next = dn->next; } dl->count--; FREE_NULL_DATA(dn->data); xfree(dn->key); xassert((dn->magic = ~DATA_LIST_NODE_MAGIC)); xfree(dn); } static void _release_data_list(data_list_t *dl) { data_list_node_t *n = dl->begin, *i; #ifndef NDEBUG int count = 0; const int init_count = dl->count; #endif _check_data_list_magic(dl); if (!n) { xassert(!dl->end); return; } xassert(dl->end); while((i = n)) { n = i->next; _release_data_list_node(dl, i); #ifndef NDEBUG count++; #endif } #ifndef NDEBUG xassert(count == init_count); xassert(!(dl->count = 0)); xassert((dl->magic = ~DATA_LIST_MAGIC)); #endif xfree(dl); } /* * Create new data list node entry * IN d - data type to take ownership of * IN key - dictionary key to dup or NULL */ static data_list_node_t *_new_data_list_node(data_t *d, const char *key) { data_list_node_t *dn = xmalloc(sizeof(*dn)); xassert((dn->magic = DATA_LIST_NODE_MAGIC)); _check_magic(d); dn->data = d; if (key) dn->key = xstrdup(key); log_flag(DATA, "%s: new data list node (0x%"PRIXPTR")", __func__, (uintptr_t) dn); return dn; } static void _data_list_append(data_list_t *dl, data_t *d, const char *key) { data_list_node_t *n = _new_data_list_node(d, key); _check_data_list_magic(dl); _check_magic(d); if (dl->end) { xassert(!dl->end->next); _check_data_list_node_magic(dl->end); _check_data_list_node_magic(dl->begin); dl->end->next = n; dl->end = n; } else { xassert(!dl->count); dl->end = n; dl->begin = n; } dl->count++; } static void _data_list_prepend(data_list_t *dl, data_t *d, const char *key) { data_list_node_t *n = _new_data_list_node(d, key); _check_data_list_magic(dl); _check_magic(d); if (dl->begin) { _check_data_list_node_magic(dl->begin); n->next = dl->begin; dl->begin = n; } else { xassert(!dl->count); dl->begin = n; dl->end = n; } dl->count++; } data_t *data_new(void) { data_t *data = xmalloc(sizeof(*data)); xassert((data->magic = DATA_MAGIC)); data->type = DATA_TYPE_NULL; log_flag(DATA, "%s: new data (0x%"PRIXPTR")", __func__, (uintptr_t) data); return data; } static void _check_magic(const data_t *data) { xassert(data); xassert(data->type > DATA_TYPE_NONE); xassert(data->type < DATA_TYPE_MAX); xassert(data->magic == DATA_MAGIC); if (data->type == DATA_TYPE_NULL) /* make sure NULL type has a NULL value */ xassert(data->data.list_u == NULL); if (data->type == DATA_TYPE_LIST) _check_data_list_magic(data->data.list_u); if (data->type == DATA_TYPE_DICT) _check_data_list_magic(data->data.dict_u); } static void _release(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_LIST: _release_data_list(data->data.list_u); xassert(!(data->data.list_u = 0)); break; case DATA_TYPE_DICT: _release_data_list(data->data.dict_u); xassert(!(data->data.dict_u = 0)); break; case DATA_TYPE_STRING: xfree(data->data.string_u); break; default: /* other types don't need to be freed */ xassert(!(data->data.int_u = 0)); break; } data->type = DATA_TYPE_NONE; } extern void data_free(data_t *data) { if (!data) return; log_flag(DATA, "%s: free data (0x%"PRIXPTR")", __func__, (uintptr_t) data); _check_magic(data); _release(data); xassert((data->magic = 0) == 0); xfree(data); } extern data_type_t data_get_type(const data_t *data) { _check_magic(data); if (data) return data->type; else return DATA_TYPE_NONE; } extern data_t *data_set_float(data_t *data, double value) { _check_magic(data); if (!data) return NULL; log_flag(DATA, "%s: set data (0x%"PRIXPTR") to float: %lf", __func__, (uintptr_t) data, value); data->type = DATA_TYPE_FLOAT; data->data.float_u = value; return data; } extern data_t *data_set_null(data_t *data) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to null", __func__, (uintptr_t) data); data->type = DATA_TYPE_NULL; xassert((memset(&data->data, 0, sizeof(data->data)))); return data; } extern data_t *data_set_bool(data_t *data, bool value) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to bool: %d", __func__, (uintptr_t) data, value); data->type = DATA_TYPE_BOOL; data->data.bool_u = value; return data; } extern data_t *data_set_int(data_t *data, int64_t value) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to int64_t: %"PRId64, __func__, (uintptr_t) data, value); data->type = DATA_TYPE_INT_64; data->data.int_u = value; return data; } extern data_t *data_set_string(data_t *data, const char *value) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to string: %s", __func__, (uintptr_t) data, value); data->type = DATA_TYPE_STRING; data->data.string_u = xstrdup(value); return data; } extern data_t *data_set_dict(data_t *data) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to dictionary", __func__, (uintptr_t) data); data->type = DATA_TYPE_DICT; data->data.dict_u = _data_list_new(); return data; } extern data_t *data_set_list(data_t *data) { _check_magic(data); if (!data) return NULL; _release(data); log_flag(DATA, "%s: set data (0x%"PRIXPTR") to list", __func__, (uintptr_t) data); data->type = DATA_TYPE_LIST; data->data.dict_u = _data_list_new(); return data; } extern data_t *data_list_append(data_t *data) { data_t *ndata = NULL; _check_magic(data); xassert(data->type == DATA_TYPE_LIST); if (!data || data->type != DATA_TYPE_LIST) return NULL; ndata = data_new(); _data_list_append(data->data.list_u, ndata, NULL); log_flag(DATA, "%s: list append data (0x%"PRIXPTR") to (0x%"PRIXPTR")", __func__, (uintptr_t) ndata, (uintptr_t) data); return ndata; } extern data_t *data_list_prepend(data_t *data) { data_t *ndata = NULL; _check_magic(data); xassert(data->type == DATA_TYPE_LIST); if (!data || data->type != DATA_TYPE_LIST) return NULL; ndata = data_new(); _data_list_prepend(data->data.list_u, ndata, NULL); log_flag(DATA, "%s: list prepend data (0x%"PRIXPTR") to (0x%"PRIXPTR")", __func__, (uintptr_t) ndata, (uintptr_t) data); return ndata; } const data_t *data_key_get_const(const data_t *data, const char *key) { const data_list_node_t *i; _check_magic(data); xassert(data->type == DATA_TYPE_DICT); if (!key || data->type != DATA_TYPE_DICT) return NULL; /* don't bother searching empty dictionary */ if (!data->data.dict_u->count) return NULL; _check_data_list_magic(data->data.dict_u); i = data->data.dict_u->begin; while (i) { _check_data_list_node_magic(i); if (!xstrcmp(key, i->key)) break; i = i->next; } if (i) return i->data; else return NULL; } data_t *data_key_get(data_t *data, const char *key) { data_list_node_t *i; _check_magic(data); xassert(data->type == DATA_TYPE_DICT); if (!key || data->type != DATA_TYPE_DICT) return NULL; /* don't bother searching empty dictionary */ if (!data->data.dict_u->count) return NULL; _check_data_list_magic(data->data.dict_u); i = data->data.dict_u->begin; while (i) { _check_data_list_node_magic(i); if (!xstrcmp(key, i->key)) break; i = i->next; } if (i) return i->data; else return NULL; } data_t *data_key_set(data_t *data, const char *key) { data_t *d; _check_magic(data); xassert(data->type == DATA_TYPE_DICT); xassert(key && key[0]); if (!key || !key[0] || data->type != DATA_TYPE_DICT) return NULL; if ((d = data_key_get(data, key))) { log_flag(DATA, "%s: set existing key in data (0x%"PRIXPTR") key: %s data (0x%"PRIXPTR")", __func__, (uintptr_t) data, key, (uintptr_t) d); return d; } d = data_new(); _data_list_append(data->data.dict_u, d, key); log_flag(DATA, "%s: set new key in data (0x%"PRIXPTR") key: %s data (0x%"PRIXPTR")", __func__, (uintptr_t) data, key, (uintptr_t) d); return d; } data_t *data_key_set_int(data_t *data, int64_t key) { char *key_str = xstrdup_printf("%"PRId64, key); data_t *node = data_key_set(data, key_str); xfree(key_str); return node; } bool data_key_unset(data_t *data, const char *key) { data_list_node_t *i; _check_magic(data); xassert(data->type == DATA_TYPE_DICT); if (!key || data->type != DATA_TYPE_DICT) return NULL; _check_data_list_magic(data->data.dict_u); i = data->data.dict_u->begin; while (i) { _check_data_list_node_magic(i); if (!xstrcmp(key, i->key)) break; i = i->next; } if (!i) { log_flag(DATA, "%s: remove non-existent key in data (0x%"PRIXPTR") key: %s", __func__, (uintptr_t) data, key); return false; } _release_data_list_node(data->data.dict_u, i); log_flag(DATA, "%s: remove existing key in data (0x%"PRIXPTR") key: %s", __func__, (uintptr_t) data, key); return true; } double data_get_float(const data_t *data) { _check_magic(data); if (!data) return NAN; xassert(data->type == DATA_TYPE_FLOAT); return data->data.float_u; } bool data_get_bool(const data_t *data) { _check_magic(data); if (!data) return false; xassert(data->type == DATA_TYPE_BOOL); return data->data.bool_u; } int64_t data_get_int(const data_t *data) { _check_magic(data); if (!data) return 0; xassert(data->type == DATA_TYPE_INT_64); return data->data.int_u; } const char *data_get_string(const data_t *data) { _check_magic(data); if (!data) return NULL; xassert(data->type == DATA_TYPE_STRING); return data->data.string_u; } int data_get_string_converted(const data_t *d, char **buffer) { _check_magic(d); char *_buffer = NULL; if (!d || !buffer) return SLURM_ERROR; if (data_get_type(d) != DATA_TYPE_STRING) { /* copy the data and then convert it to a string type */ data_t *dclone = data_new(); data_copy(dclone, d); if (data_convert_type(dclone, DATA_TYPE_STRING) == DATA_TYPE_STRING) _buffer = xstrdup(data_get_string(dclone)); FREE_NULL_DATA(dclone); } else _buffer = xstrdup(data_get_string(d)); if (_buffer) { *buffer = _buffer; return SLURM_SUCCESS; } return SLURM_ERROR; } extern int data_copy_bool_converted(const data_t *d, bool *buffer) { _check_magic(d); int rc = SLURM_ERROR; if (!d || !buffer) return rc; if (data_get_type(d) != DATA_TYPE_BOOL) { data_t *dclone = data_new(); data_copy(dclone, d); if (data_convert_type(dclone, DATA_TYPE_BOOL) == DATA_TYPE_BOOL) { *buffer = data_get_bool(dclone); rc = SLURM_SUCCESS; } FREE_NULL_DATA(dclone); return rc; } *buffer = data_get_bool(d); return SLURM_SUCCESS; } extern int data_get_bool_converted(data_t *d, bool *buffer) { int rc; _check_magic(d); if (!d || !buffer) return SLURM_ERROR; /* assign value if converted successfully */ rc = data_copy_bool_converted(d, buffer); if (!rc) data_set_bool(d, *buffer); return rc; } extern int data_get_int_converted(const data_t *d, int64_t *buffer) { _check_magic(d); int rc = SLURM_SUCCESS; if (!d || !buffer) return SLURM_ERROR; if (data_get_type(d) != DATA_TYPE_INT_64) { data_t *dclone = data_new(); data_copy(dclone, d); if (data_convert_type(dclone, DATA_TYPE_INT_64) == DATA_TYPE_INT_64) *buffer = data_get_int(dclone); else rc = SLURM_ERROR; FREE_NULL_DATA(dclone); } else { *buffer = data_get_int(d); } return rc; } size_t data_get_dict_length(const data_t *data) { _check_magic(data); if (!data) return 0; xassert(data->type == DATA_TYPE_DICT); return data->data.dict_u->count; } size_t data_get_list_length(const data_t *data) { _check_magic(data); if (!data) return 0; xassert(data->type == DATA_TYPE_LIST); return data->data.list_u->count; } extern int data_list_for_each_const(const data_t *d, DataListForFConst f, void *arg) { int count = 0; const data_list_node_t *i; _check_magic(d); if (!d || data_get_type(d) != DATA_TYPE_LIST) { error("%s: for each attempted on non-list object (0x%"PRIXPTR")", __func__, (uintptr_t) d); return -1; } i = d->data.list_u->begin; _check_data_list_magic(d->data.list_u); while (i) { _check_data_list_node_magic(i); xassert(!i->key); data_for_each_cmd_t cmd = f(i->data, arg); count++; xassert(cmd > DATA_FOR_EACH_INVALID); xassert(cmd < DATA_FOR_EACH_MAX); switch (cmd) { case DATA_FOR_EACH_CONT: break; case DATA_FOR_EACH_DELETE: fatal_abort("%s: delete attempted against const", __func__); break; case DATA_FOR_EACH_FAIL: count *= -1; /* fall through */ case DATA_FOR_EACH_STOP: i = NULL; break; default: fatal_abort("%s: invalid cmd", __func__); } if (i) i = i->next; } return count; } extern int data_list_for_each(data_t *d, DataListForF f, void *arg) { int count = 0; data_list_node_t *i; _check_magic(d); if (!d || data_get_type(d) != DATA_TYPE_LIST) { error("%s: for each attempted on non-list object (0x%"PRIXPTR")", __func__, (uintptr_t) d); return -1; } i = d->data.list_u->begin; _check_data_list_magic(d->data.list_u); while (i) { _check_data_list_node_magic(i); xassert(!i->key); data_for_each_cmd_t cmd = f(i->data, arg); count++; xassert(cmd > DATA_FOR_EACH_INVALID); xassert(cmd < DATA_FOR_EACH_MAX); switch (cmd) { case DATA_FOR_EACH_CONT: break; case DATA_FOR_EACH_DELETE: _release_data_list_node(d->data.list_u, i); break; case DATA_FOR_EACH_FAIL: count *= -1; /* fall through */ case DATA_FOR_EACH_STOP: i = NULL; break; default: fatal_abort("%s: invalid cmd", __func__); } if (i) i = i->next; } return count; } extern int data_dict_for_each_const(const data_t *d, DataDictForFConst f, void *arg) { int count = 0; const data_list_node_t *i; _check_magic(d); if (!d || data_get_type(d) != DATA_TYPE_DICT) { error("%s: for each attempted on non-dict object (0x%"PRIXPTR")", __func__, (uintptr_t) d); return -1; } i = d->data.dict_u->begin; _check_data_list_magic(d->data.dict_u); while (i) { data_for_each_cmd_t cmd; _check_data_list_node_magic(i); cmd = f(i->key, i->data, arg); count++; xassert(cmd > DATA_FOR_EACH_INVALID); xassert(cmd < DATA_FOR_EACH_MAX); switch (cmd) { case DATA_FOR_EACH_CONT: break; case DATA_FOR_EACH_DELETE: fatal_abort("%s: delete attempted against const", __func__); break; case DATA_FOR_EACH_FAIL: count *= -1; /* fall through */ case DATA_FOR_EACH_STOP: i = NULL; break; default: fatal_abort("%s: invalid cmd", __func__); } if (i) i = i->next; } return count; } extern int data_dict_for_each(data_t *d, DataDictForF f, void *arg) { int count = 0; data_list_node_t *i; _check_magic(d); if (!d || data_get_type(d) != DATA_TYPE_DICT) { error("%s: for each attempted on non-dict object (0x%"PRIXPTR")", __func__, (uintptr_t) d); return -1; } i = d->data.dict_u->begin; _check_data_list_magic(d->data.dict_u); while (i) { _check_data_list_node_magic(i); data_for_each_cmd_t cmd = f(i->key, i->data, arg); count++; xassert(cmd > DATA_FOR_EACH_INVALID); xassert(cmd < DATA_FOR_EACH_MAX); switch (cmd) { case DATA_FOR_EACH_CONT: break; case DATA_FOR_EACH_DELETE: _release_data_list_node(d->data.dict_u, i); break; case DATA_FOR_EACH_FAIL: count *= -1; /* fall through */ case DATA_FOR_EACH_STOP: i = NULL; break; default: fatal_abort("%s: invalid cmd", __func__); } if (i) i = i->next; } return count; } static int _convert_data_string(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: return SLURM_SUCCESS; case DATA_TYPE_BOOL: data_set_string(data, (data->data.bool_u ? "true" : "false")); return SLURM_SUCCESS; case DATA_TYPE_NULL: data_set_string(data, "null"); return SLURM_SUCCESS; case DATA_TYPE_FLOAT: { char *str = xstrdup_printf("%lf", data->data.float_u); data_set_string(data, str); xfree(str); return SLURM_SUCCESS; } case DATA_TYPE_INT_64: { char *str = xstrdup_printf("%"PRId64, data->data.int_u); data_set_string(data, str); xfree(str); return SLURM_SUCCESS; } default: return SLURM_ERROR; } return SLURM_ERROR; } static int _convert_data_force_bool(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: if (data->data.string_u == NULL || data->data.string_u[0] == '\0') data_set_bool(data, false); else if (_regex_quick_match(data->data.string_u, &bool_pattern_true_re)) data_set_bool(data, true); else { /* try to auto detect the type and try again */ if (data_convert_type(data, DATA_TYPE_NONE) != DATA_TYPE_NONE) return _convert_data_force_bool(data); else { /* * not NULL or empty and unknown type, * so it must be true */ data_set_bool(data, true); } } return SLURM_SUCCESS; case DATA_TYPE_BOOL: return SLURM_SUCCESS; case DATA_TYPE_NULL: data_set_bool(data, false); return SLURM_SUCCESS; case DATA_TYPE_FLOAT: data_set_bool(data, data->data.float_u != 0); return SLURM_SUCCESS; case DATA_TYPE_INT_64: data_set_bool(data, data->data.int_u != 0); return SLURM_SUCCESS; default: return SLURM_ERROR; } return SLURM_ERROR; } static int _convert_data_null(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: if (_regex_quick_match(data->data.string_u, &bool_pattern_null_re)) { log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to null: %s->null", __func__, (uintptr_t) data, data->data.string_u); data_set_null(data); return SLURM_SUCCESS; } else { return SLURM_ERROR; } case DATA_TYPE_NULL: return SLURM_SUCCESS; default: return SLURM_ERROR; } return SLURM_ERROR; } static int _convert_data_bool(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: if (_regex_quick_match(data->data.string_u, &bool_pattern_true_re)) { log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to bool: %s->true", __func__, (uintptr_t) data, data->data.string_u); data_set_bool(data, true); return SLURM_SUCCESS; } else if (_regex_quick_match(data->data.string_u, &bool_pattern_false_re)) { log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to bool: %s->false", __func__, (uintptr_t) data, data->data.string_u); data_set_bool(data, false); return SLURM_SUCCESS; } else { return SLURM_ERROR; } case DATA_TYPE_BOOL: return SLURM_SUCCESS; default: return SLURM_ERROR; } return SLURM_ERROR; } static int _convert_data_int(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: if (_regex_quick_match(data->data.string_u, &bool_pattern_int_re)) { int64_t x; if (sscanf(data->data.string_u, "%"SCNd64, &x) == 1) { log_flag(DATA, "%s: converted data (0x%"PRIXPTR") to int: %s->%"PRId64, __func__, (uintptr_t) data, data->data.string_u, x); data_set_int(data, x); return SLURM_SUCCESS; } else { /* failed */ debug2("%s: sscanf of int failed: %s", __func__, data->data.string_u); return SLURM_ERROR; } } else { return SLURM_ERROR; } case DATA_TYPE_INT_64: return SLURM_SUCCESS; default: return SLURM_ERROR; } return SLURM_ERROR; } static int _convert_data_float(data_t *data) { _check_magic(data); switch (data->type) { case DATA_TYPE_STRING: if (_regex_quick_match(data->data.string_u, &bool_pattern_float_re)) { double x; if (sscanf(data->data.string_u, "%lf", &x) == 1) { log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to float: %s->%lf", __func__, (uintptr_t) data, data->data.string_u, x); data_set_float(data, x); return SLURM_SUCCESS; } else { /* failed */ error("%s: sscanf of double failed: %s", __func__, data->data.string_u); return SLURM_ERROR; } } else { return SLURM_ERROR; } case DATA_TYPE_FLOAT: return SLURM_SUCCESS; default: return SLURM_ERROR; } return SLURM_ERROR; } extern data_type_t data_convert_type(data_t *data, data_type_t match) { _check_magic(data); /* * This currently only works on primitive types and doesn't * apply to dictionaries or lists. */ xassert(data_get_type(data) != DATA_TYPE_DICT); xassert(data_get_type(data) != DATA_TYPE_LIST); switch (match) { case DATA_TYPE_STRING: return _convert_data_string(data) ? DATA_TYPE_NONE : DATA_TYPE_STRING; case DATA_TYPE_BOOL: return _convert_data_force_bool(data) ? DATA_TYPE_NONE : DATA_TYPE_BOOL; case DATA_TYPE_INT_64: return _convert_data_int(data) ? DATA_TYPE_NONE : DATA_TYPE_INT_64; case DATA_TYPE_FLOAT: return _convert_data_float(data) ? DATA_TYPE_NONE : DATA_TYPE_FLOAT; case DATA_TYPE_NULL: return _convert_data_null(data) ? DATA_TYPE_NONE : DATA_TYPE_NULL; case DATA_TYPE_NONE: if (!_convert_data_null(data)) return DATA_TYPE_NULL; if (!_convert_data_bool(data)) return DATA_TYPE_BOOL; if (!_convert_data_int(data)) return DATA_TYPE_INT_64; if (!_convert_data_float(data)) return DATA_TYPE_FLOAT; default: break; } return DATA_TYPE_NONE; } typedef struct { size_t count; data_type_t match; } convert_args_t; data_for_each_cmd_t _convert_list_entry(data_t *data, void *arg) { convert_args_t *args = arg; args->count += data_convert_tree(data, args->match); return DATA_FOR_EACH_CONT; } data_for_each_cmd_t _convert_dict_entry(const char *key, data_t *data, void *arg) { convert_args_t *args = arg; args->count += data_convert_tree(data, args->match); return DATA_FOR_EACH_CONT; } extern size_t data_convert_tree(data_t *data, const data_type_t match) { convert_args_t args = { .match = match }; _check_magic(data); if (!data) return 0; switch (data_get_type(data)) { case DATA_TYPE_DICT: data_dict_for_each(data, _convert_dict_entry, &args); break; case DATA_TYPE_LIST: data_list_for_each(data, _convert_list_entry, &args); break; default: if (match == data_convert_type(data, match)) args.count++; break; } return args.count; } typedef struct { const data_t *b; bool mask; } find_dict_match_t; data_for_each_cmd_t _find_dict_match(const char *key, const data_t *a, void *arg) { find_dict_match_t *p = arg; const data_t *b = data_key_get_const(p->b, key); if (data_check_match(a, b, p->mask)) return DATA_FOR_EACH_CONT; else return DATA_FOR_EACH_FAIL; } static bool _data_match_dict(const data_t *a, const data_t *b, bool mask) { find_dict_match_t p = { .mask = mask, .b = b, }; if (!a || data_get_type(a) != DATA_TYPE_DICT) return false; if (!b || data_get_type(b) != DATA_TYPE_DICT) return false; _check_magic(a); _check_magic(b); if (a->data.dict_u->count != b->data.dict_u->count) return false; /* match by key and not order with dictionary */ return (data_dict_for_each_const(a, _find_dict_match, &p) >= 0); } static bool _data_match_lists(const data_t *a, const data_t *b, bool mask) { bool fail = false; const data_list_node_t *ptr_a; const data_list_node_t *ptr_b; if (!a || data_get_type(a) != DATA_TYPE_LIST) return false; if (!b || data_get_type(b) != DATA_TYPE_LIST) return false; _check_magic(a); _check_magic(b); if (a->data.list_u->count != b->data.list_u->count) return false; ptr_a = a->data.list_u->begin; ptr_b = b->data.list_u->begin; while (ptr_a && !fail) { _check_data_list_node_magic(ptr_a); if (!ptr_b && mask) /* ignore a if b is NULL when masking */ continue; _check_data_list_node_magic(ptr_b); if (data_check_match(ptr_a->data, ptr_b->data, mask)) { ptr_a = ptr_a->next; ptr_b = ptr_b->next; } else fail = true; } return !fail; } extern bool data_check_match(const data_t *a, const data_t *b, bool mask) { if (a == NULL && b == NULL) return true; if (a == NULL || b == NULL) return false; _check_magic(a); _check_magic(b); if (data_get_type(a) != data_get_type(b)) return false; switch (data_get_type(a)) { case DATA_TYPE_NULL: return (data_get_type(b) == DATA_TYPE_NULL); case DATA_TYPE_STRING: // TODO: should we have a case insensitive compare? return !xstrcmp(data_get_string(a), data_get_string(b)); case DATA_TYPE_BOOL: return (data_get_bool(a) == data_get_bool(b)); case DATA_TYPE_INT_64: return data_get_int(a) == data_get_int(b); case DATA_TYPE_FLOAT: return fuzzy_equal(data_get_float(a), data_get_float(b)); case DATA_TYPE_DICT: return _data_match_dict(a, b, mask); case DATA_TYPE_LIST: return _data_match_lists(a, b, mask); default: fatal_abort("%s: unexpected data type", __func__); } } extern const data_t *data_resolve_dict_path(const data_t *data, const char *path) { const data_t *found = data; char *save_ptr = NULL; char *token = NULL; char *str = xstrdup(path); _check_magic(data); token = strtok_r(str, "/", &save_ptr); while (token && found) { xstrtrim(token); if (data_get_type(found) != DATA_TYPE_DICT) found = NULL; if (found) { found = data_key_get_const(found, token); token = strtok_r(NULL, "/", &save_ptr); } } xfree(str); if (found) log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved dictionary path \"%s\" to (0x%"PRIXPTR")", __func__, (uintptr_t) data, path, (uintptr_t) found); else log_flag(DATA, "%s: data (0x%"PRIXPTR") failed to resolve dictionary path \"%s\"", __func__, (uintptr_t) data, path); return found; } extern data_t *data_define_dict_path(data_t *data, const char *path) { data_t *found = data; char *save_ptr = NULL; char *token = NULL; char *str = xstrdup(path); _check_magic(data); token = strtok_r(str, "/", &save_ptr); while (token && found) { xstrtrim(token); if (data_get_type(found) == DATA_TYPE_NULL) data_set_dict(found); else if (data_get_type(found) != DATA_TYPE_DICT) found = NULL; if (found) { found = data_key_set(found, token); token = strtok_r(NULL, "/", &save_ptr); } } xfree(str); if (found) log_flag(DATA, "%s: data (0x%"PRIXPTR") defined dictionary path \"%s\" to (0x%"PRIXPTR")", __func__, (uintptr_t) data, path, (uintptr_t) found); else log_flag(DATA, "%s: data (0x%"PRIXPTR") failed to define dictionary path \"%s\"", __func__, (uintptr_t) data, path); return found; } data_t *data_copy(data_t *dest, const data_t *src) { _check_magic(src); _check_magic(dest); log_flag(DATA, "%s: copy data (0x%"PRIXPTR") to (0x%"PRIXPTR")", __func__, (uintptr_t) src, (uintptr_t) dest); switch (data_get_type(src)) { case DATA_TYPE_STRING: return data_set_string(dest, data_get_string(src)); case DATA_TYPE_BOOL: return data_set_bool(dest, data_get_bool(src)); case DATA_TYPE_INT_64: return data_set_int(dest, data_get_int(src)); case DATA_TYPE_FLOAT: return data_set_float(dest, data_get_float(src)); case DATA_TYPE_NULL: return data_set_null(dest); case DATA_TYPE_LIST: { data_list_node_t *i = src->data.list_u->begin; data_set_list(dest); while (i) { _check_data_list_node_magic(i); xassert(!i->key); data_copy(data_list_append(dest), i->data); i = i->next; } return dest; } case DATA_TYPE_DICT: { data_list_node_t *i = src->data.dict_u->begin; data_set_dict(dest); while (i) { _check_data_list_node_magic(i); data_copy(data_key_set(dest, i->key), i->data); i = i->next; } return dest; } default: fatal_abort("%s: unexpected data type", __func__); return NULL; } } extern int data_retrieve_dict_path_string(const data_t *data, const char *path, char **ptr_buffer) { const data_t *d = NULL; int rc; _check_magic(data); if (!(d = data_resolve_dict_path(data, path))) return SLURM_ERROR; rc = data_get_string_converted(d, ptr_buffer); log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to \"%s\"", __func__, (uintptr_t) data, path, *ptr_buffer); return rc; } extern int data_retrieve_dict_path_bool(const data_t *data, const char *path, bool *ptr_buffer) { const data_t *d = NULL; int rc; _check_magic(data); if (!(d = data_resolve_dict_path(data, path))) return SLURM_ERROR; rc = data_copy_bool_converted(d, ptr_buffer); log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to %s", __func__, (uintptr_t) data, path, (*ptr_buffer ? "true" : "false")); return rc; } extern int data_retrieve_dict_path_int(const data_t *data, const char *path, int64_t *ptr_buffer) { const data_t *d = NULL; int rc; _check_magic(data); if (!(d = data_resolve_dict_path(data, path))) return SLURM_ERROR; rc = data_get_int_converted(d, ptr_buffer); log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to %"PRId64, __func__, (uintptr_t) data, path, *ptr_buffer); return rc; } extern char *data_type_to_string(data_type_t type) { switch(type) { case DATA_TYPE_NULL: return "null"; case DATA_TYPE_LIST: return "list"; case DATA_TYPE_DICT: return "dictionary"; case DATA_TYPE_INT_64: return "int 64bits"; case DATA_TYPE_STRING: return "string"; case DATA_TYPE_FLOAT: return "floating point"; case DATA_TYPE_BOOL: return "boolean"; default: return "INVALID"; } }