/*****************************************************************************\ * layouts_mgr.c - layouts manager data structures and main functions ***************************************************************************** * Initially written by Francois Chevallier * at Bull for slurm-2.6. * Adapted by Matthieu Hautreux for slurm-14.11. * Enhanced by Matthieu Hautreux for slurm-15.x. * * 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 #include #include #include #include #include "layouts_mgr.h" #include "slurm/slurm.h" #include "slurm/slurm_errno.h" #include "src/common/entity.h" #include "src/common/layout.h" #include "src/common/hostlist.h" #include "src/common/list.h" #include "src/common/node_conf.h" #include "src/common/pack.h" #include "src/common/plugin.h" #include "src/common/read_config.h" #include "src/common/slurm_protocol_api.h" #include "src/common/strlcpy.h" #include "src/common/timers.h" #include "src/common/xstring.h" #include "src/common/xtree.h" #include "src/common/xmalloc.h" #define PATHLEN 256 /* use to specify which layout callbacks to perform while loading data * from conf files, state files or input buffers */ #define CONF_DONE 0x00000001 #define PARSE_ENTITY 0x00000002 #define UPDATE_DONE 0x00000004 #define PARSE_RELATIONS 0x00000008 /*****************************************************************************\ * STRUCTURES AND TYPES * \*****************************************************************************/ /* * layouts_conf_spec_t - structure used to keep track of layouts conf details */ typedef struct layouts_conf_spec_st { char* whole_name; char* name; char* type; } layouts_conf_spec_t; static void layouts_conf_spec_free(void* x) { layouts_conf_spec_t* spec = (layouts_conf_spec_t*)x; xfree(spec->whole_name); xfree(spec->type); xfree(spec->name); xfree(spec); } /* * layout ops - operations associated to layout plugins * * This struct is populated while opening the plugin and linking the * associated symbols. See layout_syms description for the name of the "public" * symbols associated to this structure fields. * * Notes : the layouts plugins are able to access the entities hashtable in order * to read/create/modify entities as necessary during the load_entities and * build_layout API calls. * */ typedef struct layout_ops_st { layouts_plugin_spec_t* spec; int (*conf_done) (xhash_t* entities, layout_t* layout, s_p_hashtbl_t* tbl); void (*entity_parsing) (entity_t* e, s_p_hashtbl_t* etbl, layout_t* layout); int (*update_done) (layout_t* layout, entity_t** e_array, int e_cnt); } layout_ops_t; /* * layout plugin symbols - must be synchronized with ops structure definition * as previously detailed, that's why though being a global constant, * it is placed in this section. */ const char *layout_syms[] = { "plugin_spec", /* holds constants, definitions, ... */ "layouts_p_conf_done", /* */ "layouts_p_entity_parsing", "layouts_p_update_done", }; /* * layout_plugin_t - it is the structure holding the plugin context of the * associated layout plugin as well as the ptr to the dlsymed calls. * It is used by the layouts manager to operate on the different layouts * loaded during the layouts framework initialization */ typedef struct layout_plugin_st { plugin_context_t* context; layout_t* layout; char* name; layout_ops_t* ops; } layout_plugin_t; static void _layout_plugins_destroy(layout_plugin_t *lp) { plugin_context_destroy(lp->context); /* it might be interesting to also dlclose the ops here */ xfree(lp->name); xfree(lp->ops); } /* * layouts_keydef_t - entities similar keys share a same key definition * in order to avoid loosing too much memory duplicating similar data * like the key str itself and custom destroy/dump functions. * * The layouts manager keeps an hash table of the various keydefs and use * the factorized details while parsing the configuration and creating the * entity_data_t structs associated to the entities. * * Note custom_* functions are used if they are not NULL* and type equals * L_T_CUSTOM */ typedef struct layouts_keydef_st { char* key; /* lower case key prefixed by the "%layout_type%." string */ char* shortkey; /* original key as defined in the layout keys definition */ layouts_keydef_types_t type; uint32_t flags; void (*custom_destroy)(void* value); char* (*custom_dump)(void* value); layout_plugin_t* plugin; char* ref_key; /* lower case reference key prefixed by the "%layout_type%." might be NULL if not defined. */ char* ref_shortkey; /* original ref key as defined in the layout keys definition, might be null too. */ } layouts_keydef_t; /* * layouts_keydef_idfunc - identity function to build an hash table of * layouts_keydef_t */ static void layouts_keydef_idfunc(void* item, const char** key, uint32_t* key_len) { layouts_keydef_t* keydef = (layouts_keydef_t*)item; *key = keydef->key; *key_len = strlen(keydef->key); } /* * layouts_mgr_t - the main structure holding all the layouts, entities and * shared keydefs as well as conf elements and plugins details. */ typedef struct layouts_mgr_st { pthread_mutex_t lock; bool init_done; /* Set if memory allocated for arrays/List */ layout_plugin_t *plugins; uint32_t plugins_count; List layouts_desc; /* list of the layouts requested in conf */ xhash_t *layouts; /* hash tbl of loaded layout structs (by type) */ xhash_t *entities; /* hash tbl of loaded entity structs (by name) */ xhash_t *keydefs; /* info on key types, how to free them etc */ } layouts_mgr_t; /*****************************************************************************\ * GLOBALS * \*****************************************************************************/ /** global structure holding layouts and entities */ static layouts_mgr_t layouts_mgr = {PTHREAD_MUTEX_INITIALIZER, false}; static layouts_mgr_t* mgr = &layouts_mgr; /*****************************************************************************\ * HELPERS * \*****************************************************************************/ /* entities added to the layouts mgr hash table come from the heap, * this function will help to free them while freeing the hash table */ static void _entity_free(void* item) { entity_t* entity = (entity_t*) item; entity_free(entity); xfree(entity); } /* layouts added to the layouts mgr hash table come from the heap, * this function will help to free them while freeing the hash table */ static void _layout_free(void* item) { layout_t* layout = (layout_t*) item; layout_free(layout); xfree(layout); } /* keydef added to the layouts mgr hash table come from the heap, * this function will help to free them while freeing the hash table */ static void _layouts_keydef_free(void* x) { layouts_keydef_t* keydef = (layouts_keydef_t*)x; xfree(keydef->key); xfree(keydef->shortkey); xfree(keydef->ref_key); xfree(keydef->ref_shortkey); xfree(keydef); } /* generic xfree callback */ static void xfree_as_callback(void* p) { xfree(p); } /* safer behavior than plain strncat */ static char* _cat(char* dest, const char* src, size_t n) { size_t len; char* r; if (n == 0) return dest; len = strlen(dest); if (n - len - 1 <= 0) { dest[n - 1] = 0; return dest; } r = strncat(dest, src, n - len - 1); dest[n - 1] = 0; return r; } static char* _trim(char* str) { char* str_modifier; if (!str) return str; while (*str && isspace(*str)) ++str; str_modifier = str + strlen(str) - 1; while (str_modifier >= str && isspace(*str_modifier)) { *str_modifier = '\0'; --str_modifier; } return str; } /* check if str is in strings (null terminated string array) */ /* TODO: replace this with a xhash instead for next modification */ static int _string_in_array(const char* str, const char** strings) { xassert(strings); /* if etypes no specified in plugin, no new entity should be created */ for (; *strings; ++strings) { if (!xstrcmp(str, *strings)) return 1; } return 0; } static void _normalize_keydef_keycore(char* buffer, uint32_t size, const char* key, const char* plugtype, bool cat) { int i; char keytmp[PATHLEN]; for (i = 0; plugtype[i] && i < PATHLEN - 1; ++i) { keytmp[i] = tolower(plugtype[i]); } keytmp[i] = 0; if (cat) { _cat(buffer, keytmp, size); } else { strlcpy(buffer, keytmp, size); } _cat(buffer, ".", size); for (i = 0; key[i] && i < PATHLEN - 1; ++i) { keytmp[i] = tolower(key[i]); } keytmp[i] = 0; _cat(buffer, keytmp, size); } static void _normalize_keydef_key(char* buffer, uint32_t size, const char* key, const char* plugtype) { _normalize_keydef_keycore(buffer, size, key, plugtype, false); } static void _normalize_keydef_mgrkey(char* buffer, uint32_t size, const char* key, const char* plugtype) { strlcpy(buffer, "mgr.", size); _normalize_keydef_keycore(buffer, size, key, plugtype, true); } static void _entity_add_data(entity_t* e, const char* key, void* data) { layouts_keydef_t* hkey = xhash_get_str(mgr->keydefs, key); xassert(hkey); void (*freefunc)(void* p) = xfree_as_callback; if (hkey && hkey->type == L_T_CUSTOM) { freefunc = hkey->custom_destroy; } entity_set_data_ref(e, hkey->key, data, freefunc); } /* * used in both automerge and autoupdate calls when dealing with * advanced operations (SUM,MIN,MAX,AVG,...) while setting new key values */ #define _entity_update_kv_helper(type_t, operator) \ type_t* lvalue = (type_t*) oldvalue; \ type_t* rvalue = (type_t*) value; \ uint32_t* divider; \ switch (operator) { \ case S_P_OPERATOR_SET: \ *lvalue = *rvalue; \ break; \ case S_P_OPERATOR_ADD: \ *lvalue += *rvalue; \ break; \ case S_P_OPERATOR_SUB: \ *lvalue -= *rvalue; \ break; \ case S_P_OPERATOR_MUL: \ *lvalue *= *rvalue; \ break; \ case S_P_OPERATOR_DIV: \ if (*rvalue != (type_t) 0) \ *lvalue /= *rvalue; \ else { \ error("layouts: entity_update: " \ "key=%s val=0 operator=" \ "DIV !! skipping !!", \ keydef->key); \ } \ break; \ case S_P_OPERATOR_AVG: \ divider = (uint32_t*) value; \ if (*divider != (uint32_t) 0) \ *lvalue /= (type_t) *divider; \ else { \ error("layouts: entity_update: " \ "key=%s val=0 operator=" \ "AVG !! skipping !!", \ keydef->key); \ } \ break; \ case S_P_OPERATOR_SET_IF_MIN: \ if (*rvalue < *lvalue) \ *lvalue = *rvalue; \ break; \ case S_P_OPERATOR_SET_IF_MAX: \ if (*rvalue > *lvalue) \ *lvalue = *rvalue; \ break; \ default: \ break; \ } static int _layouts_autoupdate_layout(layout_t* layout); static int _layouts_autoupdate_layout_if_allowed(layout_t* layout); /*****************************************************************************\ * LAYOUTS INTERNAL LOCKLESS API * \*****************************************************************************/ layouts_keydef_t* _layouts_entity_get_kv_keydef(layout_t* l, entity_t* e, char* key) { char keytmp[PATHLEN]; if (l == NULL || e == NULL || key == NULL) return NULL; _normalize_keydef_key(keytmp, PATHLEN, key, l->type); return xhash_get_str(mgr->keydefs, keytmp); } int _layouts_entity_get_kv_type(layout_t* l, entity_t* e, char* key) { layouts_keydef_t* keydef; keydef = _layouts_entity_get_kv_keydef(l, e, key); if (keydef != NULL) { return keydef->type; } return SLURM_ERROR; } int _layouts_entity_get_kv_flags(layout_t* l, entity_t* e, char* key) { layouts_keydef_t* keydef; keydef = _layouts_entity_get_kv_keydef(l, e, key); if (keydef != NULL) { return keydef->flags; } return SLURM_ERROR; } int _layouts_entity_get_kv_size(layout_t* l, entity_t* e, char* key, size_t *size) { layouts_keydef_t* keydef; keydef = _layouts_entity_get_kv_keydef(l, e, key); if (keydef != NULL) { switch(keydef->type) { case L_T_ERROR: return SLURM_ERROR; case L_T_STRING: *size = sizeof(void*); break; case L_T_CUSTOM: *size = sizeof(void*); break; case L_T_LONG: *size = sizeof(long); break; case L_T_UINT16: *size = sizeof(uint16_t); break; case L_T_UINT32: *size = sizeof(uint32_t); break; case L_T_BOOLEAN: *size = sizeof(bool); break; case L_T_FLOAT: *size = sizeof(float); break; case L_T_DOUBLE: *size = sizeof(double); break; case L_T_LONG_DOUBLE: *size = sizeof(long double); break; } } else return SLURM_ERROR; return SLURM_SUCCESS; } bool _layouts_entity_check_kv_keytype(layout_t* l, entity_t* e, char* key, layouts_keydef_types_t key_type) { layouts_keydef_types_t real_type; if (l == NULL || e == NULL || key == NULL) return SLURM_ERROR; if (key_type) { real_type = _layouts_entity_get_kv_type(l, e, key); return (real_type == key_type); } /* no key type provided, consider that as a no-check request */ return true; } int _layouts_entity_push_kv(layout_t* l, entity_t* e, char* key) { /* a more advanced implementation should only pull what is necessary * instead of forcing a full autoupdate */ return _layouts_autoupdate_layout_if_allowed(l); } int _layouts_entity_pull_kv(layout_t* l, entity_t* e, char* key) { /* a more advanced implementation should only pull what is necessary * instead of forcing a full autoupdate */ return _layouts_autoupdate_layout_if_allowed(l); } int _layouts_entity_set_kv(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { void* data; size_t size; layouts_keydef_types_t real_type; char key_keydef[PATHLEN]; if (l == NULL || e == NULL || key == NULL || value == NULL) return SLURM_ERROR; real_type = _layouts_entity_get_kv_type(l, e, key); if (key_type > 0 && real_type != key_type) return SLURM_ERROR; _normalize_keydef_key(key_keydef, PATHLEN, key, l->type); switch(real_type) { case L_T_ERROR: return SLURM_ERROR; case L_T_STRING: data = xstrdup(value); return entity_set_data_ref(e, key_keydef, data, xfree_as_callback); case L_T_CUSTOM: /* TBD : add a custom_set call */ value = NULL; return SLURM_ERROR; case L_T_LONG: size = sizeof(long); break; case L_T_UINT16: size = sizeof(uint16_t); break; case L_T_UINT32: size = sizeof(uint32_t); break; case L_T_BOOLEAN: size = sizeof(bool); break; case L_T_FLOAT: size = sizeof(float); break; case L_T_DOUBLE: size = sizeof(double); break; case L_T_LONG_DOUBLE: size = sizeof(long double); break; default: value = NULL; return SLURM_ERROR; } return entity_set_data(e, key_keydef, value, size); } int _layouts_entity_set_kv_ref(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; char key_keydef[PATHLEN]; if (l == NULL || e == NULL || key == NULL || value == NULL) return rc; if (!_layouts_entity_check_kv_keytype(l, e, key, key_type)) return rc; _normalize_keydef_key(key_keydef, PATHLEN, key, l->type); return entity_set_data_ref(e, key_keydef, value, xfree_as_callback); } int _layouts_entity_setpush_kv(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; if (_layouts_entity_set_kv(l, e, key, value, key_type) == SLURM_SUCCESS) rc = _layouts_entity_push_kv(l, e, key); return rc; } int _layouts_entity_setpush_kv_ref(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; if (_layouts_entity_set_kv_ref(l, e, key, value, key_type) == SLURM_SUCCESS) rc = _layouts_entity_push_kv(l, e, key); return rc; } int _layouts_entity_get_kv(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { void* data; size_t size; layouts_keydef_types_t real_type; char key_keydef[PATHLEN]; char ** pstr; if (l == NULL || e == NULL || key == NULL || value == NULL) return SLURM_ERROR; real_type = _layouts_entity_get_kv_type(l, e, key); if (key_type > 0 && real_type != key_type) return SLURM_ERROR; _normalize_keydef_key(key_keydef, PATHLEN, key, l->type); data = entity_get_data_ref(e, key_keydef); if (data == NULL) { return SLURM_ERROR; } switch(real_type) { case L_T_STRING: pstr = (char**) value; *pstr = xstrdup(data); return SLURM_SUCCESS; case L_T_CUSTOM: /* TBD : add a custom_get call */ pstr = (char**) value; *pstr = NULL; return SLURM_ERROR; case L_T_LONG: size = sizeof(long); break; case L_T_UINT16: size = sizeof(uint16_t); break; case L_T_UINT32: size = sizeof(uint32_t); break; case L_T_BOOLEAN: size = sizeof(bool); break; case L_T_FLOAT: size = sizeof(float); break; case L_T_DOUBLE: size = sizeof(double); break; case L_T_LONG_DOUBLE: size = sizeof(long double); break; case L_T_ERROR: default: return SLURM_ERROR; } memcpy(value, data, size); return SLURM_SUCCESS; } int _layouts_entity_get_mkv(layout_t* l, entity_t* e, char* keys, void* value, size_t length, layouts_keydef_types_t key_type) { char *key = NULL; hostlist_t kl; size_t processed = 0; size_t elt_size = sizeof(void*);; int rc = 0; /* expand in order the requested keys (in hostlist format) * and iterate over each one of them, collecting the different * values into the provided buffer. * if no more space is available in the buffer, then just count * the missing elements for the exit code. * the first error encountered fakes a full buffer to just add * the remaining keys to the missing elements count before * exiting. */ kl = hostlist_create(keys); while ((key = hostlist_shift(kl))) { if (processed >= length) { rc++; } else if (_layouts_entity_get_kv_size(l, e, key, &elt_size) || (processed + elt_size) > length || _layouts_entity_get_kv(l, e, key, value, key_type)) { rc++; processed = length; } else { value += elt_size; processed += elt_size; } free(key); } hostlist_destroy(kl); return rc; } int _layouts_entity_get_kv_ref(layout_t* l, entity_t* e, char* key, void** value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; char key_keydef[PATHLEN]; void* data; if (l == NULL || e == NULL || key == NULL || value == NULL) return rc; if (!_layouts_entity_check_kv_keytype(l, e, key, key_type)) return rc; _normalize_keydef_key(key_keydef, PATHLEN, key, l->type); data = entity_get_data_ref(e, key_keydef); if (data != NULL) { *value = data; rc = SLURM_SUCCESS; } return rc; } int _layouts_entity_get_mkv_ref(layout_t* l, entity_t* e, char* keys, void* value, size_t length, layouts_keydef_types_t key_type) { char *key = NULL; hostlist_t kl; size_t processed = 0; size_t elt_size = sizeof(void*); int rc = 0; /* expand in order the requested keys (in hostlist format) * and iterate over each one of them, collecting the different * references into the provided buffer. * if no more space is available in the buffer, then just count * the missing elements for the exit code. * the first error encountered fakes a full buffer to just add * the remaining keys to the missing elements count before * exiting. */ kl = hostlist_create(keys); while ((key = hostlist_shift(kl))) { if (processed >= length) { rc++; } else if (_layouts_entity_get_kv_ref(l, e, key, value, key_type)) { rc++; processed = length; } else { value += elt_size; processed += elt_size; } free(key); } hostlist_destroy(kl); return rc; } int _layouts_entity_pullget_kv(layout_t* l, entity_t* e, char* key, void* value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; if (!_layouts_entity_check_kv_keytype(l, e, key, key_type)) return rc; if (_layouts_entity_pull_kv(l, e, key) == SLURM_SUCCESS) rc = _layouts_entity_get_kv(l, e, key, value, key_type); return rc; } int _layouts_entity_pullget_kv_ref(layout_t* l, entity_t* e, char* key, void** value, layouts_keydef_types_t key_type) { int rc = SLURM_ERROR; if (!_layouts_entity_check_kv_keytype(l, e, key, key_type)) return rc; if (_layouts_entity_pull_kv(l, e, key) == SLURM_SUCCESS) rc = _layouts_entity_get_kv_ref(l, e, key, value, key_type); return rc; } /*****************************************************************************\ * MANAGER INIT * \*****************************************************************************/ static void _layouts_init_keydef(xhash_t* keydefs, const layouts_keyspec_t* plugin_keyspec, layout_plugin_t* plugin) { char keytmp[PATHLEN]; const layouts_keyspec_t* current; layouts_keydef_t* nkeydef; /* A layout plugin may have no data to store to entities but still * being valid. */ if (!plugin_keyspec) return; /* iterate over the keys of the plugin */ for (current = plugin_keyspec; current->key; ++current) { /* if not end of list, a keyspec key is mandatory */ _normalize_keydef_key(keytmp, PATHLEN, current->key, plugin->layout->type); xassert(xhash_get_str(keydefs, keytmp) == NULL); nkeydef = (layouts_keydef_t*) xmalloc(sizeof(layouts_keydef_t)); nkeydef->key = xstrdup(keytmp); nkeydef->shortkey = xstrdup(current->key); nkeydef->type = current->type; nkeydef->flags = current->flags; nkeydef->custom_destroy = current->custom_destroy; nkeydef->custom_dump = current->custom_dump; nkeydef->plugin = plugin; if (current->ref_key != NULL) { _normalize_keydef_key(keytmp, PATHLEN, current->ref_key, plugin->layout->type); nkeydef->ref_key = xstrdup(keytmp); nkeydef->ref_shortkey = xstrdup(current->ref_key); } else { nkeydef->ref_key = NULL; nkeydef->ref_shortkey = NULL; } xhash_add(keydefs, nkeydef); } /* then add keys managed by the layouts_mgr directly */ switch(plugin->layout->struct_type) { case LAYOUT_STRUCT_TREE: _normalize_keydef_mgrkey(keytmp, PATHLEN, "enclosed", plugin->layout->type); xassert(xhash_get_str(keydefs, keytmp) == NULL); nkeydef = (layouts_keydef_t*) xmalloc(sizeof(layouts_keydef_t)); nkeydef->key = xstrdup(keytmp); nkeydef->shortkey = xstrdup("Enclosed"); nkeydef->type = L_T_STRING; nkeydef->plugin = plugin; xhash_add(keydefs, nkeydef); break; } } static void _debug_output_keydefs (void* item, void* args) { layouts_keydef_t* keydef = (layouts_keydef_t*) item; debug3("layouts/keydefs: loaded: %s flags=0x%08lx refkey=%s", keydef->key, (long unsigned int) keydef->flags, (keydef->ref_key == NULL) ? "-":keydef->ref_key); } static int _layouts_init_layouts_walk_helper(void* x, void* arg) { layouts_conf_spec_t* spec = (layouts_conf_spec_t*)x; int* i = (int*)arg; layout_plugin_t* plugin = &mgr->plugins[*i]; const char* plugin_type = "layouts"; char plugin_name[PATHLEN]; void* inserted_item; plugin_context_t* plugin_context; snprintf(plugin_name, PATHLEN, "layouts/%s_%s", spec->type, spec->name); plugin->ops = (layout_ops_t*)xmalloc(sizeof(layout_ops_t)); debug2("layouts: loading %s...", spec->whole_name); plugin->context = plugin_context = plugin_context_create( plugin_type, plugin_name, (void**)plugin->ops, layout_syms, sizeof(layout_syms)); if (!plugin_context) { error("layouts: error loading %s.", plugin_name); return SLURM_ERROR; } if (!plugin->ops->spec) { error("layouts: plugin_spec must be valid (%s plugin).", plugin_name); return SLURM_ERROR; } plugin->name = xstrdup(spec->whole_name); plugin->layout = (layout_t*)xmalloc(sizeof(layout_t)); layout_init(plugin->layout, spec->name, spec->type, 0, plugin->ops->spec->struct_type); if ((inserted_item = xhash_add(mgr->layouts, plugin->layout))) xassert(inserted_item == plugin->layout); _layouts_init_keydef(mgr->keydefs, plugin->ops->spec->keyspec, plugin); xhash_walk(mgr->keydefs, _debug_output_keydefs, NULL); ++*i; return SLURM_SUCCESS; } static void _layouts_mgr_parse_global_conf(layouts_mgr_t* mgr) { char* layouts; char* parser; char* saveptr = NULL; char* slash; layouts_conf_spec_t* nspec; mgr->layouts_desc = list_create(layouts_conf_spec_free); layouts = slurm_get_layouts(); parser = strtok_r(layouts, ",", &saveptr); while (parser) { nspec = (layouts_conf_spec_t*)xmalloc( sizeof(layouts_conf_spec_t)); nspec->whole_name = xstrdup(_trim(parser)); slash = strchr(parser, '/'); if (slash) { *slash = 0; nspec->type = xstrdup(_trim(parser)); nspec->name = xstrdup(_trim(slash+1)); } else { nspec->type = xstrdup(_trim(parser)); nspec->name = xstrdup("default"); } list_append(mgr->layouts_desc, nspec); parser = strtok_r(NULL, ",", &saveptr); } xfree(layouts); } static void _layouts_mgr_free(layouts_mgr_t* mgr) { /* free the configuration details */ FREE_NULL_LIST(mgr->layouts_desc); /* FIXME: can we do a faster free here? since each node removal will * modify either the entities or layouts for back (or forward) * references. */ xhash_free(mgr->layouts); xhash_free(mgr->entities); xhash_free(mgr->keydefs); mgr->init_done = false; } static void _layouts_mgr_init(layouts_mgr_t* mgr) { if (mgr->init_done) _layouts_mgr_free(mgr); mgr->init_done = true; _layouts_mgr_parse_global_conf(mgr); mgr->layouts = xhash_init(layout_hashable_identify_by_type, _layout_free); mgr->entities = xhash_init(entity_hashable_identify, _entity_free); mgr->keydefs = xhash_init(layouts_keydef_idfunc, _layouts_keydef_free); } /*****************************************************************************\ * CONFIGURATION * \*****************************************************************************/ static char* _conf_get_filename(const char* type) { char path[PATHLEN]; char* final_path; strlcpy(path, "layouts.d/", PATHLEN); _cat(path, type, PATHLEN); _cat(path, ".conf", PATHLEN); final_path = get_extra_conf_path(path); return final_path; } static char* _state_get_filename(const char* type) { return xstrdup_printf("%s/layouts_state_%s", slurmctld_conf.state_save_location, type); } static s_p_hashtbl_t* _conf_make_hashtbl(int struct_type, const s_p_options_t* layout_options) { s_p_hashtbl_t* tbl = NULL; s_p_hashtbl_t* tbl_relational = NULL; s_p_hashtbl_t* tbl_layout = NULL; s_p_options_t* relational_options = NULL; /* generic line option */ static s_p_options_t global_options_entity[] = { {"Entity", S_P_STRING}, {"Type", S_P_STRING}, {NULL} }; static s_p_options_t global_options[] = { {"Priority", S_P_UINT32}, {"Entity", S_P_EXPLINE, NULL, NULL, global_options_entity}, {NULL} }; /* available for constructing a tree */ static s_p_options_t tree_options_entity[] = { {"Enclosed", S_P_STRING}, {NULL} }; static s_p_options_t tree_options[] = { {"Root", S_P_STRING}, {"Entity", S_P_EXPLINE, NULL, NULL, tree_options_entity}, {NULL} }; xassert(layout_options); switch(struct_type) { case LAYOUT_STRUCT_TREE: relational_options = tree_options; break; default: fatal("layouts: does not know what relation structure to " "use for type %d", struct_type); } tbl = s_p_hashtbl_create(global_options); tbl_relational = s_p_hashtbl_create(relational_options); tbl_layout = s_p_hashtbl_create(layout_options); s_p_hashtbl_merge_keys(tbl, tbl_relational); s_p_hashtbl_merge_keys(tbl, tbl_layout); s_p_hashtbl_destroy(tbl_relational); s_p_hashtbl_destroy(tbl_layout); return tbl; } #define _layouts_load_merge(type_t, s_p_get_type) { \ type_t rvalue; \ type_t* value = &rvalue; \ type_t* oldvalue; \ slurm_parser_operator_t operator = S_P_OPERATOR_SET; \ if (!s_p_get_type(&rvalue, option_key, etbl)) { \ /* no value to merge/create */ \ continue; \ } \ s_p_get_operator(&operator, option_key, etbl); \ oldvalue = (type_t*)entity_get_data_ref(e, key_keydef); \ if (oldvalue) { \ _entity_update_kv_helper(type_t, operator); \ } else { \ type_t* newalloc = (type_t*) \ xmalloc(sizeof(type_t)); \ *newalloc = *value; \ _entity_add_data(e, key_keydef, newalloc); \ } \ } \ #define _layouts_merge_check(type1, type2) \ (entity_option->type == type1 && keydef->type == type2) static void _layouts_load_automerge(layout_plugin_t* plugin, entity_t* e, s_p_hashtbl_t* etbl, uint32_t flags) { const s_p_options_t* layout_option; const s_p_options_t* entity_option; layouts_keydef_t* keydef; char key_keydef[PATHLEN]; char* option_key; for (layout_option = plugin->ops->spec->options; layout_option && xstrcasecmp("Entity", layout_option->key); ++layout_option); xassert(layout_option); for (entity_option = layout_option->line_options; entity_option->key; ++entity_option) { option_key = entity_option->key; _normalize_keydef_key(key_keydef, PATHLEN, option_key, plugin->layout->type); keydef = xhash_get_str(mgr->keydefs, key_keydef); if (!keydef) { /* key is not meant to be automatically handled, * ignore it for this function */ continue; } /* do not perform automerge on updates for read-only keys */ if (flags & UPDATE_DONE && keydef->flags & KEYSPEC_RDONLY) { debug4("layouts: do not try to merge RDONLY key '%s'", keydef->key); continue; } if (_layouts_merge_check(S_P_LONG, L_T_LONG)) { _layouts_load_merge(long, s_p_get_long); } else if (_layouts_merge_check(S_P_UINT16, L_T_UINT16)) { _layouts_load_merge(uint16_t, s_p_get_uint16); } else if (_layouts_merge_check(S_P_UINT32, L_T_UINT32)) { _layouts_load_merge(uint32_t, s_p_get_uint32); } else if (_layouts_merge_check(S_P_BOOLEAN, L_T_BOOLEAN)) { bool newvalue; if (s_p_get_boolean(&newvalue, option_key, etbl)) { bool *newalloc = xmalloc(sizeof(bool)); *newalloc = newvalue; _entity_add_data(e, key_keydef, newalloc); } } else if (_layouts_merge_check(S_P_LONG, L_T_LONG)) { _layouts_load_merge(long, s_p_get_long); } else if (_layouts_merge_check(S_P_FLOAT, L_T_FLOAT)) { _layouts_load_merge(float, s_p_get_float); } else if (_layouts_merge_check(S_P_DOUBLE, L_T_DOUBLE)) { _layouts_load_merge(double, s_p_get_double); } else if (_layouts_merge_check(S_P_LONG_DOUBLE, L_T_LONG_DOUBLE)) { _layouts_load_merge(long double, s_p_get_long_double); } else if (_layouts_merge_check(S_P_STRING, L_T_STRING)) { char* newvalue; if (s_p_get_string(&newvalue, option_key, etbl)) { _entity_add_data(e, key_keydef, newvalue); } } } } /* extract Enlosed= attributes providing the relational structures info */ static void _layouts_parse_relations(layout_plugin_t* plugin, entity_t* e, s_p_hashtbl_t* entity_tbl) { char* e_enclosed; char* e_already_enclosed; char* e_new_enclosed; char key[PATHLEN]; switch(plugin->layout->struct_type) { case LAYOUT_STRUCT_TREE: if (s_p_get_string(&e_enclosed, "Enclosed", entity_tbl)) { _normalize_keydef_mgrkey(key, PATHLEN, "enclosed", plugin->layout->type); e_already_enclosed = (char*) entity_get_data_ref(e, key); if (e_already_enclosed) { e_new_enclosed = (char*) xmalloc( strlen(e_already_enclosed) + strlen(e_enclosed) + 2); strcat(e_new_enclosed, e_already_enclosed); strcat(e_new_enclosed, ","); strcat(e_new_enclosed, e_enclosed); xfree(e_enclosed); e_enclosed = e_new_enclosed; } _entity_add_data(e, key, e_enclosed); } break; } } static int _layouts_read_config_post(layout_plugin_t* plugin, s_p_hashtbl_t* tbl) { char* root_nodename; entity_t* e; entity_node_t* enode; xtree_node_t* root_node; xtree_t* tree; switch(plugin->layout->struct_type) { case LAYOUT_STRUCT_TREE: tree = layout_get_tree(plugin->layout); xassert(tree); if (!s_p_get_string(&root_nodename, "Root", tbl)) { error("layouts: unable to construct the layout tree, " "no root node specified"); xfree(root_nodename); return SLURM_ERROR; } e = xhash_get_str(mgr->entities, _trim(root_nodename)); if (!e) { error("layouts: unable to find specified root " "entity `%s'", _trim(root_nodename)); xfree(root_nodename); return SLURM_ERROR; } xfree(root_nodename); if (!(enode = entity_add_node(e, plugin->layout))) xassert(enode); if (!(root_node = xtree_add_child( tree, NULL, enode, XTREE_APPEND))) xassert(root_node); enode->node = (void*) root_node; break; } return SLURM_SUCCESS; } /* * _layouts_load_config_common - called by layouts_read_config, * layouts_read_state or layouts_update_config with either a * filename or a buffer as well as a flag to indicate if it * is a full load or not (state save only) */ static int _layouts_load_config_common(layout_plugin_t* plugin, char* filename, Buf buffer, uint32_t flags) { s_p_hashtbl_t* tbl = NULL; s_p_hashtbl_t** entities_tbl = NULL; s_p_hashtbl_t* entity_tbl = NULL; int entities_tbl_count = 0, i; entity_t** updated_entities = NULL; int rc = SLURM_SUCCESS; uint32_t l_priority; entity_t* e; char* e_name = NULL; char* e_type = NULL; if (!plugin->ops->spec->options) { /* no option in this layout plugin, nothing to parse */ return SLURM_SUCCESS; } tbl = _conf_make_hashtbl(plugin->layout->struct_type, plugin->ops->spec->options); if (filename) { if (s_p_parse_file(tbl, NULL, filename, false) == SLURM_ERROR) { info("layouts: something went wrong when opening/reading " "'%s': %m", filename); rc = SLURM_ERROR; goto cleanup; } debug3("layouts: configuration file '%s' is loaded", filename); } else if (buffer) { if (s_p_parse_buffer(tbl, NULL, buffer, false) == SLURM_ERROR) { error("layouts: something went wrong when parsing " "buffer : %m"); rc = SLURM_ERROR; goto cleanup; } debug3("layouts: buffer loaded"); } else { error("layouts: invalid usage of _layouts_load_config_common"); rc = SLURM_ERROR; goto cleanup; } if (s_p_get_uint32(&l_priority, "Priority", tbl)) { plugin->layout->priority = l_priority; } /* get the config hash tables of the defined entities */ if (!s_p_get_expline(&entities_tbl, &entities_tbl_count, "Entity", tbl)) { error("layouts: no valid Entity found, can not append any " "information nor construct relations for %s/%s", plugin->layout->type, plugin->layout->name); rc = SLURM_ERROR; goto cleanup; } /* stage 0: xmalloc an array of entity_t* to save the updated entity_t * and give their references in the update_done layout callback */ updated_entities = (entity_t**) xmalloc(entities_tbl_count*sizeof(entity_t*)); /* stage 1: create the described entities or update them */ for (i = 0; i < entities_tbl_count; ++i) { updated_entities[i] = NULL; entity_tbl = entities_tbl[i]; xfree(e_name); xfree(e_type); if (!s_p_get_string(&e_name, "Entity", entity_tbl)) { info("layouts: no name associated to entity[%d], " "skipping...", i); rc = SLURM_ERROR; continue; } /* look for the entity in the entities hash table*/ e = xhash_get_str(mgr->entities, e_name); if (!e) { /* if the entity does not already exists, create it */ if (!s_p_get_string(&e_type, "Type", entity_tbl)) { info("layouts: entity '%s' does not already " "exists and no type was specified, " "skipping", e_name); rc = SLURM_ERROR; continue; } if (!_string_in_array(e_type, plugin->ops->spec->etypes)) { info("layouts: entity '%s' type (%s) is " "invalid, skipping", e_name, e_type); rc = SLURM_ERROR; continue; } e = (entity_t*)xmalloc(sizeof(entity_t)); entity_init(e, e_name, e_type); xhash_add(mgr->entities, e); } else if (s_p_get_string(&e_type, "Type", entity_tbl)) { /* if defined, check that the type is consistent */ if (!_string_in_array(e_type, plugin->ops->spec->etypes)) { info("layouts: entity '%s' type (%s) is " "invalid, skipping", e_name, e_type); rc = SLURM_ERROR; continue; } if (!e->type || xstrcmp(e_type, e->type)) { info("layouts: entity '%s' type (%s) differs " "from already registered entity type (%s)" " skipping", e_name, e_type, e->type); rc = SLURM_ERROR; continue; } } /* ** Full load config only (flags==0) ** * look for "Enclosed" pragmas identifying the relations * among entities and kep that along with the entity for * stage 2 */ if (flags & PARSE_RELATIONS) _layouts_parse_relations(plugin, e, entity_tbl); /* * if the layout plugin requests automerge, try to automatically * parse the conf hash table using the s_p_option_t description * of the plugin, creating the key/vlaue with the right value * type and adding them to the entity key hash table. */ if (plugin->ops->spec->automerge) { _layouts_load_automerge(plugin, e, entity_tbl, flags); } /* * in case the automerge was not sufficient, the layout parsing * callback is called for further actions. */ if ((flags & PARSE_ENTITY) && plugin->ops->entity_parsing) { plugin->ops->entity_parsing(e, entity_tbl, plugin->layout); } /* add the entity ref to the array for further usage when * calling the update_done layout callback */ updated_entities[i] = e; } xfree(e_name); xfree(e_type); /* ** Full load config only (flags==0) ** * post-read-and-build (post stage 1) * ensure that a Root entity was defined and set it as the root of * the relational structure of the layout. * fails in case of error as a root is mandatory to walk the relational * structure of the layout */ if ((flags & CONF_DONE) && _layouts_read_config_post(plugin, tbl) != SLURM_SUCCESS) { goto cleanup; } /* ** Full load config only (flags==0) ** * call the layout plugin conf_done callback for further * layout specific actions. */ if ((flags & CONF_DONE) && plugin->ops->conf_done) { if (!plugin->ops->conf_done(mgr->entities, plugin->layout, tbl)) { error("layouts: plugin %s/%s has an error parsing its" " configuration", plugin->layout->type, plugin->layout->name); rc = SLURM_ERROR; goto cleanup; } } /* * In case we are processing an update (not a startup configuration) * if the layout plugin requests autoupdate, call the autoupdate * function on the current layout in order to set the inherited values * according to the newly modified ones. * (in startup configuration, the autoupdate is performed in stage 3 * when the relational structures are available) */ if ((flags & UPDATE_DONE) && plugin->ops->spec->autoupdate) { _layouts_autoupdate_layout(plugin->layout); } /* * Call the layout plugin update_done callback for further * layout specific actions. * Note : some entries of the updated_entities array might be NULL * reflecting an issue while trying to analyze the corresponding * parsed hash table. */ if ((flags & UPDATE_DONE) && plugin->ops->update_done) { if (!plugin->ops->update_done(plugin->layout, updated_entities, entities_tbl_count)) { error("layouts: plugin %s/%s has an error reacting to" " entities update", plugin->layout->type, plugin->layout->name); rc = SLURM_ERROR; goto cleanup; } } xfree(updated_entities); cleanup: s_p_hashtbl_destroy(tbl); return rc; } /* * _layouts_read_config - called after base entities are loaded successfully * * This function is the stage 1 of the layouts loading stage, where we collect * info on all the entities and store them in a global hash table. * Entities that do not already exist are created, otherwise updated. * * Information concerning the relations among entities provided by the * 'Enclosed' conf pragma are also extracted here for further usage in stage 2. * * When layout plugins callbacks are called, relational structures among * entities are not yet built. */ static int _layouts_read_config(layout_plugin_t* plugin) { int rc; char* filename = _conf_get_filename(plugin->layout->type); if (!filename) { fatal("layouts: cannot find configuration file for " "required layout '%s'", plugin->name); } rc = _layouts_load_config_common(plugin, filename, NULL, CONF_DONE | PARSE_ENTITY | PARSE_RELATIONS); xfree(filename); return rc; } /* * _layouts_read_state - called to restore saved state of layout entities * * This function is the stage 1.1 of the layouts loading stage, where we collect * info on all the entities stored in the state of the layout and store/update * them in the global hash table. * * Information concerning the relations among entities provided by the * 'Enclosed' conf pragma are not taken into account for now and will be those * loaded during stage 1. * * No layout plugins callbacks are called when doing that for now. */ static int _layouts_read_state(layout_plugin_t* plugin) { int rc = SLURM_SUCCESS; struct stat stat_buf; char *filename = _state_get_filename(plugin->layout->type); if (!filename) { error("layouts: unable to build read state filename of layout" " '%s/%s'", plugin->layout->type, plugin->layout->name); return SLURM_ERROR; } /* check availability of the file otherwise it will later block * waiting for a file to appear (in s_p_parse_file) */ if (stat(filename, &stat_buf) < 0) { debug("layouts: skipping non existent state file for '%s/%s'", plugin->layout->type, plugin->layout->name); } else { rc = _layouts_load_config_common(plugin, filename, NULL, PARSE_ENTITY); } xfree(filename); return rc; } static int _layouts_update_state(layout_plugin_t* plugin, Buf buffer) { return _layouts_load_config_common(plugin, NULL, buffer, PARSE_ENTITY | UPDATE_DONE); } typedef struct _layouts_build_xtree_walk_st { layout_t* layout; char* enclosed_key; xtree_t* tree; } _layouts_build_xtree_walk_t; uint8_t _layouts_build_xtree_walk(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { _layouts_build_xtree_walk_t* p = (_layouts_build_xtree_walk_t*)arg; entity_t* e; entity_node_t* enode; char* enclosed_str; char* enclosed_name; hostlist_t enclosed_hostlist; entity_t* enclosed_e; xtree_node_t* enclosed_node; xassert(arg); /* get the entity from the entity node associated with the tree node */ enode = (entity_node_t*) xtree_node_get_data(node); xassert(enode); e = enode->entity; xassert(e); /* * FIXME: something goes wrong with the order... * after a first growing, the first new child is called with preorder. * * for now, testing each time and use enclosed_str to know if it has * been processed. */ if (which != XTREE_GROWING && which != XTREE_PREORDER) return 1; enclosed_str = (char*) entity_get_data_ref(e, p->enclosed_key); if (enclosed_str) { enclosed_hostlist = hostlist_create(enclosed_str); entity_delete_data(e, p->enclosed_key); while ((enclosed_name = hostlist_shift(enclosed_hostlist))) { enclosed_e = xhash_get_str(mgr->entities, enclosed_name); if (!enclosed_e) { error("layouts: entity '%s' specified in " "enclosed entities of entity '%s' " "not found, ignoring.", enclosed_name, e->name); free(enclosed_name); continue; } free(enclosed_name); /* create an entity node associated to the entity * for this layout */ enode = entity_add_node(enclosed_e, p->layout); xassert(enode); /* add it to the tree, getting an xtree_node_t ref */ if (!(enclosed_node = xtree_add_child( p->tree, node, enode, XTREE_APPEND))) xassert(enclosed_node); /* store the xtree_node_t ref in the entity node. It * will be used to access this layout tree from the * entity when necessary through the entity node */ enode->node = enclosed_node; } hostlist_destroy(enclosed_hostlist); } return 1; } /* * _layouts_build_relations - called after _layouts_read_config to create the * relational structure of the layout according to the topological * details parsed in stage 1. This is the stage 2 of the layouts * configuration load. * * This function is the stage 2 of the layouts loading stage, where we use * the relational details extracted from the parsing stage (Enclosed pragmas * and Root entity) to build the relational structure of the layout. * */ static int _layouts_build_relations(layout_plugin_t* plugin) { xtree_t* tree; xtree_node_t* root_node; char key[PATHLEN]; switch(plugin->layout->struct_type) { case LAYOUT_STRUCT_TREE: tree = layout_get_tree(plugin->layout); xassert(tree); root_node = xtree_get_root(tree); _normalize_keydef_mgrkey(key, PATHLEN, "enclosed", plugin->layout->type); _layouts_build_xtree_walk_t p = { plugin->layout, key, tree }; xtree_walk(tree, root_node, 0, XTREE_LEVEL_MAX, _layouts_build_xtree_walk, &p); break; } return SLURM_SUCCESS; } /*****************************************************************************\ * STATE DUMP * \*****************************************************************************/ /* * _pack_args_t : helper struct/type used when passing args among the various * functions used when packing layouts into a buffer as a set of strings. */ typedef struct _pack_args { Buf buffer; char *current_line; layout_t *layout; hostlist_t list_entities; char *type; uint32_t all; uint32_t flags; uint32_t record_count; } _pack_args_t; /* * _pack_data_key : internal function used to get the key=val * string representation of a particular entity data value */ static char* _pack_data_key(layouts_keydef_t* keydef, void* value) { char val; if (!keydef) { return NULL; } switch(keydef->type) { case L_T_ERROR: return NULL; case L_T_STRING: return xstrdup_printf("%s=%s", keydef->shortkey, (char*)value); case L_T_LONG: return xstrdup_printf("%s=%ld", keydef->shortkey, *(long*)value); case L_T_UINT16: return xstrdup_printf("%s=%u", keydef->shortkey, *(uint16_t*)value); case L_T_UINT32: return xstrdup_printf("%s=%"PRIu32, keydef->shortkey, *(uint32_t*)value); case L_T_BOOLEAN: val = *(bool*)value; return xstrdup_printf("%s=%s", keydef->shortkey, val ? "true" : "false"); case L_T_FLOAT: return xstrdup_printf("%s=%f", keydef->shortkey, *(float*)value); case L_T_DOUBLE: return xstrdup_printf("%s=%f", keydef->shortkey, *(double*)value); case L_T_LONG_DOUBLE: return xstrdup_printf("%s=%Lf", keydef->shortkey, *(long double*)value); case L_T_CUSTOM: if (keydef->custom_dump) { return keydef->custom_dump(value); } else return NULL; } return NULL; } /* * _pack_entity_layout_data : internal function used to append the * key/value of a entity data element corresponding to the targeted * layout when walking an entity list of entity data elements * * - append the " %key%=%val%" to the char* received as an input arg member * */ static void _pack_entity_layout_data(void* item, void* arg) { entity_data_t* data; _pack_args_t *pargs; layouts_keydef_t* keydef; char *data_dump; xassert(item); xassert(arg); data = (entity_data_t*) item; pargs = (_pack_args_t *) arg; /* the pack args must contain a valid char* to append to */ xassert(pargs->current_line); /* we must be able to get the keydef associated to the data key */ xassert(data); keydef = xhash_get_str(mgr->keydefs, data->key); xassert(keydef); /* only dump keys related to the targeted layout */ if (!xstrncmp(keydef->plugin->layout->type, pargs->layout->type, PATHLEN)) { data_dump = _pack_data_key(keydef, data->value); /* avoid printing any error in case of NULL pointer returned */ if (data_dump) { xstrcat(pargs->current_line, " "); xstrcat(pargs->current_line, data_dump); xfree(data_dump); } } return; } /* * _pack_layout_tree : internal function used when walking a layout tree * * - print one line per entity with the following pattern : * Entity=%name% [Type=%type%] [Enclosed=%childrens%] [key1=val1 ...] * * - potentially print an header line if the entity is the root like : * Root=%name% * */ static uint8_t _pack_layout_tree(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { _pack_args_t *pargs; xtree_node_t* child; entity_node_t* enode; hostlist_t enclosed; char *enclosed_str = NULL, *e_name = NULL, *e_type = NULL; Buf buffer; char *strdump, *str = NULL; /* only need to work for preorder and leaf cases */ if (which != XTREE_PREORDER && which != XTREE_LEAF) { return 1; } /* get the buffer we need to pack the data too */ pargs = (_pack_args_t *) arg; buffer = pargs->buffer; /* aggregate children names to build the Enclosed=.. value */ if (which == XTREE_PREORDER) { enclosed = hostlist_create(NULL); child = node->start; while (child) { enode = (entity_node_t*) xtree_node_get_data(child); if (!enode || !enode->entity) { hostlist_push(enclosed, "NULL"); } else { hostlist_push(enclosed, enode->entity->name); } child = child->next; } hostlist_uniq(enclosed); if (hostlist_count(enclosed) > 0) { enclosed_str = hostlist_ranged_string_xmalloc(enclosed); } hostlist_destroy(enclosed); } /* get the entity associated to this xtree node */ enode = (entity_node_t*) xtree_node_get_data(node); if (!enode || !enode->entity) { e_name = (char*) "NULL"; e_type = NULL; } else { e_name = enode->entity->name; e_type = enode->entity->type; } /* print this entity as root if necessary */ if (level == 0 && !(pargs->flags & LAYOUTS_DUMP_NOLAYOUT) && pargs->type == NULL) { if (pargs->all != 0 || pargs->list_entities == NULL || hostlist_find(pargs->list_entities, e_name) != -1) { str = xstrdup_printf("Root=%s\n", e_name); packstr(str, buffer); pargs->record_count++; xfree(str); } } /* print entity name and type when possible */ str = xstrdup_printf("Entity=%s", e_name); if (e_type) { strdump = xstrdup_printf("%s Type=%s", str, e_type); xfree(str); str = strdump; } /* add entity keys matching the layout to the current str */ pargs->current_line = str; if (enode && enode->entity) { xhash_walk(enode->entity->data, _pack_entity_layout_data, pargs); } /* the current line might have been extended/remalloced, so * we need to sync it again in str for further actions */ str = pargs->current_line; pargs->current_line = NULL; /* don't print enclosed if no_relation option */ if ((pargs->flags & LAYOUTS_DUMP_NOLAYOUT) && enclosed_str != NULL && pargs->list_entities == NULL) { xfree(enclosed_str); xfree(str); return 1; } /* don't print non enclosed if no "entities char*" option */ if (pargs->all == 0 && pargs->list_entities == NULL && enclosed_str == NULL ) { xfree(str); return 1; } /* don't print entities if not named in "entities char*" */ if (pargs->all == 0 && pargs->list_entities != NULL && hostlist_find(pargs->list_entities, e_name) == -1) { xfree(str); return 1; } /* don't print entities if not type of "type char*" */ if (pargs->type != NULL && (e_type == NULL || xstrcasecmp(e_type, pargs->type)!=0)) { xfree(str); return 1; } /* print enclosed entities if any */ if (!enclosed_str) { xstrcat(str, "\n"); } else { strdump = xstrdup_printf("%s Enclosed=%s\n", str, enclosed_str); xfree(enclosed_str); xfree(str); str = strdump; } packstr(str, buffer); pargs->record_count++; xfree(str); return 1; } /* helper function used by layouts_save_state when walking through * the various layouts to save their state in Slurm state save location */ static void _state_save_layout(void* item, void* arg) { layout_t* layout = (layout_t*)item; layouts_state_save_layout(layout->type); } /*****************************************************************************\ * ENTITIES KVs AUTOUPDATE * \*****************************************************************************/ /* * helper structure used when walking the tree of relational nodes in order * to automatically update the entities KVs based on their inheritance * relationships */ typedef struct _autoupdate_tree_args { entity_node_t* enode; uint8_t which; uint32_t level; } _autoupdate_tree_args_t; /* * helper function used to update a particular KV value of an entity according * to a particular operator looking for the right type to apply during the * operation */ static int _autoupdate_entity_kv(layouts_keydef_t* keydef, layouts_keydef_t* ref_keydef, slurm_parser_operator_t operator, void* oldvalue, void* value) { int rc = SLURM_ERROR; if (keydef->type != ref_keydef->type) return rc; if (keydef->type == L_T_LONG) { _entity_update_kv_helper(long, operator); } else if (keydef->type == L_T_UINT16) { _entity_update_kv_helper(uint16_t, operator); } else if (keydef->type == L_T_UINT32) { _entity_update_kv_helper(uint32_t, operator); } else if (keydef->type == L_T_FLOAT) { _entity_update_kv_helper(float, operator); } else if (keydef->type == L_T_DOUBLE) { _entity_update_kv_helper(double, operator); } else if (keydef->type == L_T_LONG_DOUBLE) { _entity_update_kv_helper(long double, operator); } else { // L_T_BOOLEAN, L_T_STRING, L_T_CUSTOM not yet supported return rc; } return SLURM_SUCCESS; } /* * helper function used to update KVs of an entity using its xtree_node * looking for known inheritance in the neighborhood (parents/children) */ static void _tree_update_node_entity_data(void* item, void* arg) { uint32_t action; entity_data_t* data; _autoupdate_tree_args_t *pargs; layouts_keydef_t* keydef; layouts_keydef_t* ref_keydef; slurm_parser_operator_t operator; xtree_node_t *node, *child; entity_node_t *enode, *cnode; void* oldvalue; void* value; uint32_t count; int setter; xassert(item); xassert(arg); data = (entity_data_t*) item; pargs = (_autoupdate_tree_args_t *) arg; cnode = pargs->enode; /* we must be able to get the keydef associated to the data key */ xassert(data); keydef = xhash_get_str(mgr->keydefs, data->key); xassert(keydef); /* only work on keys that depend of their neighborhood */ if (!(keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK) && !(keydef->flags & KEYSPEC_UPDATE_PARENTS_MASK)) { return; } /* if children dependant and we are at leaf level, nothing to do */ if (keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK && pargs->which == XTREE_LEAF) return; /* only work on keys related to the targeted layout */ if (xstrncmp(keydef->plugin->layout->type, pargs->enode->layout->type, PATHLEN)) { return; } /* get ref_key (identical if not defined) */ if (keydef->ref_key != NULL) { ref_keydef = xhash_get_str(mgr->keydefs, keydef->ref_key); if (!ref_keydef) { debug2("layouts: autoupdate: key='%s': invalid " "ref_key='%s'", keydef->key, keydef->ref_key); return; } } else { ref_keydef = keydef; } /* process parents aggregation * for now, xtree only provides one parent so any update op * (MAX, MIN, FSHARE, ...) is a setter */ if ((action = keydef->flags & KEYSPEC_UPDATE_PARENTS_MASK) && (pargs->which == XTREE_PREORDER || pargs->which == XTREE_LEAF) && (node = ((xtree_node_t*)pargs->enode->node)->parent) != NULL ) { /* get current node value reference */ oldvalue = entity_get_data_ref(cnode->entity, keydef->key); if (!oldvalue) return; /* get siblings count */ child = node->start; count = 0; while (child) { count++; child = child->next; } /* get parent node KV data ref */ enode = (entity_node_t*) xtree_node_get_data(node); value = entity_get_data_ref(enode->entity, ref_keydef->key); if (!value) return; /* only set operation currently provided for parents except * for fshare action */ _autoupdate_entity_kv(keydef, ref_keydef, S_P_OPERATOR_SET, oldvalue, value); if (action == KEYSPEC_UPDATE_PARENTS_FSHARE) { _autoupdate_entity_kv(keydef, ref_keydef, S_P_OPERATOR_AVG, oldvalue, (void*) &count); } return; } /* process children aggregation */ if ((action = keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK) && pargs->which == XTREE_ENDORDER) { /* get current node value reference */ oldvalue = entity_get_data_ref(cnode->entity, keydef->key); if (!oldvalue) return; /* get children count */ node = (xtree_node_t*)cnode->node; child = node->start; count = 0; while (child) { count++; child = child->next; } /* no action if no children */ if (count == 0) return; /* if count action, do what is necessary and return */ if (action == KEYSPEC_UPDATE_CHILDREN_COUNT) { _autoupdate_entity_kv(keydef, ref_keydef, S_P_OPERATOR_SET, oldvalue, (void*) &count); return; } /* iterate on the children */ setter = 1; child = node->start; while (child) { /* get child node KV data ref */ enode = (entity_node_t*) xtree_node_get_data(child); value = entity_get_data_ref(enode->entity, ref_keydef->key); if (!value) { /* try next child */ child = child-> next; continue; } switch (action) { case KEYSPEC_UPDATE_CHILDREN_SUM: case KEYSPEC_UPDATE_CHILDREN_AVG: /* first child is a setter */ if (setter) { operator = S_P_OPERATOR_SET; setter = 0; } else operator = S_P_OPERATOR_ADD; break; case KEYSPEC_UPDATE_CHILDREN_MIN: operator = S_P_OPERATOR_SET_IF_MIN; break; case KEYSPEC_UPDATE_CHILDREN_MAX: operator = S_P_OPERATOR_SET_IF_MAX; break; default: /* should not be called! */ return; } /* update the value according to the operator */ _autoupdate_entity_kv(keydef, ref_keydef, operator, oldvalue, value); /* then next child */ child = child-> next; } /* if average action, do what is necessary before return */ if (action == KEYSPEC_UPDATE_CHILDREN_AVG) { _autoupdate_entity_kv(keydef, ref_keydef, S_P_OPERATOR_AVG, oldvalue, (void*) &count); return; } return; } } /* * _autoupdate_layout_tree : internal function used when automatically * updating elements of a layout tree using _layouts_autoupdate_layout */ static uint8_t _autoupdate_layout_tree(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { entity_node_t* cnode; _autoupdate_tree_args_t sync_args; /* only need to work for preorder, leaf and endorder cases */ if (which != XTREE_PREORDER && which != XTREE_LEAF && which != XTREE_ENDORDER) { return 1; } /* extract current node entity_node to next browsing */ cnode = (entity_node_t*) xtree_node_get_data(node); if (!cnode) return 1; /* prepare downcall args */ sync_args.enode = cnode; sync_args.which = which; sync_args.level = level; /* iterate over the K/V of the entity, syncing them according * to their autoupdate flags */ xhash_walk(cnode->entity->data, _tree_update_node_entity_data, &sync_args); return 1; } /* helper function used to automatically update a layout internal * entities KVs based on inheritance relations (parents/children) */ static int _layouts_autoupdate_layout(layout_t* layout) { /* autoupdate according to the layout struct type */ switch(layout->struct_type) { case LAYOUT_STRUCT_TREE: xtree_walk(layout->tree, NULL, 0, XTREE_LEVEL_MAX, _autoupdate_layout_tree, NULL); break; } return SLURM_SUCCESS; } /* helper function used to automatically update a layout internal * entities KVs based on inheritance relations (parents/children) * only when allowed by the associated plugin */ static int _layouts_autoupdate_layout_if_allowed(layout_t* layout) { int i, rc = SLURM_ERROR; /* look if the corresponding layout plugin enables autoupdate */ for (i = 0; i < mgr->plugins_count; i++) { if (mgr->plugins[i].layout == layout) { /* no autoupdate allowed, return success */ if (!mgr->plugins[i].ops->spec->autoupdate) rc = SLURM_SUCCESS; else rc = _layouts_autoupdate_layout(layout); break; } } return rc; } /*****************************************************************************\ * DEBUG DUMP * \*****************************************************************************/ /* * For debug purposes, dump functions helping to print the layout mgr * internal states in a file after the load. */ #if 0 static char* _dump_data_key(layouts_keydef_t* keydef, void* value) { char val; if (!keydef) { return xstrdup("ERROR_bad_keydef"); } switch(keydef->type) { case L_T_ERROR: return xstrdup("ERROR_keytype!"); case L_T_STRING: return xstrdup((char*)value); case L_T_LONG: return xstrdup_printf("%ld", *(long*)value); case L_T_UINT16: return xstrdup_printf("%u", *(uint16_t*)value); case L_T_UINT32: return xstrdup_printf("%ul", *(uint32_t*)value); case L_T_BOOLEAN: val = *(bool*)value; return xstrdup_printf("%s", val ? "true" : "false"); case L_T_FLOAT: return xstrdup_printf("%f", *(float*)value); case L_T_DOUBLE: return xstrdup_printf("%f", *(double*)value); case L_T_LONG_DOUBLE: return xstrdup_printf("%Lf", *(long double*)value); case L_T_CUSTOM: if (keydef->custom_dump) { return keydef->custom_dump(value); } return xstrdup_printf("custom_ptr(%p)", value); } return NULL; } static void _dump_entity_data(void* item, void* arg) { entity_data_t* data = (entity_data_t*)item; FILE* fdump = (FILE*)arg; layouts_keydef_t* keydef; char* data_dump; xassert(data); keydef = xhash_get_str(mgr->keydefs, data->key); xassert(keydef); data_dump = _dump_data_key(keydef, data->value); fprintf(fdump, "data %s (type: %d): %s\n", data->key, keydef->type, data_dump); xfree(data_dump); } static void _dump_entities(void* item, void* arg) { entity_t* entity = (entity_t*)item; FILE* fdump = (FILE*)arg; fprintf(fdump, "-- entity %s --\n", entity->name); fprintf(fdump, "type: %s\nnode count: %d\nptr: %p\n", entity->type, list_count(entity->nodes), entity->ptr); xhash_walk(entity->data, _dump_entity_data, fdump); } static uint8_t _dump_layout_tree(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { FILE* fdump = (FILE*)arg; entity_t* e; entity_node_t* enode; if (which != XTREE_PREORDER && which != XTREE_LEAF) { return 1; } enode = (entity_node_t*) xtree_node_get_data(node); if (!enode || !enode->entity) { fprintf(fdump, "NULL_entity\n"); } else { fprintf(fdump, "%*s%s\n", level, " ", enode->entity->name); } return 1; } static void _dump_layouts(void* item, void* arg) { layout_t* layout = (layout_t*)item; FILE* fdump = (FILE*)arg; fprintf(fdump, "-- layout %s --\n" "type: %s\n" "priority: %u\n" "struct_type: %d\n" "relational ptr: %p\n", layout->name, layout->type, layout->priority, layout->struct_type, layout->tree); switch(layout->struct_type) { case LAYOUT_STRUCT_TREE: fprintf(fdump, "struct_type(string): tree, count: %d\n" "entities list:\n", xtree_get_count(layout->tree)); xtree_walk(layout->tree, NULL, 0, XTREE_LEVEL_MAX, _dump_layout_tree, fdump); break; } } #endif /*****************************************************************************\ * SLURM LAYOUTS API * \*****************************************************************************/ int layouts_init(void) { int i = 0; uint32_t layouts_count; debug3("layouts: layouts_init()..."); if (mgr->plugins) { return SLURM_SUCCESS; } slurm_mutex_lock(&layouts_mgr.lock); _layouts_mgr_init(&layouts_mgr); layouts_count = list_count(layouts_mgr.layouts_desc); if (layouts_count == 0) info("layouts: no layout to initialize"); else info("layouts: %d layout(s) to initialize", layouts_count); mgr->plugins = xmalloc(sizeof(layout_plugin_t) * layouts_count); list_for_each(layouts_mgr.layouts_desc, _layouts_init_layouts_walk_helper, &i); mgr->plugins_count = i; if (mgr->plugins_count != layouts_count) { error("layouts: only %d/%d layouts loaded, aborting...", mgr->plugins_count, layouts_count); for (i = 0; i < mgr->plugins_count; i++) { _layout_plugins_destroy(&mgr->plugins[i]); } xfree(mgr->plugins); mgr->plugins = NULL; } else if (layouts_count > 0) { info("layouts: layouts_init done : %d layout(s) " "initialized", layouts_count); } slurm_mutex_unlock(&layouts_mgr.lock); return mgr->plugins_count == layouts_count ? SLURM_SUCCESS : SLURM_ERROR; } int layouts_fini(void) { int i; debug3("layouts: layouts_fini()..."); /* push layouts states to the state save location */ layouts_state_save(); slurm_mutex_lock(&mgr->lock); /* * free the layouts before destroying the plugins, * otherwise we will get trouble xfreeing the layouts whose * memory is owned by the plugins structs */ _layouts_mgr_free(mgr); for (i = 0; i < mgr->plugins_count; i++) { _layout_plugins_destroy(&mgr->plugins[i]); } xfree(mgr->plugins); mgr->plugins = NULL; mgr->plugins_count = 0; slurm_mutex_unlock(&mgr->lock); info("layouts: all layouts are now unloaded."); return SLURM_SUCCESS; } int layouts_load_config(int recover) { int i, rc, inx; node_record_t *node_ptr; layout_t *layout; uint32_t layouts_count; entity_t *entity; entity_node_t *enode; void *ptr; info("layouts: loading entities/relations information"); rc = SLURM_SUCCESS; slurm_mutex_lock(&mgr->lock); if (xhash_count(layouts_mgr.entities)) { slurm_mutex_unlock(&mgr->lock); return rc; } /* * create a base layout to contain the configured nodes * Notes : it might be moved to its own external layout in the * slurm source layouts directory. */ layout = (layout_t*) xmalloc(sizeof(layout_t)); layout_init(layout, "slurm", "base", 0, LAYOUT_STRUCT_TREE); if (xtree_add_child(layout->tree, NULL, NULL, XTREE_APPEND) == NULL) { error("layouts: unable to create base layout tree root" ", aborting"); goto exit; } /* * generate and store the slurm node entities, * add them to the base layout at the same time */ for (inx = 0, node_ptr = node_record_table_ptr; inx < node_record_count; inx++, node_ptr++) { debug3("layouts: loading node %s", node_ptr->name); xassert (node_ptr->magic == NODE_MAGIC); xassert (node_ptr->config_ptr->magic == CONFIG_MAGIC); /* init entity structure on the heap */ entity = (entity_t*) xmalloc(sizeof(struct entity_st)); entity_init(entity, node_ptr->name, "Node"); entity->ptr = node_ptr; /* add to mgr entity hashtable */ if (xhash_add(layouts_mgr.entities,(void*)entity) == NULL) { error("layouts: unable to add entity of node %s" "in the hashtable, aborting", node_ptr->name); entity_free(entity); xfree(entity); rc = SLURM_ERROR; break; } /* add to the base layout (storing a callback ref to the * layout node pointing to it) */ enode = entity_add_node(entity, layout); ptr = xtree_add_child(layout->tree, layout->tree->root, (void*)enode, XTREE_APPEND); if (!ptr) { error("layouts: unable to add entity of node %s" "in the hashtable, aborting", node_ptr->name); entity_free(entity); xfree(entity); rc = SLURM_ERROR; break; } else { enode->node = ptr; } } debug("layouts: %d/%d nodes in hash table, rc=%d", xhash_count(layouts_mgr.entities), node_record_count, rc); if (rc != SLURM_SUCCESS) goto exit; /* add the base layout to the layouts manager dedicated hashtable */ if (xhash_add(layouts_mgr.layouts, (void*)layout) == NULL) { error("layouts: unable to add base layout into the hashtable"); layout_free(layout); rc = SLURM_ERROR; } /* check that we get as many layouts as initiliazed plugins * as layouts are added and referenced by type. * do +1 in the operation as the base layout is currently managed * separately. * If this base layout is moved to a dedicated plugin and automatically * added to the mgr layouts_desc at init, the +1 will have to be * removed here as it will be counted as the other plugins in the sum */ layouts_count = xhash_count(layouts_mgr.layouts); if ( layouts_count != mgr->plugins_count + 1) { error("layouts: %d/%d layouts added to hashtable, aborting", layouts_count, mgr->plugins_count+1); rc = SLURM_ERROR; } exit: if (rc != SLURM_SUCCESS) { layout_free(layout); xfree(layout); } else { debug("layouts: loading stage 1"); for (i = 0; i < mgr->plugins_count; ++i) { debug3("layouts: reading config for %s", mgr->plugins[i].name); if (_layouts_read_config(&mgr->plugins[i]) != SLURM_SUCCESS) { rc = SLURM_ERROR; break; } } if (recover) { debug("layouts: loading stage 1.1 (restore state)"); for (i = 0; i < mgr->plugins_count; ++i) { debug3("layouts: reading state of %s", mgr->plugins[i].name); _layouts_read_state(&mgr->plugins[i]); } } debug("layouts: loading stage 2"); for (i = 0; i < mgr->plugins_count; ++i) { debug3("layouts: creating relations for %s", mgr->plugins[i].name); if (_layouts_build_relations(&mgr->plugins[i]) != SLURM_SUCCESS) { rc = SLURM_ERROR; break; } } debug("layouts: loading stage 3"); for (i = 0; i < mgr->plugins_count; ++i) { debug3("layouts: autoupdating %s", mgr->plugins[i].name); if (mgr->plugins[i].ops->spec->autoupdate) { if (_layouts_autoupdate_layout(mgr->plugins[i]. layout) != SLURM_SUCCESS) { rc = SLURM_ERROR; break; } } } } /* * For debug purposes, print the layout mgr internal states * in a file after the load. */ #if 0 /* temporary section to test layouts */ FILE* fdump = fopen("/tmp/slurm-layouts-dump.txt", "wb"); xhash_walk(mgr->entities, _dump_entities, fdump); xhash_walk(mgr->layouts, _dump_layouts, fdump); if (fdump) fclose(fdump); #endif slurm_mutex_unlock(&mgr->lock); return rc; } layout_t* layouts_get_layout_nolock(const char* type) { return (layout_t*)xhash_get_str(mgr->layouts, type); } layout_t* layouts_get_layout(const char* type) { layout_t *layout = NULL; slurm_mutex_lock(&mgr->lock); layout = layouts_get_layout_nolock(type); slurm_mutex_unlock(&mgr->lock); return layout; } entity_t* layouts_get_entity_nolock(const char* name) { return (entity_t*)xhash_get_str(mgr->entities, name); } entity_t* layouts_get_entity(const char* name) { entity_t* e; slurm_mutex_lock(&mgr->lock); e = layouts_get_entity_nolock(name); slurm_mutex_unlock(&mgr->lock); return e; } int layouts_pack_layout(char *l_type, char *char_entities, char *type, uint32_t flags, Buf buffer) { _pack_args_t pargs; layout_t* layout; int orig_offset, fini_offset; char *str; slurm_mutex_lock(&mgr->lock); layout = layouts_get_layout_nolock(l_type); if (layout == NULL) { slurm_mutex_unlock(&mgr->lock); info("unable to get layout of type '%s'", l_type); return SLURM_ERROR; } /* initialize args for recursive packing */ pargs.buffer = buffer; pargs.layout = layout; pargs.current_line = NULL; pargs.all = 0; pargs.list_entities = NULL; if (char_entities != NULL) { if (xstrcmp(char_entities, "*") == 0) pargs.all = 1; else pargs.list_entities = hostlist_create(char_entities); } pargs.type = type; pargs.flags = flags; pargs.record_count = 0; orig_offset = get_buf_offset(buffer); pack32(pargs.record_count, buffer); /* start by packing the layout priority in case we are dumping state */ if (pargs.flags & LAYOUTS_DUMP_STATE) { str = xstrdup_printf("Priority=%u\n", layout->priority); packstr(str, buffer); pargs.record_count++; xfree(str); } /* pack according to the layout struct type */ switch (layout->struct_type) { case LAYOUT_STRUCT_TREE: xtree_walk(layout->tree, NULL, 0, XTREE_LEVEL_MAX, _pack_layout_tree, &pargs); break; } if (pargs.list_entities != NULL) slurm_hostlist_destroy(pargs.list_entities); fini_offset = get_buf_offset(buffer); set_buf_offset(buffer, orig_offset); pack32(pargs.record_count, buffer); set_buf_offset(buffer, fini_offset); slurm_mutex_unlock(&mgr->lock); return SLURM_SUCCESS; } int layouts_update_layout(char *l_type, Buf buffer) { int i, rc; slurm_mutex_lock(&mgr->lock); for (i = 0; i < mgr->plugins_count; i++) { if (!xstrcmp(mgr->plugins[i].name, l_type)) { rc = _layouts_update_state((layout_plugin_t*) &mgr->plugins[i], buffer); slurm_mutex_unlock(&mgr->lock); return rc; } } info("%s: no plugin matching layout=%s, skipping", __func__, l_type); slurm_mutex_unlock(&mgr->lock); return SLURM_ERROR; } int layouts_autoupdate_layout(char *l_type) { int rc = SLURM_ERROR; layout_t* layout; slurm_mutex_lock(&mgr->lock); layout = layouts_get_layout_nolock(l_type); if (layout == NULL) { info("unable to get layout of type '%s'", l_type); } else { rc = _layouts_autoupdate_layout(layout); } slurm_mutex_unlock(&mgr->lock); return rc; } int layouts_state_save_layout(char* l_type) { int error_code = 0, log_fd, offset; char *old_file = NULL, *new_file = NULL, *reg_file = NULL; static int high_buffer_size = (16 * 1024); Buf buffer = init_buf(high_buffer_size); FILE* fdump; uint32_t utmp32, record_count = 0; char *tmp_str = NULL; DEF_TIMERS; START_TIMER; /* pack the targeted layout into a tmp buffer */ error_code = layouts_pack_layout(l_type, "*", NULL, LAYOUTS_DUMP_STATE, buffer); if (error_code != SLURM_SUCCESS) { error("unable to save layout[%s] state", l_type); return error_code; } /* rewind the freshly created buffer to unpack it into a file */ offset = get_buf_offset(buffer); high_buffer_size = MAX(high_buffer_size, offset); set_buf_offset(buffer, 0); /* create working files */ reg_file = _state_get_filename(l_type); old_file = xstrdup_printf("%s.old", reg_file); new_file = xstrdup_printf("%s.new", reg_file); log_fd = creat(new_file, 0600); if (log_fd < 0 || !(fdump = fdopen(log_fd, "w"))) { error("Can't save state, create file %s error %m", new_file); error_code = errno; } else { /* extract the amount of records and then proceed * then dump packed strings into the temporary file */ safe_unpack32(&record_count, buffer); debug("layouts/%s: dumping %u records into state file", l_type, record_count); while (get_buf_offset(buffer) < offset) { safe_unpackstr_xmalloc(&tmp_str, &utmp32, buffer); if (tmp_str != NULL) { if (*tmp_str == '\0') { xfree(tmp_str); break; } fprintf(fdump, "%s", tmp_str); xfree(tmp_str); continue; } unpack_error: break; } fflush(fdump); fsync(log_fd); fclose(fdump); } if (error_code) (void) unlink(new_file); else { /* file shuffle */ (void) unlink(old_file); if (link(reg_file, old_file)) debug4("unable to create link for %s -> %s: %m", reg_file, old_file); (void) unlink(reg_file); if (link(new_file, reg_file)) debug4("unable to create link for %s -> %s: %m", new_file, reg_file); (void) unlink(new_file); } xfree(old_file); xfree(reg_file); xfree(new_file); free_buf(buffer); END_TIMER2("layouts_state_save_layout"); return SLURM_SUCCESS; } int layouts_state_save(void) { DEF_TIMERS; START_TIMER; xhash_walk(mgr->layouts, _state_save_layout, NULL); END_TIMER2("layouts_state_save"); return SLURM_SUCCESS; } #define _layouts_entity_wrapper(func, l, e, r...) \ layout_t* layout; \ entity_t* entity; \ int rc; \ slurm_mutex_lock(&mgr->lock); \ layout = layouts_get_layout_nolock(l); \ entity = layouts_get_entity_nolock(e); \ rc = func(layout, entity, ##r); \ slurm_mutex_unlock(&mgr->lock); \ return rc; \ int layouts_entity_get_kv_type(char* l, char* e, char* key) { _layouts_entity_wrapper(_layouts_entity_get_kv_type,l,e,key); } int layouts_entity_get_kv_flags(char* l, char* e, char* key) { _layouts_entity_wrapper(_layouts_entity_get_kv_flags, l, e, key); } int layouts_entity_push_kv(char* l, char* e, char* key) { _layouts_entity_wrapper(_layouts_entity_push_kv, l, e, key); } int layouts_entity_pull_kv(char* l, char* e, char* key) { _layouts_entity_wrapper(_layouts_entity_pull_kv, l, e, key); } int layouts_entity_set_kv(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_set_kv, l, e, key, value, key_type); } int layouts_entity_set_kv_ref(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_set_kv_ref, l, e, key, value, key_type); } int layouts_entity_setpush_kv(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_setpush_kv, l, e, key, value, key_type); } int layouts_entity_setpush_kv_ref(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_setpush_kv_ref, l, e, key, value, key_type); } int layouts_entity_get_kv(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_get_kv, l, e, key, value, key_type); } int layouts_entity_get_mkv(char* l, char* e, char* keys, void* value, size_t size, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_get_mkv, l, e, keys, value, size, key_type); } int layouts_entity_get_kv_ref(char* l, char* e, char* key, void** value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_get_kv_ref, l, e, key, value, key_type); } int layouts_entity_get_mkv_ref(char* l, char* e, char* keys, void* value, size_t size, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_get_mkv_ref, l, e, keys, value, size, key_type); } int layouts_entity_pullget_kv(char* l, char* e, char* key, void* value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_pullget_kv, l, e, key, value, key_type); } int layouts_entity_pullget_kv_ref(char* l, char* e, char* key, void** value, layouts_keydef_types_t key_type) { _layouts_entity_wrapper(_layouts_entity_pullget_kv_ref, l, e, key, value, key_type); }