/*****************************************************************************\
 *  slurm_protocol_defs.c - functions for initializing and releasing
 *	storage for RPC data structures. these are the functions used by
 *	the slurm daemons directly, not for user client use.
 *****************************************************************************
 *  Copyright (C) SchedMD LLC.
 *  Copyright (C) 2002-2007 The Regents of the University of California.
 *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Kevin Tew <tew1@llnl.gov> et. al.
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

#include "src/common/cron.h"
#include "src/common/forward.h"
#include "src/common/job_options.h"
#include "src/common/job_record.h"
#include "src/common/log.h"
#include "src/common/parse_time.h"
#include "src/common/sluid.h"
#include "src/common/slurm_protocol_defs.h"
#include "src/common/slurm_time.h"
#include "src/common/slurmdbd_defs.h"
#include "src/common/uid.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"

#include "src/interfaces/accounting_storage.h"
#include "src/interfaces/acct_gather_energy.h"
#include "src/interfaces/auth.h"
#include "src/interfaces/cred.h"
#include "src/interfaces/conn.h"
#include "src/interfaces/jobacct_gather.h"
#include "src/interfaces/select.h"
#include "src/interfaces/switch.h"
#include "src/interfaces/topology.h"

/*
** Define slurm-specific aliases for use by plugins, see slurm_xlator.h
** for details.
 */
strong_alias(preempt_mode_string, slurm_preempt_mode_string);
strong_alias(preempt_mode_num, slurm_preempt_mode_num);
strong_alias(job_share_string, slurm_job_share_string);
strong_alias(job_oversubscribe_string, slurm_job_oversubscribe_string);
strong_alias(job_exclusive_display_string, slurm_job_exclusive_display_string);
strong_alias(job_state_string, slurm_job_state_string);
strong_alias(job_state_string_compact, slurm_job_state_string_compact);
strong_alias(job_state_num, slurm_job_state_num);
strong_alias(valid_base_state, slurm_valid_base_state);
strong_alias(node_state_base_string, slurm_node_state_base_string);
strong_alias(node_state_flag_string, slurm_node_state_flag_string);
strong_alias(node_state_flag_string_single, slurm_node_state_flag_string_single);
strong_alias(node_state_string, slurm_node_state_string);
strong_alias(node_state_string_compact, slurm_node_state_string_compact);
strong_alias(node_state_string_complete, slurm_node_state_string_complete);
strong_alias(private_data_string, slurm_private_data_string);
strong_alias(accounting_enforce_string, slurm_accounting_enforce_string);
strong_alias(reservation_flags_string, slurm_reservation_flags_string);
strong_alias(print_multi_line_string, slurm_print_multi_line_string);

/*
 * Macro for implementing generic integer sort comparison
 *
 * param T: base type to cast void* to
 * param va: void * pointer to first item
 * param vb: void * pointer to other item
 */
#define SORT_INT_ASC(T, va, vb) \
	({T _a = *(T *) (va), _b = *(T *) (vb); \
	 ((_a < _b) ? -1 : (_a > _b) ? 1 : 0);})

/* It's just ASC with va and vb swapped */
#define SORT_INT_DESC(T, va, vb) SORT_INT_ASC(T, (vb), (va))

#define FIND_INT(T, va, vb) \
	({T _a = *(T *) (va), _b = *(T *) (vb); \
	 ((_a == _b) ? 1 : 0);})

typedef struct {
	uint32_t flag;
	const char *str;
} node_state_flags_t;

static const node_state_flags_t node_states[] = {
	{ NODE_STATE_DOWN, "DOWN" },
	{ NODE_STATE_IDLE, "IDLE" },
	{ NODE_STATE_ALLOCATED, "ALLOCATED" },
	{ NODE_STATE_ERROR, "ERROR" },
	{ NODE_STATE_MIXED, "MIXED" },
	{ NODE_STATE_FUTURE, "FUTURE" },
	{ NODE_STATE_UNKNOWN, "UNKNOWN" },
};

static const node_state_flags_t node_state_flags[] = {
	{ NODE_STATE_BLOCKED, "BLOCKED" },
	{ NODE_STATE_CLOUD, "CLOUD" },
	{ NODE_STATE_COMPLETING, "COMPLETING" },
	{ NODE_STATE_DRAIN, "DRAIN" },
	{ NODE_STATE_DYNAMIC_FUTURE, "DYNAMIC_FUTURE" },
	{ NODE_STATE_DYNAMIC_NORM, "DYNAMIC_NORM" },
	{ NODE_STATE_EXTERNAL, "EXTERNAL" },
	{ NODE_STATE_INVALID_REG, "INVALID_REG" },
	{ NODE_STATE_FAIL, "FAIL" },
	{ NODE_STATE_MAINT, "MAINTENANCE" },
	{ NODE_STATE_POWER_DOWN, "POWER_DOWN" },
	{ NODE_STATE_POWER_UP, "POWER_UP" },
	{ NODE_STATE_POWERED_DOWN, "POWERED_DOWN" },
	{ NODE_STATE_REBOOT_REQUESTED, "REBOOT_REQUESTED" },
	{ NODE_STATE_REBOOT_ISSUED, "REBOOT_ISSUED" },
	{ NODE_STATE_RES, "RESERVED" },
	{ NODE_RESUME, "RESUME" },
	{ NODE_STATE_NO_RESPOND, "NOT_RESPONDING" },
	{ NODE_STATE_PLANNED, "PLANNED" },
	{ NODE_STATE_POWERING_UP, "POWERING_UP" },
	{ NODE_STATE_POWERING_DOWN, "POWERING_DOWN" },
};

static const struct {
	char *name;
	int step_id;
} step_names[] = {
	{ "TBD", SLURM_PENDING_STEP },
	{ "extern", SLURM_EXTERN_CONT },
	{ "batch", SLURM_BATCH_SCRIPT },
	{ "interactive", SLURM_INTERACTIVE_STEP },
};

static void _free_all_job_info (job_info_msg_t *msg);

static void _free_all_node_info (node_info_msg_t *msg);

static void _free_all_partitions (partition_info_msg_t *msg);

static void  _free_all_reservations(reserve_info_msg_t *msg);

static void _free_all_step_info (job_step_info_response_msg_t *msg);

static char *_convert_to_id(char *name, bool gid)
{
	char *tmp_name;
	if (gid) {
		gid_t gid;
		if (gid_from_string( name, &gid )) {
			error("Invalid group id: %s", name);
			return NULL;
		}
		tmp_name = xstrdup_printf("%u", gid);
	} else {
		uid_t uid;
		if (uid_from_string( name, &uid )) {
			error("Invalid user id: %s", name);
			return NULL;
		}
		tmp_name = xstrdup_printf("%u", uid);
	}
	return tmp_name;
}

/*
 * Translate a string, with optional suffix, into its equivalent numeric value
 * tok IN - the string to translate
 * value IN - numeric value
 * RET true if "tok" is a valid number
 */
static bool _is_valid_number(char *tok, uint64_t *value)
{
	uint64_t tmp_val = 1;
	uint64_t mult;
	char *end_ptr = NULL;

	if (isdigit(tok[0])) {
		tmp_val = strtoull(tok, &end_ptr, 10);
		if (tmp_val == ULLONG_MAX)
			return false;
	} else
		return false;

	if ((mult = suffix_mult(end_ptr)) == NO_VAL64)
		return false;
	tmp_val *= mult;
	*value = tmp_val;
	return true;
}

/*
 * slurm_msg_t_init - initialize a slurm message
 * OUT msg - pointer to the slurm_msg_t structure which will be initialized
 */
extern void slurm_msg_t_init(slurm_msg_t *msg)
{
	*msg = (slurm_msg_t) SLURM_MSG_INITIALIZER;
}

extern int slurm_msg_t_init_address(slurm_msg_t *msg)
{
	int rc = EINVAL, fd = -1;
	conn_t *conn = NULL;

	if (!msg)
		return rc;

	/* Check if already populated */
	if (msg->address.ss_family != AF_UNSPEC)
		return SLURM_SUCCESS;

	if (msg->conmgr_con) {
		/* conmgr should always populate msg->address */
		xassert(msg->address.ss_family != AF_UNSPEC);
		return SLURM_COMMUNICATIONS_MISSING_SOCKET_ERROR;
	}

	if (msg->conn)
		conn = msg->conn;
	else if (msg->pcon)
		conn = msg->pcon->conn;

	if (!conn)
		return SLURM_COMMUNICATIONS_MISSING_SOCKET_ERROR;

	if ((fd = conn_g_get_fd(conn)) < 0)
		return SLURM_COMMUNICATIONS_INVALID_FD;

	if ((rc = slurm_get_peer_addr(fd, &msg->address)))
		log_flag(NET, "%s: [fd:%d] resolving peer failed: %s",
			 __func__, fd, slurm_strerror(rc));

	return rc;
}

/*
 * slurm_msg_t_copy - initialize a slurm_msg_t structure "dest" with
 *	values from the "src" slurm_msg_t structure.
 * IN src - Pointer to the initialized message from which "dest" will
 *	be initialized.
 * OUT dest - Pointer to the slurm_msg_t which will be initialized.
 * NOTE: the "dest" structure will contain pointers into the contents of "src".
 */
extern void slurm_msg_t_copy(slurm_msg_t *dest, slurm_msg_t *src)
{
	slurm_msg_t_init(dest);
	dest->protocol_version = src->protocol_version;
	dest->forward = src->forward;
	dest->ret_list = src->ret_list;
	dest->forward_struct = src->forward_struct;

#if 0
	/* explicitly blow away the address. probably redundant */
	if (dest->orig_addr.ss_family == AF_INET6) {
		struct sockaddr_in6 *sin =
			(struct sockaddr_in6 *) &dest->orig_addr;
		memset(&sin->sin6_addr, 0, 16);
	} else {
		struct sockaddr_in *sin =
			(struct sockaddr_in *) &dest->orig_addr;
		sin->sin_addr.s_addr = 0;
	}
#endif

	dest->orig_addr.ss_family = AF_UNSPEC;
	if (src->auth_ids_set)
		slurm_msg_set_r_uid(dest, src->auth_uid);

	dest->conn = src->conn;
}

/* here to add \\ to all \" in a string this needs to be xfreed later */
extern char *slurm_add_slash_to_quotes(char *str)
{
	char *dup, *copy = NULL;
	int len = 0;
	if (!str || !(len = strlen(str)))
		return NULL;

	/* make a buffer 2 times the size just to be safe */
	copy = dup = xmalloc((2 * len) + 1);
	if (copy)
		do if (*str == '\\' || *str == '\'' || *str == '"')
			   *dup++ = '\\';
		while ((*dup++ = *str++));

	return copy;
}

extern list_t *slurm_copy_char_list(list_t *char_list)
{
	list_t *ret_list = NULL;
	char *tmp_char = NULL;
	list_itr_t *itr = NULL;

	if (!char_list || !list_count(char_list))
		return NULL;

	itr = list_iterator_create(char_list);
	ret_list = list_create(xfree_ptr);

	while ((tmp_char = list_next(itr)))
		list_append(ret_list, xstrdup(tmp_char));

	list_iterator_destroy(itr);

	return ret_list;
}

/*
 * ListFindF to find exact string in char pointer List.
 *
 * IN: x, list data (char *).
 * IN: key, string to be found.
 *
 * RET: 1 if found, 0 otherwise
 */
extern int slurm_find_char_exact_in_list(void *x, void *key)
{
	char *str1 = x;
	char *str2 = key;

	if (!xstrcmp(str1, str2))
		return 1;

	return 0;
}

extern int slurm_find_char_in_list(void *x, void *key)
{
	char *char1 = (char *)x;
	char *char2 = (char *)key;

	if (!xstrcasecmp(char1, char2))
		return 1;

	return 0;
}

extern int slurm_find_ptr_in_list(void *x, void *key)
{
	if (x == key)
		return 1;
	return 0;
}

extern int slurm_find_uint16_in_list(void *x, void *key)
{
	return FIND_INT(uint16_t, x, key);
}

extern int slurm_find_uint32_in_list(void *x, void *key)
{
	return FIND_INT(uint32_t, x, key);
}

extern int slurm_find_uint64_in_list(void *x, void *key)
{
	return FIND_INT(uint64_t, x, key);
}

extern int slurm_find_uint_in_list(void *x, void *key)
{
	return FIND_INT(unsigned int, x, key);
}

extern int slurm_find_int_in_list(void *x, void *key)
{
	return FIND_INT(int, x, key);
}

extern int slurm_find_int64_in_list(void *x, void *key)
{
	return FIND_INT(int64_t, x, key);
}

static int _char_list_append_str(void *x, void *arg)
{
	char  *char_item = (char *)x;
	char **out_str   = (char **)arg;

	xassert(char_item);
	xassert(out_str);

	xstrfmtcat(*out_str, "%s%s", *out_str ? "," : "", char_item);

	return SLURM_SUCCESS;
}

extern char *slurm_char_list_to_xstr(list_t *char_list)
{
	char *out = NULL;

	if (!char_list)
		return NULL;

	list_sort(char_list, (ListCmpF)slurm_sort_char_list_asc);
	list_for_each(char_list, _char_list_append_str, &out);

	return out;
}

static int _for_each_remove_str_from_list(void *x, void *arg)
{
	char *rem_str = x;
	list_t *from_list = arg;

	list_delete_all(from_list, slurm_find_char_exact_in_list, rem_str);

	return 0;
}

extern void slurm_remove_char_list_from_char_list(list_t *haystack,
						  list_t *needles)
{
	list_for_each(needles, _for_each_remove_str_from_list, haystack);
}

static int _char_list_copy(void *item, void *dst)
{
	list_append((list_t *) dst, xstrdup((char *)item));
	return SLURM_SUCCESS;
}

extern int slurm_char_list_copy(list_t *dst, list_t *src)
{
	xassert(dst);
	xassert(src);

	list_for_each(src, _char_list_copy, dst);

	return SLURM_SUCCESS;
}

extern int slurm_parse_char_list(
	list_t *char_list, char *names, void *args,
	int (*func_ptr)(list_t *char_list, char *name, void *args))
{
	int i = 0, start = 0, count = 0, result = 0;
	char quote_c = '\0';
	int quote = 0;
	char *tmp_names;

	if (!names)
		return 0;

	tmp_names = xstrdup(names);

	if ((tmp_names[i] == '\"') || (tmp_names[i] == '\'')) {
		quote_c = tmp_names[i];
		quote = 1;
		i++;
	}
	start = i;
	while (tmp_names[i]) {
		if (quote && (tmp_names[i] == quote_c)){
			tmp_names[i] = '\0';
			break;
		} else if ((tmp_names[i] == '\"') || (tmp_names[i] == '\''))
			tmp_names[i] = '`';
		else if (tmp_names[i] == ',') {
			if (i != start) {
				tmp_names[i] = '\0';
				result = (*func_ptr)(char_list,
						     tmp_names + start, args);
				tmp_names[i] = ',';

				if (result == SLURM_ERROR) {
					xfree(tmp_names);
					return SLURM_ERROR;
				} else
					count += result;
			}
			start = i + 1;
		}
		i++;
	}

	if (tmp_names[start]) {
		result = (*func_ptr)(char_list, tmp_names + start, args);
		if (result == SLURM_ERROR) {
			xfree(tmp_names);
			return SLURM_ERROR;
		} else
			count += result;
	}
	xfree(tmp_names);

	return count;
}

extern int slurm_addto_char_list(list_t *char_list, char *names)
{
	return slurm_addto_char_list_with_case(char_list, names, true);
}

static void _add_to_list(char *name,
			 list_t *char_list,
			 bool lower_case_normalization)
{
	/*
	 * If we get a duplicate remove the first one and tack this on the end.
	 * This is needed for get associations with QOS.
	 */
	xstrtrim(name);
	if (lower_case_normalization)
		xstrtolower(name);
	list_delete_all(char_list, slurm_find_char_exact_in_list, name);
	list_append(char_list, name);
}

/* returns number of objects added to list */
extern int slurm_addto_char_list_with_case(list_t *char_list, char *names,
					   bool lower_case_normalization)
{
	int i = 0, start = 0, cnt = 0;
	char *name = NULL;
	char quote_c = '\0';
	int quote = 0;
	bool brack_not = false;
	bool first_brack = false;
	char *this_node_name;
	char *tmp_this_node_name;
	hostlist_t *host_list;

	if (!char_list) {
		error("No list was given to fill in");
		return 0;
	}

	if (names) {
		if (names[i] == '\"' || names[i] == '\'') {
			quote_c = names[i];
			quote = 1;
			i++;
		}
		start = i;
		cnt = list_count(char_list);
		while (names[i]) {
			//info("got %d - %d = %d", i, start, i-start);
			if (quote && (names[i] == quote_c))
				break;
			else if ((names[i] == '\"') || (names[i] == '\''))
				names[i] = '`';
			else if (names[i] == '[')
			       /*
				* Make sure there is a open bracket. This
				* check is to allow comma-separated notation
				* within the bracket (e.g. "linux[0-1,2]").
				*/
				first_brack = true;
			else if (names[i] == ',' && !first_brack) {
				/* Check that the string before , was
				 * not a [] notation value */
				if (!brack_not) {
					/*
					 * If there is a comma at the end just
					 * ignore it
					 */
					if (!names[i+1])
						break;

					/*
					 * Only add the non-blank names to the
					 * list
					 */
					if (i != start) {
						name = xstrndup(names+start,
								(i-start));

						_add_to_list(name, char_list,
							     lower_case_normalization);
					}

					/*
					 * This line used to be "start = ++i".
					 * If we increment i too early, we will
					 * get issues with a list such as
					 * ",,this".
					 */
					start = i + 1;
				} else {
					brack_not = false;
					/*
					 * Skip over the "," so it is
					 * not included in the char list
					 */
					start = i + 1;
				}
			} else if (names[i] == ']') {
				brack_not = true;
				first_brack = false;
				name = xstrndup(names+start, ((i + 1)-start));
				//info("got %s %d", name, i-start);

				if ((host_list = hostlist_create(name))) {
					while ((tmp_this_node_name =
						hostlist_shift(host_list))) {
						/*
						 * Move from malloc-ed to
						 * xmalloc-ed memory
						 */
						this_node_name =
						    xstrdup(tmp_this_node_name);
						free(tmp_this_node_name);

						_add_to_list(this_node_name,
							     char_list,
							     lower_case_normalization);

						start = i + 1;
					}
				}
				hostlist_destroy(host_list);
				xfree(name);
			}
			i++;
		}

		/* check for empty strings user='' etc */
		if ((cnt == list_count(char_list)) || (i - start)) {
			name = xstrndup(names+start, (i-start));

			_add_to_list(name, char_list, lower_case_normalization);
		}
	}

	return list_count(char_list);
}

/* Parses string and converts names to either uid or gid list */
static int _slurm_addto_id_char_list_internal(list_t *char_list, char *name,
					      void *x)
{
	bool gid = *(bool *)x;
	char *tmp_name = _convert_to_id(name, gid);
	if (!tmp_name) {
		list_flush(char_list);
		return SLURM_ERROR;
	}

	if (!list_find_first(char_list, slurm_find_char_in_list, tmp_name)) {
		list_append(char_list, tmp_name);
		return 1;
	} else {
		xfree(tmp_name);
		return 0;
	}
}

/* Parses string and converts names to either uid or gid list */
extern int slurm_addto_id_char_list(list_t *char_list, char *names, bool gid)
{
	if (!char_list) {
		error("No list was given to fill in");
		return 0;
	}

	return slurm_parse_char_list(char_list, names, &gid,
				     _slurm_addto_id_char_list_internal);
}

typedef struct {
	bool add_set;
	bool equal_set;
	int mode;
} char_list_internal_args_t;

static int _slurm_addto_mode_char_list_internal(list_t *char_list, char *name,
						void *args_in)
{
	char *tmp_name = NULL;
	char_list_internal_args_t *args = args_in;
	char *err_msg = "You can't use '=' and '+' or '-' in the same line";

	int tmp_mode = args->mode;
	if ((name[0] == '+') || (name[0] == '-')) {
		tmp_mode = name[0];
		name++;
	}
	if (tmp_mode) {
		if (args->equal_set) {
			error("%s", err_msg);
			list_flush(char_list);
			return SLURM_ERROR;
		}
		args->add_set = 1;
		tmp_name = xstrdup_printf("%c%s", tmp_mode, name);
	} else {
		if (args->add_set) {
			error("%s", err_msg);
			list_flush(char_list);
			return SLURM_ERROR;
		}
		args->equal_set = 1;
		tmp_name = xstrdup_printf("%s", name);
	}

	if (!list_find_first(char_list, slurm_find_char_in_list, tmp_name)) {
		list_append(char_list, tmp_name);
		return 1;
	} else {
		xfree(tmp_name);
		return 0;
	}
}

/* Parses strings such as stra,+strb,-strc and appends the default mode to each
 * string in the list if no specific mode is listed.
 * RET: returns the number of items added to the list. -1 on error. */
extern int slurm_addto_mode_char_list(list_t *char_list, char *names, int mode)
{
	char_list_internal_args_t args = {0};

	args.mode = mode;

	if (!char_list) {
		error("No list was given to fill in");
		return 0;
	}

	return slurm_parse_char_list(char_list, names, &args,
			      	     _slurm_addto_mode_char_list_internal);
}

static int _addto_step_list_internal(list_t *step_list, char *name, void *x)
{
	slurm_selected_step_t *selected_step = NULL;

	if (!isdigit(*name) && (name[0] != 's')) {
		fatal("Bad job/step specified: %s", name);
		return SLURM_ERROR;
	}

	selected_step = slurm_parse_step_str(name);

	if (!list_find_first(step_list, slurmdb_find_selected_step_in_list,
			     selected_step)) {
		list_append(step_list, selected_step);
		return 1;
	} else {
		slurm_destroy_selected_step(selected_step);
		return 0;
	}
}

/* returns number of objects added to list */
extern int slurm_addto_step_list(list_t *step_list, char *names)
{
	if (!step_list) {
		error("No list was given to fill in");
		return 0;
	}

	return slurm_parse_char_list(step_list, names, NULL,
			      	     _addto_step_list_internal);
}

extern int slurm_sort_char_list_asc(void *v1, void *v2)
{
	char *name_a = *(char **)v1;
	char *name_b = *(char **)v2;
	int diff = xstrcmp(name_a, name_b);

	if (diff < 0)
		return -1;
	else if (diff > 0)
		return 1;

	return 0;
}

extern int slurm_sort_char_list_desc(void *v1, void *v2)
{
	char *name_a = *(char **)v1;
	char *name_b = *(char **)v2;
	int diff = xstrcmp(name_a, name_b);

	if (diff > 0)
		return -1;
	else if (diff < 0)
		return 1;

	return 0;
}

extern int slurm_sort_time_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(time_t, v1, v2);
}

extern int slurm_sort_time_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(time_t, v1, v2);
}

extern int slurm_sort_uint16_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(uint16_t, v1, v2);
}

extern int slurm_sort_uint16_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(uint16_t, v1, v2);
}

extern int slurm_sort_uint32_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(uint32_t, v1, v2);
}

extern int slurm_sort_uint32_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(uint32_t, v1, v2);
}

extern int slurm_sort_uint64_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(uint64_t, v1, v2);
}

extern int slurm_sort_uint64_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(uint64_t, v1, v2);
}

extern int slurm_sort_int_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(int, v1, v2);
}

extern int slurm_sort_int_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(int, v1, v2);
}

extern int slurm_sort_int64_list_asc(const void *v1, const void *v2)
{
	return SORT_INT_ASC(int64_t, v1, v2);
}

extern int slurm_sort_int64_list_desc(const void *v1, const void *v2)
{
	return SORT_INT_DESC(int64_t, v1, v2);
}

extern char **slurm_char_array_copy(int n, char **src)
{
	char **dst = xcalloc(n + 1, sizeof(char *));
	int i;

	for (i = 0; i < n; i++) {
		dst[i] = xstrdup(src[i]);
	}
	dst[n] = NULL;

	return dst;
}

extern char *slurm_sort_node_list_str(char *node_list)
{
	char *sorted_node_list;
	hostset_t *hs;

	hs = hostset_create(node_list);
	sorted_node_list = hostset_ranged_string_xmalloc(hs);
	hostset_destroy(hs);

	return sorted_node_list;
}

extern bool slurm_parse_array_tok(char *tok, bitstr_t *array_bitmap,
				  uint32_t max)
{
	char *end_ptr = NULL;
	long int i, first, last, step = 1;

	if (tok[0] == '[')	/* Strip leading "[" */
		tok++;
	first = strtol(tok, &end_ptr, 10);
	if (end_ptr[0] == ']')	/* Strip trailing "]" */
		end_ptr++;
	if (first < 0)
		return false;
	if (end_ptr[0] == '-') {
		last = strtol(end_ptr + 1, &end_ptr, 10);
		if (end_ptr[0] == ']')	/* Strip trailing "]" */
			end_ptr++;
		if (end_ptr[0] == ':') {
			step = strtol(end_ptr + 1, &end_ptr, 10);
			if (end_ptr[0] == ']')	/* Strip trailing "]" */
				end_ptr++;
			if ((end_ptr[0] != '\0') && (end_ptr[0] != '%'))
				return false;
			if ((step <= 0) || (step >= max))
				return false;
		} else if ((end_ptr[0] != '\0') && (end_ptr[0] != '%')) {
			return false;
		}
		if (last < first)
			return false;
	} else if ((end_ptr[0] != '\0') && (end_ptr[0] != '%')) {
		return false;
	} else {
		last = first;
	}

	if (last >= max)
		return false;

	for (i = first; i <= last; i += step) {
		bit_set(array_bitmap, i);
	}

	return true;
}

extern bitstr_t *slurm_array_str2bitmap(char *str, uint32_t max_array_size,
					int32_t *i_last_p)
{
	bitstr_t *array_bitmap;
	char *tmp, *tok;
	bool valid = true;
	int32_t i_last = -1;

	xassert(max_array_size != NO_VAL);

	array_bitmap = bit_alloc(max_array_size);
	if (!array_bitmap)
		return NULL;

	tmp = xstrdup(str);
	tok = strtok_r(tmp, ",", &str);
	while (tok && valid) {
		valid = slurm_parse_array_tok(tok, array_bitmap,
					      max_array_size);
		tok = strtok_r(NULL, ",", &str);
	}
	xfree(tmp);

	if (!valid) {
		FREE_NULL_BITMAP(array_bitmap);
		return NULL;
	}
	i_last = bit_fls(array_bitmap);
	if (i_last < 0) {
		FREE_NULL_BITMAP(array_bitmap);
		return NULL;
	}

	if (i_last_p)
		*i_last_p = i_last;

	return array_bitmap;
}

/*
 * Parse a step identifier (number or name) from a string.
 * Expects the text after the '.' separator (e.g. "0", "extern", "batch").
 *
 * IN str - step identifier string (after the '.')
 * IN/OUT id - step_id field is populated on success
 * OUT end_ptr - if non-NULL, set to point past the parsed step identifier
 * RET SLURM_SUCCESS or error
 */
static int _unfmt_step_id_str(const char *str, slurm_step_id_t *id,
			      char **end_ptr)
{
	char *str_end;
	long step;

	if (!str || !*str)
		return ESLURM_EMPTY_STEP_ID;

	errno = 0;
	step = strtol(str, &str_end, 10);

	if (str_end == str) {
		/* check for step name instead */
		for (int i = 0;; i++) {
			if (i == ARRAY_SIZE(step_names))
				return ESLURM_INVALID_STEP_ID_NON_NUMERIC;

			if (!xstrncasecmp(step_names[i].name, str,
					  strlen(step_names[i].name))) {
				step = step_names[i].step_id;
				str_end = (char *) str +
					  strlen(step_names[i].name);
				break;
			}
		}
	} else if (step < 0) {
		return ESLURM_INVALID_STEP_ID_NEGATIVE;
	} else if (step >= SLURM_MAX_NORMAL_STEP_ID) {
		return ESLURM_INVALID_STEP_ID_TOO_LARGE;
	} else if (errno) {
		return SLURM_ERROR;
	}

	id->step_id = step;

	if (end_ptr)
		*end_ptr = str_end;

	return SLURM_SUCCESS;
}

extern int unfmt_job_id_string(const char *src, slurm_selected_step_t *id,
			       uint32_t max_array_size)
{
	char *end_ptr = NULL, *step_het_end_ptr = NULL;
	long job, step_het;
	int rc;

	/*
	 * Based on parser in scontrol_print_job() and scontrol_print_step()
	 */

	/* reset to default of NO_VAL */
	id->array_bitmap = NULL;
	id->array_task_id = NO_VAL;
	id->het_job_offset = NO_VAL;
	id->step_id = SLURM_STEP_ID_INITIALIZER;

	if (!src || !src[0])
		return ESLURM_EMPTY_JOB_ID;

	if (src[0] == 's') {
		sluid_t sluid;
		char tmp[SLUID_STR_BYTES];

		if (strlen(src) >= SLUID_STR_BYTES) {
			if (src[SLUID_STR_BYTES - 1] != '.')
				return ESLURM_INVALID_SLUID;
		}

		strlcpy(tmp, src, sizeof(tmp));

		sluid = str2sluid(tmp);
		if (!sluid)
			return ESLURM_INVALID_SLUID;

		if (src[SLUID_STR_BYTES - 1] == '.') {
			char *step_end;
			rc = _unfmt_step_id_str(&src[SLUID_STR_BYTES],
						&id->step_id, &step_end);
			if (rc != SLURM_SUCCESS)
				return rc;
			if (*step_end != '\0')
				return ESLURM_INVALID_STEP_ID_NON_NUMERIC;
		}

		id->step_id.sluid = sluid;
		return SLURM_SUCCESS;
	}

	errno = 0;
	job = strtol(src, &end_ptr, 10);
	if (job == 0)
		return ESLURM_INVALID_JOB_ID_ZERO;
	else if (job < 0)
		return ESLURM_INVALID_JOB_ID_NEGATIVE;
	else if (job >= MAX_FED_JOB_ID)
		return ESLURM_INVALID_JOB_ID_TOO_LARGE;
	else if (end_ptr == src)
		return ESLURM_INVALID_JOB_ID_NON_NUMERIC;
	else if (errno)
		return SLURM_ERROR;

	id->step_id.job_id = job;

	if ((*end_ptr == '_') && (*(end_ptr + 1) == '[')) {
		char *close_bracket;
		bitstr_t *array_bitmap;

		if (!max_array_size || (max_array_size == NO_VAL))
			return ESLURM_INVALID_JOB_ID_NON_NUMERIC;

		close_bracket = xstrchr(end_ptr + 2, ']');
		if (!close_bracket || (close_bracket[1] != '\0'))
			return ESLURM_INVALID_JOB_ARRAY_ID_NON_NUMERIC;

		array_bitmap = slurm_array_str2bitmap(end_ptr + 1,
						      max_array_size, NULL);
		if (!array_bitmap)
			return ESLURM_INVALID_JOB_ARRAY_ID_NON_NUMERIC;
		id->array_bitmap = array_bitmap;
		end_ptr = close_bracket + 1;
	} else if (*end_ptr == '_') {
		char *array_end_ptr = NULL;
		long array;

		if (*(end_ptr + 1) == '\0')
			return ESLURM_EMPTY_JOB_ARRAY_ID;

		errno = 0;
		array = strtol(end_ptr + 1, &array_end_ptr, 10);

		if (array < 0)
			return ESLURM_INVALID_JOB_ARRAY_ID_NEGATIVE;
		else if (array == LONG_MAX)
			return ESLURM_INVALID_JOB_ARRAY_ID_TOO_LARGE;
		else if (array_end_ptr == end_ptr + 1)
			return ESLURM_INVALID_JOB_ARRAY_ID_NON_NUMERIC;
		else if (errno)
			return SLURM_ERROR;

		id->array_task_id = array;
		end_ptr = array_end_ptr;
	}

	if (*end_ptr == '+') {
		char *het_end_ptr = NULL;
		long het;

		if (id->array_task_id != NO_VAL)
			return ESLURM_INVALID_HET_JOB_AND_ARRAY;
		else if (*(end_ptr + 1) == '\0')
			return ESLURM_EMPTY_HET_JOB_COMP;

		errno = 0;
		het = strtol(end_ptr + 1, &het_end_ptr, 10);

		if (het < 0)
			return ESLURM_INVALID_HET_JOB_COMP_NEGATIVE;
		else if (het > MAX_HET_JOB_COMPONENTS)
			return ESLURM_INVALID_HET_JOB_COMP_TOO_LARGE;
		else if (het_end_ptr == end_ptr + 1)
			return ESLURM_INVALID_HET_JOB_COMP_NON_NUMERIC;
		else if (errno)
			return SLURM_ERROR;

		id->het_job_offset = het;
		end_ptr = het_end_ptr;

		if (*end_ptr == '_')
			return ESLURM_INVALID_HET_JOB_AND_ARRAY;
	}

	if (*end_ptr == '\0')
		return SLURM_SUCCESS;

	if (*end_ptr != '.')
		return ESLURM_INVALID_JOB_ID_NON_NUMERIC;

	end_ptr++;

	if ((rc = _unfmt_step_id_str(end_ptr, &id->step_id, &end_ptr)))
		return rc;

	if (*end_ptr == '\0')
		return SLURM_SUCCESS;

	if (*end_ptr != '+')
		return ESLURM_INVALID_STEP_ID_NON_NUMERIC;

	if (id->het_job_offset != NO_VAL)
		return ESLURM_INVALID_HET_STEP_JOB;

	end_ptr++;

	if (*end_ptr == '\0')
		return SLURM_SUCCESS;

	errno = 0;
	step_het = strtol(end_ptr, &step_het_end_ptr, 10);

	if (step_het_end_ptr == end_ptr)
		return ESLURM_EMPTY_HET_STEP;
	else if (step_het == 0)
		return ESLURM_INVALID_HET_STEP_ZERO;
	else if (step_het < 0)
		return ESLURM_INVALID_HET_STEP_NEGATIVE;
	else if (step_het >= MAX_HET_JOB_COMPONENTS)
		return ESLURM_INVALID_HET_STEP_TOO_LARGE;
	else if (errno)
		return SLURM_ERROR;

	if (*step_het_end_ptr != '\0')
		return ESLURM_INVALID_HET_STEP_NON_NUMERIC;

	id->step_id.step_het_comp = step_het;

	return SLURM_SUCCESS;
}

extern int fmt_job_id_string(slurm_selected_step_t *id, char **dst)
{
	int rc;
	char *str = NULL, *pos = NULL;

	xassert(dst && !*dst);

	if (id->step_id.sluid &&
	    ((id->step_id.job_id == NO_VAL) || !id->step_id.job_id)) {
		char sluid_str[SLUID_STR_BYTES];
		print_sluid(id->step_id.sluid, sluid_str, sizeof(sluid_str));
		xstrfmtcatat(str, &pos, "%s", sluid_str);
		*dst = str;
		return SLURM_SUCCESS;
	}

	if (id->step_id.job_id == NO_VAL) {
		rc = ESLURM_EMPTY_JOB_ID;
		goto cleanup;
	}

	xstrfmtcatat(str, &pos, "%u", id->step_id.job_id);

	if ((id->array_task_id != NO_VAL) && (id->het_job_offset != NO_VAL)) {
		rc = ESLURM_INVALID_HET_JOB_AND_ARRAY;
		goto cleanup;
	}

	if (id->array_bitmap && (bit_ffs(id->array_bitmap) != -1)) {
		char *bitmap_str = bit_fmt_full(id->array_bitmap);

		xstrfmtcatat(str, &pos, "_[%s]", bitmap_str);
		xfree(bitmap_str);
		*dst = str;
		return SLURM_SUCCESS;
	}

	if (id->array_task_id != NO_VAL)
		xstrfmtcatat(str, &pos, "_%u", id->array_task_id);

	if (id->het_job_offset != NO_VAL)
		xstrfmtcatat(str, &pos, "+%u", id->het_job_offset);

	if ((id->step_id.step_id == NO_VAL) &&
	    (id->step_id.step_het_comp != NO_VAL)) {
		rc = ESLURM_EMPTY_STEP_ID;
		goto cleanup;
	}

	if (id->step_id.step_id != NO_VAL) {
		char *id_str = NULL;

		for (int i = 0; i < ARRAY_SIZE(step_names); i++) {
			if (step_names[i].step_id == id->step_id.step_id) {
				id_str = step_names[i].name;
				break;
			}
		}

		if (id_str)
			xstrfmtcatat(str, &pos, ".%s", id_str);
		else
			xstrfmtcatat(str, &pos, ".%u", id->step_id.step_id);

		if (id->step_id.step_het_comp != NO_VAL)
			xstrfmtcatat(str, &pos, "+%u",
				     id->step_id.step_het_comp);
	}

	*dst = str;
	return SLURM_SUCCESS;
cleanup:
	xfree(str);
	return rc;
}

extern slurm_selected_step_t *slurm_parse_step_str(char *name)
{
	slurm_selected_step_t *selected_step;
	int rc;

	xassert(name);

	selected_step = xmalloc(sizeof(*selected_step));
	rc = unfmt_job_id_string(name, selected_step, NO_VAL);
	if (rc) {
		fatal("Bad job/step specified: %s: %s", name,
		      slurm_strerror(rc));
	}

	return selected_step;
}

extern resource_allocation_response_msg_t *
slurm_copy_resource_allocation_response_msg(
	resource_allocation_response_msg_t *msg)
{
	resource_allocation_response_msg_t *new;

	if (!msg)
		return NULL;

	new = xmalloc(sizeof(*msg));

	memcpy(new, msg, sizeof(*msg));
	new->account = xstrdup(msg->account);
	new->batch_host = xstrdup(msg->batch_host);

	if (msg->cpus_per_node) {
		new->cpus_per_node = xcalloc(new->num_cpu_groups,
					     sizeof(*new->cpus_per_node));
		memcpy(new->cpus_per_node, msg->cpus_per_node,
		       new->num_cpu_groups * sizeof(*new->cpus_per_node));
	}

	if (msg->cpu_count_reps) {
		new->cpu_count_reps = xcalloc(new->num_cpu_groups,
					      sizeof(*new->cpu_count_reps));
		memcpy(new->cpu_count_reps, msg->cpu_count_reps,
		       new->num_cpu_groups * sizeof(*new->cpu_count_reps));
	}

	new->environment = env_array_copy((const char **)msg->environment);
	new->gid = msg->gid;
	new->group_name = xstrdup(msg->group_name);
	new->job_submit_user_msg = xstrdup(msg->job_submit_user_msg);
	new->node_list = xstrdup(msg->node_list);
	new->partition = xstrdup(msg->partition);
	new->qos = xstrdup(msg->qos);
	new->resv_name = xstrdup(msg->resv_name);
	new->uid = msg->uid;
	new->user_name = xstrdup(msg->user_name);
	new->working_cluster_rec = NULL;
	return new;
}


extern void slurm_free_last_update_msg(last_update_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_init_reboot_msg(reboot_msg_t *msg, bool clear)
{
	xassert(msg);

	if (clear)
		memset(msg, 0, sizeof(reboot_msg_t));

	msg->next_state = NO_VAL;
}

extern void slurm_free_reboot_msg(reboot_msg_t * msg)
{
	if (msg) {
		xfree(msg->features);
		xfree(msg->node_list);
		xfree(msg->reason);
		xfree(msg->power_action_name);
		xfree(msg);
	}
}

extern void slurm_free_shutdown_msg(shutdown_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_job_alloc_info_msg(job_alloc_info_msg_t * msg)
{
	if (msg) {
		xfree(msg->req_cluster);
		xfree(msg);
	}
}

extern void slurm_free_return_code2_msg(return_code2_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->err_msg);
	xfree(msg);
}

extern void slurm_free_return_code_msg(return_code_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_reroute_msg(reroute_msg_t *msg)
{
	if (msg) {
		xfree(msg->stepmgr);
		slurmdb_destroy_cluster_rec(msg->working_cluster_rec);
		xfree(msg);
	}
}

extern void slurm_free_batch_script_msg(char *msg)
{
	xfree(msg);
}

extern void slurm_free_job_id_msg(job_id_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_job_user_id_msg(job_user_id_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_step_id(slurm_step_id_t *msg)
{
	xfree(msg);
}

extern void slurm_free_job_id_request_msg(job_id_request_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_config_request_msg(config_request_msg_t *msg)
{
	if (msg) {
		xfree(msg);
	}
}

extern void slurm_free_config_response_msg(config_response_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->config_files);
		xfree(msg->slurmd_spooldir);
		xfree(msg);
	}
}

extern void slurm_free_update_step_msg(step_update_request_msg_t * msg)
{
	if (msg) {
		xfree(msg);
	}
}

extern container_state_msg_t *slurm_create_container_state_msg(void)
{
	container_state_msg_t *msg = xmalloc(sizeof(*msg));
	msg->status = CONTAINER_ST_UNKNOWN;
	msg->pid = NO_VAL;
	msg->annotations = list_create(destroy_config_key_pair);

	return msg;
}

extern void slurm_destroy_container_state_msg(container_state_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->oci_version);
	xfree(msg->id);
	msg->status = CONTAINER_ST_INVALID;
	xfree(msg->bundle);
	FREE_NULL_LIST(msg->annotations);
	xfree(msg);
}

extern void slurm_destroy_container_exec_msg(container_exec_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->args);
	xfree(msg->env);
	xfree(msg);
}

extern const char *slurm_container_status_to_str(
	container_state_msg_status_t status)
{
	static const struct {
		int msg;
		char *status;
	} status_str[] = {
		{ CONTAINER_ST_INVALID, "INVALID" },
		{ CONTAINER_ST_UNKNOWN, "UNKNOWN" },
		{ CONTAINER_ST_CREATING, "CREATING" },
		{ CONTAINER_ST_CREATED, "CREATED" },
		{ CONTAINER_ST_STARTING, "STARTING" },
		{ CONTAINER_ST_RUNNING, "RUNNING" },
		{ CONTAINER_ST_STOPPING, "STOPPING" },
		{ CONTAINER_ST_STOPPED, "STOPPED" },
	};

	for (int i = 0; i < ARRAY_SIZE(status_str); i++)
		if (status == status_str[i].msg)
			return status_str[i].status;

	return "UNKNOWN";
}

extern void slurm_destroy_selected_step(void *object)
{
	slurm_selected_step_t *step = (slurm_selected_step_t *)object;

	if (!step)
		return;

	FREE_NULL_BITMAP(step->array_bitmap);
	xfree(step);
}

extern void slurm_free_job_id_response_msg(job_id_response_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_job_step_kill_msg(job_step_kill_msg_t * msg)
{
	if (msg) {
		xfree(msg->sibling);
		xfree(msg->sjob_id);
		xfree(msg);
	}
}

extern void slurm_free_kill_jobs_msg(kill_jobs_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->account);
	xfree(msg->admin_comment);
	xfree(msg->job_name);
	xfree(msg->partition);
	xfree(msg->qos);
	xfree(msg->reservation);
	xfree(msg->user_name);
	xfree(msg->wckey);
	xfree(msg->nodelist);
	xfree_array(msg->jobs_array);
	xfree(msg);
}

extern void slurm_free_kill_jobs_resp_job_t(kill_jobs_resp_job_t *job_resp)
{
	if (!job_resp)
		return;

	xfree(job_resp->error_msg);
	xfree(job_resp->id);
	xfree(job_resp->sibling_name);
	/* job_resp was not malloc'd so do not free */
}

extern void slurm_free_kill_jobs_response_msg(kill_jobs_resp_msg_t *msg)
{
	if (!msg)
		return;

	for (int i = 0; i < msg->jobs_cnt; i++) {
		slurm_free_kill_jobs_resp_job_t(&msg->job_responses[i]);
	}
	xfree(msg->job_responses);
	xfree(msg);
}

extern void slurm_free_container_id_request_msg(
	container_id_request_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->container_id);
	xfree(msg);
}

extern void slurm_free_container_id_response_msg(
	container_id_response_msg_t *msg)
{
	if (!msg)
		return;

	FREE_NULL_LIST(msg->steps);
	xfree(msg);
}

extern void slurm_free_job_info_request_msg(job_info_request_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->job_ids);
		xfree(msg);
	}
}

extern void slurm_free_job_state_request_msg(job_state_request_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->job_ids);
	xfree(msg);
}

extern void slurm_free_job_state_response_msg(job_state_response_msg_t *msg)
{
	if (!msg)
		return;

	for (int i = 0; i < msg->jobs_count; i++)
		FREE_NULL_BITMAP(msg->jobs[i].array_task_id_bitmap);

	xfree(msg->jobs);
	xfree(msg);
}

extern void slurm_free_job_step_info_request_msg(job_step_info_request_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_node_info_request_msg(node_info_request_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_node_info_single_msg(node_info_single_msg_t *msg)
{
	if (msg) {
		xfree(msg->node_name);
		xfree(msg);
	}
}

extern void slurm_free_part_info_request_msg(part_info_request_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_job_desc_msg(job_desc_msg_t *msg)
{
	int i;

	if (msg) {
		xfree(msg->account);
		xfree(msg->acctg_freq);
		xfree(msg->admin_comment);
		xfree(msg->alloc_node);
		xfree(msg->alloc_tls_cert);
		if (msg->argv) {
			for (i = 0; i < msg->argc; i++)
				xfree(msg->argv[i]);
		}
		xfree(msg->argv);
		FREE_NULL_BITMAP(msg->array_bitmap);
		xfree(msg->array_inx);
		xfree(msg->batch_features);
		xfree(msg->burst_buffer);
		xfree(msg->clusters);
		xfree(msg->comment);
		xfree(msg->container);
		xfree(msg->container_id);
		xfree(msg->container_type);
		xfree(msg->cpu_bind);
		xfree(msg->cpus_per_tres);
		free_cron_entry(msg->crontab_entry);
		xfree(msg->dependency);
		env_array_free(msg->environment);
		msg->environment = NULL;
		xfree(msg->extra);
		xfree(msg->exc_nodes);
		xfree(msg->features);
		xfree(msg->cluster_features);
		FREE_NULL_IDENTITY(msg->id);
		xfree(msg->job_id_str);
		xfree(msg->job_size_str);
		xfree(msg->licenses);
		xfree(msg->licenses_tot);
		xfree(msg->mail_user);
		xfree(msg->mcs_label);
		xfree(msg->mem_bind);
		xfree(msg->mem_per_tres);
		xfree(msg->name);
		xfree(msg->network);
		xfree(msg->origin_cluster);
		xfree(msg->partition);
		xfree(msg->qos);
		xfree(msg->req_context);
		xfree(msg->req_nodes);
		xfree(msg->reservation);
		xfree(msg->resp_host);
		xfree(msg->script);
		FREE_NULL_BUFFER(msg->script_buf);
		xfree(msg->selinux_context);
		xfree(msg->std_err);
		xfree(msg->std_in);
		xfree(msg->std_out);
		if (msg->spank_job_env) {
			for (i = 0; i < msg->spank_job_env_size; i++)
				xfree(msg->spank_job_env[i]);
			xfree(msg->spank_job_env);
		}
		xfree(msg->submit_line);
		xfree(msg->tres_bind);
		xfree(msg->tres_freq);
		xfree(msg->tres_req_cnt);
		xfree(msg->tres_per_job);
		xfree(msg->tres_per_node);
		xfree(msg->tres_per_socket);
		xfree(msg->tres_per_task);
		xfree(msg->wckey);
		xfree(msg->work_dir);
		xfree(msg->x11_magic_cookie);
		xfree(msg->x11_target);
		xfree(msg);
	}
}

extern void slurm_free_sib_msg(sib_msg_t *msg)
{
	if (msg) {
		FREE_NULL_BUFFER(msg->data_buffer);
		xfree(msg->resp_host);
		if (msg->data)
			slurm_free_msg_data(msg->data_type, msg->data);
		xfree(msg->submit_host);
		xfree(msg);
	}
}

extern void slurm_free_dep_msg(dep_msg_t *msg)
{
	if (msg) {
		xfree(msg->dependency);
		xfree(msg->job_name);
		xfree(msg);
	}
}

extern void slurm_free_dep_update_origin_msg(dep_update_origin_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->depend_list);
		xfree(msg);
	}
}

extern void slurm_free_prolog_launch_msg(prolog_launch_msg_t * msg)
{
	int i;

	if (msg) {
		xfree(msg->alloc_tls_cert);
		FREE_NULL_LIST(msg->job_gres_prep);
		xfree(msg->nodes);
		xfree(msg->work_dir);

		xfree(msg->x11_alloc_host);
		xfree(msg->x11_magic_cookie);
		xfree(msg->x11_target);

		if (msg->spank_job_env) {
			for (i = 0; i < msg->spank_job_env_size; i++)
				xfree(msg->spank_job_env[i]);
			xfree(msg->spank_job_env);
		}
		slurm_cred_destroy(msg->cred);

		/* stepmgr variables */
		job_record_delete(msg->job_ptr);
		part_record_delete(msg->part_ptr);
		FREE_NULL_LIST(msg->job_node_array);

		FREE_NULL_BUFFER(msg->job_ptr_buf);
		FREE_NULL_BUFFER(msg->job_node_array_buf);
		FREE_NULL_BUFFER(msg->part_ptr_buf);

		xfree(msg);
	}
}

extern void slurm_free_prolog_complete_msg(prolog_complete_msg_t *msg)
{
	xfree(msg->node_name);
	xfree(msg);
}

extern void slurm_free_job_launch_msg(batch_job_launch_msg_t * msg)
{
	int i;

	if (msg) {
		xfree(msg->account);
		xfree(msg->acctg_freq);
		if (msg->argv) {
			for (i = 0; i < msg->argc; i++)
				xfree(msg->argv[i]);
			xfree(msg->argv);
		}
		xfree(msg->container);
		xfree(msg->cpu_bind);
		xfree(msg->cpus_per_node);
		xfree(msg->cpu_count_reps);
		slurm_cred_destroy(msg->cred);
		if (msg->environment) {
			for (i = 0; i < msg->envc; i++)
				xfree(msg->environment[i]);
			xfree(msg->environment);
		}
		xfree(msg->gids);
		xfree(msg->nodes);
		xfree(msg->partition);
		xfree(msg->qos);
		xfree(msg->resv_name);
		xfree(msg->script);
		FREE_NULL_BUFFER(msg->script_buf);
		if (msg->spank_job_env) {
			for (i = 0; i < msg->spank_job_env_size; i++)
				xfree(msg->spank_job_env[i]);
			xfree(msg->spank_job_env);
		}
		xfree(msg->std_err);
		xfree(msg->std_in);
		xfree(msg->std_out);
		xfree(msg->tres_bind);
		xfree(msg->tres_freq);
		xfree(msg->tres_per_task);
		xfree(msg->work_dir);
		xfree(msg);
	}
}

extern void slurm_free_job_info(job_info_t * job)
{
	if (job) {
		slurm_free_job_info_members(job);
		xfree(job);
	}
}

extern void slurm_free_job_info_members(job_info_t * job)
{
	int i;

	if (job) {
		xfree(job->account);
		xfree(job->admin_comment);
		xfree(job->alloc_node);
		FREE_NULL_BITMAP(job->array_bitmap);
		xfree(job->array_task_str);
		xfree(job->batch_features);
		xfree(job->batch_host);
		xfree(job->burst_buffer);
		xfree(job->burst_buffer_state);
		xfree(job->cluster);
		xfree(job->cluster_features);
		xfree(job->command);
		xfree(job->comment);
		xfree(job->container);
		xfree(job->container_id);
		xfree(job->container_type);
		xfree(job->cpus_per_tres);
		xfree(job->cronspec);
		xfree(job->dependency);
		xfree(job->exc_nodes);
		xfree(job->exc_node_inx);
		xfree(job->extra);
		xfree(job->failed_node);
		xfree(job->features);
		xfree(job->fed_origin_str);
		xfree(job->fed_siblings_active_str);
		xfree(job->fed_siblings_viable_str);
		xfree(job->gres_total);
		if (job->gres_detail_str) {
			for (i = 0; i < job->gres_detail_cnt; i++)
				xfree(job->gres_detail_str[i]);
			xfree(job->gres_detail_str);
		}
		xfree(job->het_job_id_set);
		xfree(job->job_size_str);
		xfree(job->licenses);
		xfree(job->licenses_allocated);
		xfree(job->mail_user);
		xfree(job->mcs_label);
		xfree(job->mem_per_tres);
		xfree(job->name);
		xfree(job->network);
		xfree(job->node_inx);
		xfree(job->nodes);
		xfree(job->sched_nodes);
		xfree(job->partition);
		xfree(job->priority_array);
		xfree(job->priority_array_names);
		xfree(job->prefer);
		xfree(job->qos);
		xfree(job->req_node_inx);
		xfree(job->req_nodes);
		xfree(job->resv_name);
		xfree(job->resv_ports);
		free_job_resources(&job->job_resrcs);
		xfree(job->selinux_context);
		xfree(job->state_desc);
		xfree(job->std_err);
		xfree(job->std_in);
		xfree(job->std_out);
		xfree(job->submit_line);
		xfree(job->system_comment);
		xfree(job->tres_alloc_str);
		xfree(job->tres_bind);
		xfree(job->tres_freq);
		xfree(job->tres_per_job);
		xfree(job->tres_per_node);
		xfree(job->tres_per_socket);
		xfree(job->tres_per_task);
		xfree(job->tres_req_str);
		xfree(job->user_name);
		xfree(job->wckey);
		xfree(job->work_dir);
	}
}


extern void slurm_free_acct_gather_node_resp_msg(
	acct_gather_node_resp_msg_t *msg)
{
	if (msg) {
		xfree(msg->node_name);
		acct_gather_energy_destroy(msg->energy);
		xfree(msg);
	}
}

extern void slurm_free_acct_gather_energy_req_msg(
	acct_gather_energy_req_msg_t *msg)
{
	if (msg) {
		xfree(msg);
	}
}

extern void slurm_free_node_gres_layout(node_gres_layout_t *msg)
{
	if (!msg)
		return;

	xfree(msg->name);
	xfree(msg->type);
	FREE_NULL_BITMAP(msg->index);
	xfree(msg);
}

extern void slurm_free_node_resource_layout(node_resource_layout_t *msg)
{
	if (!msg)
		return;

	xfree(msg->node);
	xfree(msg->core_bitmap);
	FREE_NULL_LIST(msg->gres);
	xfree(msg);
}

extern void slurm_free_resource_layout_msg(resource_layout_msg_t *msg)
{
	if (!msg)
		return;

	FREE_NULL_LIST(msg->nodes);
	xfree(msg);
}

extern void slurm_free_node_registration_status_msg(
	slurm_node_registration_status_msg_t * msg)
{
	if (msg) {
		xfree(msg->arch);
		xfree(msg->dynamic_conf);
		xfree(msg->dynamic_feature);
		xfree(msg->cpu_spec_list);
		if (msg->energy)
			acct_gather_energy_destroy(msg->energy);
		xfree(msg->extra);
		xfree(msg->features_active);
		xfree(msg->features_avail);
		xfree(msg->hostname);
		xfree(msg->instance_id);
		xfree(msg->instance_type);
		FREE_NULL_BUFFER(msg->gres_info);
		xfree(msg->node_name);
		xfree(msg->os);
		xfree(msg->parameters);
		xfree(msg->step_id);
		xfree(msg->version);
		xfree(msg);
	}
}

extern void slurm_free_sbcast_cred_req_msg(sbcast_cred_req_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->node_list);
	xfree(msg);
}

extern void slurm_free_node_reg_resp_msg(
	slurm_node_reg_resp_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->node_name);
	FREE_NULL_LIST(msg->tres_list);
	xfree(msg);
}

extern void slurm_free_update_node_msg(update_node_msg_t * msg)
{
	if (msg) {
		xfree(msg->cert_token);
		xfree(msg->comment);
		xfree(msg->extra);
		xfree(msg->features);
		xfree(msg->features_act);
		xfree(msg->gres);
		xfree(msg->instance_id);
		xfree(msg->instance_type);
		xfree(msg->node_addr);
		xfree(msg->node_hostname);
		xfree(msg->node_names);
		xfree(msg->power_action_name);
		xfree(msg->reason);
		xfree(msg->topology_str);
		xfree(msg);
	}
}

extern void slurm_free_run_power_action_msg(run_power_action_msg_t *msg)
{
	if (msg) {
		xfree(msg->action_name);
		xfree(msg->file_content);
		xfree(msg->file_env_name);
		xfree(msg);
	}
}

extern void slurm_free_update_part_msg(update_part_msg_t * msg)
{
	if (msg) {
		slurm_free_partition_info_members((partition_info_t *)msg);
		xfree(msg);
	}
}

extern void slurm_free_delete_part_msg(delete_part_msg_t * msg)
{
	if (msg) {
		xfree(msg->name);
		xfree(msg);
	}
}

extern void slurm_free_resv_desc_msg_part(resv_desc_msg_t *msg,
					  uint32_t res_free_flags)
{
	if (!msg)
		return;

	if (res_free_flags & RESV_FREE_STR_USER)
		xfree(msg->users);
	if (res_free_flags & RESV_FREE_STR_ACCT)
		xfree(msg->accounts);
	if (res_free_flags & RESV_FREE_STR_TRES_BB)
		xfree(msg->burst_buffer);
	if (res_free_flags & RESV_FREE_STR_COMMENT)
		xfree(msg->comment);
	if (res_free_flags & RESV_FREE_STR_ALLOWED_PARTS)
		xfree(msg->allowed_parts);
	if (res_free_flags & RESV_FREE_STR_QOS)
		xfree(msg->qos);
	if (res_free_flags & RESV_FREE_STR_TRES_LIC)
		xfree(msg->licenses);
	if (res_free_flags & RESV_FREE_STR_GROUP)
		xfree(msg->groups);
	if (res_free_flags & RESV_FREE_STR_NODES)
		xfree(msg->node_list);
	if (res_free_flags & RESV_FREE_STR_TRES)
		xfree(msg->tres_str);
}

extern void slurm_free_resv_desc_members(resv_desc_msg_t *msg)
{
	xfree(msg->features);
	xassert(!msg->job_ptr); /* This shouldn't be here */
	xfree(msg->name);
	xfree(msg->node_list);
	xfree(msg->partition);

	slurm_free_resv_desc_msg_part(msg, 0xffffffff);
}

extern void slurm_free_resv_desc_msg(resv_desc_msg_t *msg)
{
	if (msg) {
		slurm_free_resv_desc_members(msg);
		xfree(msg);
	}
}

extern void slurm_free_resv_name_msg(reservation_name_msg_t * msg)
{
	if (msg) {
		xfree(msg->name);
		xfree(msg);
	}
}

extern void slurm_free_resv_info_request_msg(resv_info_request_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_launch_parameters(slurm_step_launch_params_t *params)
{
	if (!params)
		return;
	xfree_array(params->argv);
	env_array_free(params->env);
	xfree(params->container);
	xfree(params->cwd);
	xfree(params->output_filename);
	xfree(params->error_filename);
	xfree(params->input_filename);
	xfree(params->het_job_step_task_cnts);
	if (params->het_job_nnodes != NO_VAL) {
		xfree(params->het_job_task_cnts);
		if (params->het_job_tids) {
			for (uint32_t i = 0; i < params->het_job_nnodes; i++)
				xfree(params->het_job_tids[i]);
			xfree(params->het_job_tids);
		}
		xfree(params->het_job_tid_offsets);
	}
	xfree(params->het_job_node_list);
	xfree(params->task_prolog);
	xfree(params->task_epilog);
	xfree(params->cpu_bind);
	xfree(params->mem_bind);
	xfree(params->cpt_compact_array);
	xfree(params->cpt_compact_reps);
	xfree(params->mpi_plugin_name);
	xfree(params->acctg_freq);
	xfree_array(params->spank_job_env);
	xfree(params->tres_bind);
	xfree(params->tres_freq);
	xfree(params);
}

extern void slurm_free_job_step_create_request_msg(
		job_step_create_request_msg_t *msg)
{
	if (msg) {
		xfree(msg->container);
		xfree(msg->container_id);
		xfree(msg->container_type);
		xfree(msg->cpus_per_tres);
		xfree(msg->exc_nodes);
		xfree(msg->features);
		xfree(msg->host);
		slurm_free_launch_parameters(msg->launch_params);
		xfree(msg->mem_per_tres);
		xfree(msg->name);
		xfree(msg->network);
		xfree(msg->node_list);
		xfree(msg->step_het_grps);
		xfree(msg->cwd);
		xfree(msg->std_err);
		xfree(msg->std_in);
		xfree(msg->std_out);
		xfree(msg->submit_line);
		xfree(msg->tres_bind);
		xfree(msg->tres_freq);
		xfree(msg->tres_per_step);
		xfree(msg->tres_per_node);
		xfree(msg->tres_per_socket);
		xfree(msg->tres_per_task);
		xfree(msg);
	}
}

extern void slurm_free_complete_job_allocation_msg(
	complete_job_allocation_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_complete_batch_script_msg(
		complete_batch_script_msg_t * msg)
{
	if (msg) {
		jobacctinfo_destroy(msg->jobacct);
		xfree(msg->node_name);
		xfree(msg);
	}
}


extern void slurm_free_launch_tasks_response_msg(
		launch_tasks_response_msg_t *msg)
{
	if (msg) {
		xfree(msg->node_name);
		xfree(msg->local_pids);
		xfree(msg->task_ids);
		xfree(msg);
	}
}

extern void slurm_free_kill_job_msg(kill_job_msg_t * msg)
{
	if (msg) {
		int i;
		slurm_cred_destroy(msg->cred);
		xfree(msg->details);
		FREE_NULL_LIST(msg->job_gres_prep);
		xfree(msg->nodes);

		if (msg->spank_job_env) {
			for (i = 0; i < msg->spank_job_env_size; i++)
				xfree(msg->spank_job_env[i]);
			xfree(msg->spank_job_env);
		}
		xfree(msg->work_dir);
		xfree(msg);
	}
}

extern void slurm_free_task_exit_msg(task_exit_msg_t * msg)
{
	if (msg) {
		xfree(msg->task_id_list);
		xfree(msg);
	}
}

extern void slurm_free_launch_tasks_request_msg(launch_tasks_request_msg_t * msg)
{
	int i;

	if (msg == NULL)
		return;

	slurm_cred_destroy(msg->cred);

	if (msg->env) {
		for (i = 0; i < msg->envc; i++) {
			xfree(msg->env[i]);
		}
		xfree(msg->env);
	}
	xfree(msg->acctg_freq);
	xfree(msg->container);
	xfree(msg->cwd);
	xfree(msg->cpt_compact_array);
	xfree(msg->cpt_compact_reps);
	xfree(msg->cpu_bind);
	xfree(msg->mem_bind);
	if (msg->argv) {
		for (i = 0; i < msg->argc; i++) {
			xfree(msg->argv[i]);
		}
		xfree(msg->argv);
	}
	if (msg->spank_job_env) {
		for (i = 0; i < msg->spank_job_env_size; i++) {
			xfree(msg->spank_job_env[i]);
		}
		xfree(msg->spank_job_env);
	}
	if (msg->global_task_ids) {
		for (i = 0; i < msg->nnodes; i++) {
			xfree(msg->global_task_ids[i]);
		}
		xfree(msg->global_task_ids);
	}
	xfree(msg->gids);
	xfree(msg->het_job_node_list);
	xfree(msg->het_job_step_task_cnts);
	xfree(msg->het_job_task_cnts);
	if (msg->het_job_nnodes != NO_VAL) {
		for (i = 0; i < msg->het_job_nnodes; i++)
			xfree(msg->het_job_tids[i]);
		xfree(msg->het_job_tids);
	}
	xfree(msg->het_job_tid_offsets);
	xfree(msg->tasks_to_launch);
	xfree(msg->resp_port);
	xfree(msg->io_port);
	xfree(msg->global_task_ids);
	xfree(msg->ifname);
	xfree(msg->ofname);
	xfree(msg->efname);

	xfree(msg->task_prolog);
	xfree(msg->task_epilog);
	xfree(msg->complete_nodelist);

	if (msg->switch_step)
		switch_g_stepinfo_free(msg->switch_step);

	FREE_NULL_LIST(msg->options);

	xfree(msg->alloc_tls_cert);
	xfree(msg->tres_bind);
	xfree(msg->tres_per_task);
	xfree(msg->tres_freq);
	xfree(msg->x11_alloc_host);
	xfree(msg->x11_magic_cookie);
	xfree(msg->x11_target);

	xfree(msg->stepmgr);
	job_record_delete(msg->job_ptr);
	part_record_delete(msg->part_ptr);
	FREE_NULL_LIST(msg->job_node_array);

	xfree(msg);
}

extern void slurm_free_reattach_tasks_request_msg(
		reattach_tasks_request_msg_t *msg)
{
	if (msg) {
		xfree(msg->tls_cert);
		xfree(msg->resp_port);
		xfree(msg->io_key);
		xfree(msg->io_port);
		xfree(msg);
	}
}

extern void slurm_free_reattach_tasks_response_msg(
		reattach_tasks_response_msg_t *msg)
{
	int i;

	if (msg) {
		xfree(msg->node_name);
		xfree(msg->local_pids);
		xfree(msg->gtids);
		if (msg->executable_names) {
			for (i = 0; i < msg->ntasks; i++) {
				xfree(msg->executable_names[i]);
			}
			xfree(msg->executable_names);
		}
		xfree(msg);
	}
}

extern void slurm_free_signal_tasks_msg(signal_tasks_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_job_mem_usage_msg(job_mem_usage_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_job_mem_usage_resp_msg(job_mem_usage_resp_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_update_job_mem_msg(update_job_mem_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_response_update_job_mem_msg(response_update_job_mem_msg_t
							   *msg)
{
	if (msg) {
		xfree(msg->node_name);
		xfree(msg);
	}
}

extern void slurm_free_epilog_complete_msg(epilog_complete_msg_t * msg)
{
	if (msg) {
		xfree(msg->node_name);
		xfree(msg);
	}
}

extern void slurm_free_srun_job_complete_msg(
		srun_job_complete_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_srun_ping_msg(srun_ping_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_net_forward_msg(net_forward_msg_t *msg)
{
	if (msg) {
		xfree(msg->target);
		xfree(msg);
	}
}

extern void slurm_free_srun_node_fail_msg(srun_node_fail_msg_t * msg)
{
	if (msg) {
		xfree(msg->nodelist);
		xfree(msg);
	}
}

extern void slurm_free_srun_step_missing_msg(srun_step_missing_msg_t * msg)
{
	if (msg) {
		xfree(msg->nodelist);
		xfree(msg);
	}
}

extern void slurm_free_srun_timeout_msg(srun_timeout_msg_t * msg)
{
	xfree(msg);
}

extern void slurm_free_srun_user_msg(srun_user_msg_t * user_msg)
{
	if (user_msg) {
		xfree(user_msg->msg);
		xfree(user_msg);
	}
}

extern void slurm_free_steps_drained_sub_msg(steps_drained_sub_msg_t *msg)
{
	if (msg) {
		xfree(msg->host);
		xfree(msg->tls_cert);
		xfree(msg);
	}
}

extern void slurm_free_suspend_msg(suspend_msg_t *msg)
{
	if (msg) {
		xfree(msg->job_id_str);
		xfree(msg);
	}
}

extern void slurm_free_top_job_msg(top_job_msg_t *msg)
{
	if (msg) {
		xfree(msg->job_id_str);
		xfree(msg);
	}
}

extern void slurm_free_token_request_msg(token_request_msg_t *msg)
{
	if (msg) {
		xfree(msg->username);
		xfree(msg);
	}
}

extern void slurm_free_token_response_msg(token_response_msg_t *msg)
{
	if (msg) {
		xfree(msg->token);
		xfree(msg);
	}
}

extern void
slurm_free_requeue_msg(requeue_msg_t *msg)
{
	if (msg) {
		xfree(msg->job_id_str);
		xfree(msg);
	}
}

extern void slurm_free_resize_msg(resize_msg_t *msg)
{
	if (msg) {
		xfree(msg->nodelist);
		xfree(msg);
	}
}

extern void slurm_free_suspend_int_msg(suspend_int_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_stats_response_msg(stats_info_response_msg_t *msg)
{
	int i;
	if (msg) {
		xfree(msg->bf_exit);
		xfree(msg->schedule_exit);
		xfree(msg->rpc_type_id);
		xfree(msg->rpc_type_cnt);
		xfree(msg->rpc_type_time);
		xfree(msg->rpc_type_queued);
		xfree(msg->rpc_type_dropped);
		xfree(msg->rpc_type_cycle_last);
		xfree(msg->rpc_type_cycle_max);
		xfree(msg->rpc_user_id);
		xfree(msg->rpc_user_cnt);
		xfree(msg->rpc_user_time);
		xfree(msg->rpc_queue_type_id);
		xfree(msg->rpc_queue_count);
		xfree(msg->rpc_dump_types);
		for (i = 0; i < msg->rpc_dump_count; i++) {
			xfree(msg->rpc_dump_hostlist[i]);
		}
		xfree(msg->rpc_dump_hostlist);
		xfree(msg);
	}
}

/* Free job array oriented response with individual return codes by task ID */
extern void slurm_free_job_array_resp(job_array_resp_msg_t *msg)
{
	uint32_t i;

	if (msg) {
		if (msg->job_array_id) {
			for (i = 0; i < msg->job_array_count; i++) {
				xfree(msg->job_array_id[i]);
				xfree(msg->err_msg[i]);
			}
			xfree(msg->job_array_id);
		}
		xfree(msg->err_msg);
		xfree(msg->error_code);
		xfree(msg);
	}
}

extern void slurm_free_get_kvs_msg(kvs_get_msg_t *msg)
{
	if (msg) {
		xfree(msg->hostname);
		xfree(msg->tls_cert);
		xfree(msg);
	}
}

extern void slurm_free_kvs_comm_set(kvs_comm_set_t *msg)
{
	int i, j;

	if (msg) {
		if (msg->kvs_host_ptr) {
			for (i = 0; i < msg->host_cnt; i++) {
				xfree(msg->kvs_host_ptr[i].hostname);
				xfree(msg->kvs_host_ptr[i].tls_cert);
			}
			xfree(msg->kvs_host_ptr);
		}
		if (msg->kvs_comm_ptr) {
			for (i = 0; i < msg->kvs_comm_recs; i++) {
				if (!msg->kvs_comm_ptr[i])
					continue;

				xfree(msg->kvs_comm_ptr[i]->kvs_name);
				for (j = 0; j < msg->kvs_comm_ptr[i]->kvs_cnt;
				     j++) {
					xfree(msg->kvs_comm_ptr[i]->
					      kvs_keys[j]);
					xfree(msg->kvs_comm_ptr[i]->
					      kvs_values[j]);
				}
				xfree(msg->kvs_comm_ptr[i]->kvs_keys);
				xfree(msg->kvs_comm_ptr[i]->kvs_values);
				xfree(msg->kvs_comm_ptr[i]);
			}
			xfree(msg->kvs_comm_ptr);
		}
		xfree(msg);
	}
}

extern void slurm_free_will_run_response_msg(will_run_response_msg_t *msg)
{
	if (msg) {
		xfree(msg->cluster_name);
		xfree(msg->job_submit_user_msg);
		xfree(msg->node_list);
		xfree(msg->part_name);
		FREE_NULL_LIST(msg->preemptee_job_id);
		xfree(msg);
	}
}

extern void slurm_free_forward_data_msg(forward_data_msg_t *msg)
{
	if (msg) {
		xfree(msg->address);
		xfree(msg->data);
		xfree(msg);
	}
}

extern void slurm_free_ping_slurmd_resp(ping_slurmd_resp_msg_t *msg)
{
	if (msg) {
		xfree(msg->node_name);
		xfree(msg);
	}
}

/*
 * structured as a static lookup table, which allows this
 * to be thread safe while avoiding any heap allocation
 */
extern const char *preempt_mode_string(uint16_t preempt_mode)
{
	if (preempt_mode == PREEMPT_MODE_OFF)
		return "OFF";
	if (preempt_mode == PREEMPT_MODE_GANG)
		return "GANG";
	if (preempt_mode == PREEMPT_MODE_WITHIN)
		return "WITHIN";
	if (preempt_mode == PREEMPT_MODE_PRIORITY)
		return "PRIORITY";

	if (preempt_mode & PREEMPT_MODE_GANG) {
		preempt_mode &= (~PREEMPT_MODE_GANG);
		if (preempt_mode == PREEMPT_MODE_CANCEL)
			return "GANG,CANCEL";
		else if (preempt_mode == PREEMPT_MODE_REQUEUE)
			return "GANG,REQUEUE";
		else if (preempt_mode == PREEMPT_MODE_SUSPEND)
			return "GANG,SUSPEND";
		return "GANG,UNKNOWN";
	} else if (preempt_mode & PREEMPT_MODE_WITHIN) {
		preempt_mode &= (~PREEMPT_MODE_WITHIN);
		if (preempt_mode == PREEMPT_MODE_CANCEL)
			return "WITHIN,CANCEL";
		else if (preempt_mode == PREEMPT_MODE_REQUEUE)
			return "WITHIN,REQUEUE";
		else if (preempt_mode == PREEMPT_MODE_SUSPEND)
			return "WITHIN,SUSPEND";
		return "WITHIN,UNKNOWN";
	} else if (preempt_mode & PREEMPT_MODE_PRIORITY) {
		preempt_mode &= (~PREEMPT_MODE_PRIORITY);
		if (preempt_mode == PREEMPT_MODE_CANCEL)
			return "PRIORITY,CANCEL";
		else if (preempt_mode == PREEMPT_MODE_REQUEUE)
			return "PRIORITY,REQUEUE";
		else if (preempt_mode == PREEMPT_MODE_SUSPEND)
			return "PRIORITY,SUSPEND";
		return "PRIORITY,UNKNOWN";
	} else {
		if (preempt_mode == PREEMPT_MODE_CANCEL)
			return "CANCEL";
		else if (preempt_mode == PREEMPT_MODE_REQUEUE)
			return "REQUEUE";
		else if (preempt_mode == PREEMPT_MODE_SUSPEND)
			return "SUSPEND";
	}

	return "UNKNOWN";
}

extern uint16_t preempt_mode_num(const char *preempt_mode)
{
	uint16_t mode_num = 0;
	int preempt_modes = 0;
	char *tmp_str, *last = NULL, *tok;

	if (preempt_mode == NULL)
		return mode_num;

	tmp_str = xstrdup(preempt_mode);
	tok = strtok_r(tmp_str, ",", &last);
	while (tok) {
		if (xstrcasecmp(tok, "gang") == 0) {
			mode_num |= PREEMPT_MODE_GANG;
		} else if (!xstrcasecmp(tok, "within")) {
			mode_num |= PREEMPT_MODE_WITHIN;
		} else if (!xstrcasecmp(tok, "priority")) {
			mode_num |= PREEMPT_MODE_PRIORITY;
		} else if ((xstrcasecmp(tok, "off") == 0)
			   || (xstrcasecmp(tok, "cluster") == 0)) {
			mode_num += PREEMPT_MODE_OFF;
			preempt_modes++;
		} else if (xstrcasecmp(tok, "cancel") == 0) {
			mode_num += PREEMPT_MODE_CANCEL;
			preempt_modes++;
		} else if (xstrcasecmp(tok, "requeue") == 0) {
			mode_num += PREEMPT_MODE_REQUEUE;
			preempt_modes++;
		} else if ((xstrcasecmp(tok, "on") == 0) ||
			   (xstrcasecmp(tok, "suspend") == 0)) {
			mode_num += PREEMPT_MODE_SUSPEND;
			preempt_modes++;
		} else {
			preempt_modes = 0;
			mode_num = NO_VAL16;
			break;
		}
		tok = strtok_r(NULL, ",", &last);
	}
	xfree(tmp_str);

	if (preempt_modes > 1) {
		/*
		 * Only one mode value may be set, optionally combined with
		 * GANG or WITHIN.
		 */
		mode_num = NO_VAL16;
	} else if ((mode_num & PREEMPT_MODE_GANG) &&
		   (mode_num & PREEMPT_MODE_WITHIN)) {
		/* "GANG,WITHIN" is an invalid combination */
		mode_num = NO_VAL16;
	} else if ((mode_num & PREEMPT_MODE_GANG) &&
		   (mode_num & PREEMPT_MODE_PRIORITY)) {
		/* "GANG,PRIORITY" is an invalid combination */
		mode_num = NO_VAL16;
	}

	return mode_num;
}

/* Convert log level number to equivalent string */
extern char *log_num2string(uint16_t inx)
{
	switch (inx) {
	case LOG_LEVEL_QUIET:
		return "quiet";
	case LOG_LEVEL_FATAL:
		return "fatal";
	case LOG_LEVEL_ERROR:
		return "error";
	case LOG_LEVEL_INFO:
		return "info";
	case LOG_LEVEL_VERBOSE:
		return "verbose";
	case LOG_LEVEL_DEBUG:
		return "debug";
	case LOG_LEVEL_DEBUG2:
		return "debug2";
	case LOG_LEVEL_DEBUG3:
		return "debug3";
	case LOG_LEVEL_DEBUG4:
		return "debug4";
	case LOG_LEVEL_DEBUG5:
		return "debug5";
	case LOG_LEVEL_END:
		/*
		 * "(null)" is printed through 'scontrol show config' to
		 * to indicate a given value has not been set. Convert
		 * LOG_LEVEL_END to "(null)" to indicate a given logging
		 * channel is disabled, rather than printing "unknown".
		 */
		return "(null)";
	default:
		return "unknown";
	}
}

/* Convert log level string to equivalent number */
extern uint16_t log_string2num(const char *name)
{
	if (name == NULL)
		return NO_VAL16;

	if ((name[0] >= '0') && (name[0] <= '9'))
		return (uint16_t) atoi(name);

	if (!xstrcasecmp(name, "quiet"))
		return (uint16_t) 0;
	if (!xstrcasecmp(name, "fatal"))
		return (uint16_t) 1;
	if (!xstrcasecmp(name, "error"))
		return (uint16_t) 2;
	if (!xstrcasecmp(name, "info"))
		return (uint16_t) 3;
	if (!xstrcasecmp(name, "verbose"))
		return (uint16_t) 4;
	if (!xstrcasecmp(name, "debug"))
		return (uint16_t) 5;
	if (!xstrcasecmp(name, "debug2"))
		return (uint16_t) 6;
	if (!xstrcasecmp(name, "debug3"))
		return (uint16_t) 7;
	if (!xstrcasecmp(name, "debug4"))
		return (uint16_t) 8;
	if (!xstrcasecmp(name, "debug5"))
		return (uint16_t) 9;

	return NO_VAL16;
}

extern char *job_share_string(uint16_t shared)
{
	if (shared == JOB_SHARED_NONE)
		return "NO";
	else if (shared == JOB_SHARED_OK)
		return "YES";
	else if (shared == JOB_SHARED_USER)
		return "USER";
	else if (shared == JOB_SHARED_MCS)
		return "MCS";
	else if (shared == JOB_SHARED_TOPO)
		return "TOPO";
	else
		return "OK";
}

/*
 * Oversubscribe display string for cred/env: NO|YES|OK (JOB_OVERSUBSCRIBE_*).
 */
extern char *job_oversubscribe_string(uint16_t val)
{
	if (val == JOB_OVERSUBSCRIBE_NO)
		return "NO";
	if (val == JOB_OVERSUBSCRIBE_YES)
		return "YES";
	if (val == JOB_OVERSUBSCRIBE_OK)
		return "OK";
	return "NO";
}

/*
 * Exclusive display string for cred/env: NO|NODE|USER|MCS|TOPO.
 * val is JOB_EXCLUSIVE_* (single value, no combinations).
 */
extern char *job_exclusive_display_string(uint16_t val)
{
	switch (val) {
	case JOB_EXCLUSIVE_NONE:
		return "NO";
	case JOB_EXCLUSIVE_NODE:
		return "NODE";
	case JOB_EXCLUSIVE_USER:
		return "USER";
	case JOB_EXCLUSIVE_MCS:
		return "MCS";
	case JOB_EXCLUSIVE_TOPO:
		return "TOPO";
	default:
		return "NO";
	}
}

extern char *job_state_string(uint32_t inx)
{
	/* Process JOB_STATE_FLAGS */
	if (inx & JOB_COMPLETING)
		return "COMPLETING";
	if (inx & JOB_STAGE_OUT)
		return "STAGE_OUT";
	if (inx & JOB_CONFIGURING)
		return "CONFIGURING";
	if (inx & JOB_EXPEDITING)
		return "EXPEDITING";
	if (inx & JOB_RESIZING)
		return "RESIZING";
	if (inx & JOB_REQUEUE)
		return "REQUEUED";
	if (inx & JOB_REQUEUE_FED)
		return "REQUEUE_FED";
	if (inx & JOB_REQUEUE_HOLD)
		return "REQUEUE_HOLD";
	if (inx & JOB_SPECIAL_EXIT)
		return "SPECIAL_EXIT";
	if (inx & JOB_STOPPED)
		return "STOPPED";
	if (inx & JOB_REVOKED)
		return "REVOKED";
	if (inx & JOB_RESV_DEL_HOLD)
		return "RESV_DEL_HOLD";
	if (inx & JOB_SIGNALING)
		return "SIGNALING";

	/* Process JOB_STATE_BASE */
	switch (inx & JOB_STATE_BASE) {
	case JOB_PENDING:
		return "PENDING";
	case JOB_RUNNING:
		return "RUNNING";
	case JOB_SUSPENDED:
		return "SUSPENDED";
	case JOB_COMPLETE:
		return "COMPLETED";
	case JOB_CANCELLED:
		return "CANCELLED";
	case JOB_FAILED:
		return "FAILED";
	case JOB_TIMEOUT:
		return "TIMEOUT";
	case JOB_NODE_FAIL:
		return "NODE_FAIL";
	case JOB_PREEMPTED:
		return "PREEMPTED";
	case JOB_BOOT_FAIL:
		return "BOOT_FAIL";
	case JOB_DEADLINE:
		return "DEADLINE";
	case JOB_OOM:
		return "OUT_OF_MEMORY";
	default:
		return "?";
	}
}

extern char *job_state_string_compact(uint32_t inx)
{
	/* Process JOB_STATE_FLAGS */
	if (inx & JOB_COMPLETING)
		return "CG";
	if (inx & JOB_STAGE_OUT)
		return "SO";
	if (inx & JOB_CONFIGURING)
		return "CF";
	if (inx & JOB_RESIZING)
		return "RS";
	if (inx & JOB_REQUEUE)
		return "RQ";
	if (inx & JOB_REQUEUE_FED)
		return "RF";
	if (inx & JOB_REQUEUE_HOLD)
		return "RH";
	if (inx & JOB_SPECIAL_EXIT)
		return "SE";
	if (inx & JOB_STOPPED)
		return "ST";
	if (inx & JOB_REVOKED)
		return "RV";
	if (inx & JOB_RESV_DEL_HOLD)
		return "RD";
	if (inx & JOB_SIGNALING)
		return "SI";

	/* Process JOB_STATE_BASE */
	switch (inx & JOB_STATE_BASE) {
	case JOB_PENDING:
		return "PD";
	case JOB_RUNNING:
		return "R";
	case JOB_SUSPENDED:
		return "S";
	case JOB_COMPLETE:
		return "CD";
	case JOB_CANCELLED:
		return "CA";
	case JOB_FAILED:
		return "F";
	case JOB_TIMEOUT:
		return "TO";
	case JOB_NODE_FAIL:
		return "NF";
	case JOB_PREEMPTED:
		return "PR";
	case JOB_BOOT_FAIL:
		return "BF";
	case JOB_DEADLINE:
		return "DL";
	case JOB_OOM:
		return "OOM";
	default:
		return "?";
	}
}

/*
 * job_state_string_complete - build a string describing the job state
 *
 * IN: state - job state
 * RET string representation of the job state;
 * NOTE: the caller must call xfree() on the RET value to free memory
 */
extern char *job_state_string_complete(uint32_t state)
{
	/* Malloc space ahead of time to avoid realloc inside of xstrcat. */
	char *state_str = xmalloc(100);

	if (state == NO_VAL) {
		xstrcat(state_str, "UNINITIALISED");
		return state_str;
	}

	/* Process JOB_STATE_BASE */
	switch (state & JOB_STATE_BASE) {
	case JOB_PENDING:
		xstrcat(state_str, "PENDING");
		break;
	case JOB_RUNNING:
		xstrcat(state_str, "RUNNING");
		break;
	case JOB_SUSPENDED:
		xstrcat(state_str, "SUSPENDED");
		break;
	case JOB_COMPLETE:
		xstrcat(state_str, "COMPLETED");
		break;
	case JOB_CANCELLED:
		xstrcat(state_str, "CANCELLED");
		break;
	case JOB_FAILED:
		xstrcat(state_str, "FAILED");
		break;
	case JOB_TIMEOUT:
		xstrcat(state_str, "TIMEOUT");
		break;
	case JOB_NODE_FAIL:
		xstrcat(state_str, "NODE_FAIL");
		break;
	case JOB_PREEMPTED:
		xstrcat(state_str, "PREEMPTED");
		break;
	case JOB_BOOT_FAIL:
		xstrcat(state_str, "BOOT_FAIL");
		break;
	case JOB_DEADLINE:
		xstrcat(state_str, "DEADLINE");
		break;
	case JOB_OOM:
		xstrcat(state_str, "OUT_OF_MEMORY");
		break;
	default:
		xstrcat(state_str, "?");
		break;
	}

	/* Process JOB_STATE_FLAGS */
	if (state & JOB_LAUNCH_FAILED)
		xstrcat(state_str, ",LAUNCH_FAILED");
	if (state & JOB_COMPLETING)
		xstrcat(state_str, ",COMPLETING");
	if (state & JOB_CONFIGURING)
		xstrcat(state_str, ",CONFIGURING");
	if (state & JOB_EXPEDITING)
		xstrcat(state_str, ",EXPEDITING");
	if (state & JOB_POWER_UP_NODE)
		xstrcat(state_str, ",POWER_UP_NODE");
	if (state & JOB_RECONFIG_FAIL)
		xstrcat(state_str, ",RECONFIG_FAIL");
	if (state & JOB_RESIZING)
		xstrcat(state_str, ",RESIZING");
	if (state & JOB_REQUEUE)
		xstrcat(state_str, ",REQUEUED");
	if (state & JOB_REQUEUE_FED)
		xstrcat(state_str, ",REQUEUE_FED");
	if (state & JOB_REQUEUE_HOLD)
		xstrcat(state_str, ",REQUEUE_HOLD");
	if (state & JOB_SPECIAL_EXIT)
		xstrcat(state_str, ",SPECIAL_EXIT");
	if (state & JOB_STOPPED)
		xstrcat(state_str, ",STOPPED");
	if (state & JOB_REVOKED)
		xstrcat(state_str, ",REVOKED");
	if (state & JOB_RESV_DEL_HOLD)
		xstrcat(state_str, ",RESV_DEL_HOLD");
	if (state & JOB_SIGNALING)
		xstrcat(state_str, ",SIGNALING");
	if (state & JOB_STAGE_OUT)
		xstrcat(state_str, ",STAGE_OUT");

	return state_str;
}

static bool _job_name_test(uint32_t state_num, const char *state_name)
{
	if (!xstrcasecmp(state_name, job_state_string(state_num)) ||
	    !xstrcasecmp(state_name, job_state_string_compact(state_num))) {
		return true;
	}
	return false;
}

extern uint32_t job_state_num(const char *state_name)
{
	uint32_t i;

	for (i = 0; i < JOB_END; i++) {
		if (_job_name_test(i, state_name))
			return i;
	}

	if (_job_name_test(JOB_COMPLETING, state_name))
		return JOB_COMPLETING;
	if (_job_name_test(JOB_CONFIGURING, state_name))
		return JOB_CONFIGURING;
	if (_job_name_test(JOB_EXPEDITING, state_name))
		return JOB_EXPEDITING;
	if (_job_name_test(JOB_RESIZING, state_name))
		return JOB_RESIZING;
	if (_job_name_test(JOB_RESV_DEL_HOLD, state_name))
		return JOB_RESV_DEL_HOLD;
	if (_job_name_test(JOB_REQUEUE, state_name))
		return JOB_REQUEUE;
	if (_job_name_test(JOB_REQUEUE_FED, state_name))
		return JOB_REQUEUE_FED;
	if (_job_name_test(JOB_REQUEUE_HOLD, state_name))
		return JOB_REQUEUE_HOLD;
	if (_job_name_test(JOB_REVOKED, state_name))
		return JOB_REVOKED;
	if (_job_name_test(JOB_SIGNALING, state_name))
		return JOB_SIGNALING;
	if (_job_name_test(JOB_SPECIAL_EXIT, state_name))
		return JOB_SPECIAL_EXIT;
	if (_job_name_test(JOB_STAGE_OUT, state_name))
		return JOB_STAGE_OUT;
	if (_job_name_test(JOB_STOPPED, state_name))
		return JOB_STOPPED;

	return NO_VAL;
}

extern char *trigger_res_type(uint16_t res_type)
{
	if      (res_type == TRIGGER_RES_TYPE_JOB)
		return "job";
	else if (res_type == TRIGGER_RES_TYPE_NODE)
		return "node";
	else if (res_type == TRIGGER_RES_TYPE_SLURMCTLD)
		return "slurmctld";
	else if (res_type == TRIGGER_RES_TYPE_SLURMDBD)
		return "slurmdbd";
	else if (res_type == TRIGGER_RES_TYPE_DATABASE)
		return "database";
	else if (res_type == TRIGGER_RES_TYPE_OTHER)
		return "other";
	else
		return "unknown";
}

/* Convert HealthCheckNodeState numeric value to a string.
 * Caller must xfree() the return value */
extern char *health_check_node_state_str(uint32_t node_state)
{
	char *state_str = NULL;
	char *sep = "";

	if (node_state & HEALTH_CHECK_CYCLE) {
		state_str = xstrdup("CYCLE");
		sep = ",";
	}

	if ((node_state & HEALTH_CHECK_NODE_ANY) == HEALTH_CHECK_NODE_ANY) {
		xstrfmtcat(state_str, "%s%s", sep, "ANY");
		return state_str;
	}

	if (node_state & HEALTH_CHECK_NODE_IDLE) {
		xstrfmtcat(state_str, "%s%s", sep, "IDLE");
		sep = ",";
	}
	if (node_state & HEALTH_CHECK_NODE_ALLOC) {
		xstrfmtcat(state_str, "%s%s", sep, "ALLOC");
		sep = ",";
	}
	if (node_state & HEALTH_CHECK_NODE_MIXED) {
		xstrfmtcat(state_str, "%s%s", sep, "MIXED");
		sep = ",";
	}
	if (node_state & HEALTH_CHECK_NODE_NONDRAINED_IDLE) {
		xstrfmtcat(state_str, "%s%s", sep, "NONDRAINED_IDLE");
		sep = ",";
	}
	if (node_state & HEALTH_CHECK_REBOOT_ONLY) {
		xstrfmtcat(state_str, "%s%s", sep, "REBOOT_ONLY");
		sep = ",";
	}
	if (node_state & HEALTH_CHECK_START_ONLY) {
		xstrfmtcat(state_str, "%s%s", sep, "START_ONLY");
		sep = ",";
	}

	return state_str;
}

extern char *trigger_type(uint32_t trig_type)
{
	if      (trig_type == TRIGGER_TYPE_UP)
		return "up";
	else if (trig_type == TRIGGER_TYPE_DOWN)
		return "down";
	else if (trig_type == TRIGGER_TYPE_DRAINED)
		return "drained";
	else if (trig_type == TRIGGER_TYPE_FAIL)
		return "fail";
	else if (trig_type == TRIGGER_TYPE_IDLE)
		return "idle";
	else if (trig_type == TRIGGER_TYPE_TIME)
		return "time";
	else if (trig_type == TRIGGER_TYPE_FINI)
		return "fini";
	else if (trig_type == TRIGGER_TYPE_RECONFIG)
		return "reconfig";
	else if (trig_type == TRIGGER_TYPE_PRI_CTLD_FAIL)
		return "primary_slurmctld_failure";
	else if (trig_type == TRIGGER_TYPE_PRI_CTLD_RES_OP)
		return "primary_slurmctld_resumed_operation";
	else if (trig_type == TRIGGER_TYPE_PRI_CTLD_RES_CTRL)
		return "primary_slurmctld_resumed_control";
	else if (trig_type == TRIGGER_TYPE_PRI_CTLD_ACCT_FULL)
		return "primary_slurmctld_acct_buffer_full";
	else if (trig_type == TRIGGER_TYPE_BU_CTLD_FAIL)
		return "backup_slurmctld_failure";
	else if (trig_type == TRIGGER_TYPE_BU_CTLD_RES_OP)
		return "backup_slurmctld_resumed_operation";
	else if (trig_type == TRIGGER_TYPE_BU_CTLD_AS_CTRL)
		return "backup_slurmctld_assumed_control";
	else if (trig_type == TRIGGER_TYPE_PRI_DBD_FAIL)
		return "primary_slurmdbd_failure";
	else if (trig_type == TRIGGER_TYPE_PRI_DBD_RES_OP)
		return "primary_slurmdbd_resumed_operation";
	else if (trig_type == TRIGGER_TYPE_PRI_DB_FAIL)
		return "primary_database_failure";
	else if (trig_type == TRIGGER_TYPE_PRI_DB_RES_OP)
		return "primary_database_resumed_operation";
	else if (trig_type == TRIGGER_TYPE_BURST_BUFFER)
		return "burst_buffer";
	else if (trig_type == TRIGGER_TYPE_DRAINING)
		return "draining";
	else if (trig_type == TRIGGER_TYPE_RESUME)
		return "resume";
	else
		return "unknown";
}

/* user needs to xfree return value */
extern char *reservation_flags_string(reserve_info_t * resv_ptr)
{
	char *flag_str = xstrdup("");
	uint64_t flags = resv_ptr->flags;

	if (flags & RESERVE_FLAG_MAINT)
		xstrcat(flag_str, "MAINT");
	if (flags & RESERVE_FLAG_NO_MAINT) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_MAINT");
	}
	if (flags & RESERVE_FLAG_FLEX) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "FLEX");
	}
	if (flags & RESERVE_FLAG_OVERLAP) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "OVERLAP");
	}
	if (flags & RESERVE_FLAG_IGN_JOBS) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "IGNORE_JOBS");
	}
	if (flags & RESERVE_FLAG_HOURLY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "HOURLY");
	}
	if (flags & RESERVE_FLAG_NO_HOURLY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_HOURLY");
	}
	if (flags & RESERVE_FLAG_DAILY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "DAILY");
	}
	if (flags & RESERVE_FLAG_NO_DAILY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_DAILY");
	}
	if (flags & RESERVE_FLAG_WEEKDAY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "WEEKDAY");
	}
	if (flags & RESERVE_FLAG_WEEKEND) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "WEEKEND");
	}
	if (flags & RESERVE_FLAG_WEEKLY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "WEEKLY");
	}
	if (flags & RESERVE_FLAG_NO_WEEKLY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_WEEKLY");
	}
	if (flags & RESERVE_FLAG_SPEC_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "SPEC_NODES");
	}
	if (flags & RESERVE_FLAG_ALL_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "ALL_NODES");
	}
	if (flags & RESERVE_FLAG_ANY_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "ANY_NODES");
	}
	if (flags & RESERVE_FLAG_NO_ANY_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_ANY_NODES");
	}
	if (flags & RESERVE_FLAG_STATIC) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "STATIC");
	}
	if (flags & RESERVE_FLAG_NO_STATIC) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_STATIC");
	}
	if (flags & RESERVE_FLAG_PART_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "PART_NODES");
	}
	if (flags & RESERVE_FLAG_NO_PART_NODES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_PART_NODES");
	}
	if (flags & RESERVE_FLAG_TIME_FLOAT) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "TIME_FLOAT");
	}
	if (flags & RESERVE_FLAG_REPLACE) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "REPLACE");
	}
	if (flags & RESERVE_FLAG_REPLACE_DOWN) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "REPLACE_DOWN");
	}
	if (flags & RESERVE_FLAG_PURGE_COMP) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		if (resv_ptr->purge_comp_time) {
			char tmp_pct[40];
			secs2time_str(resv_ptr->purge_comp_time,
				      tmp_pct, sizeof(tmp_pct));
			xstrfmtcat(flag_str, "PURGE_COMP=%s", tmp_pct);
		} else
			xstrcat(flag_str, "PURGE_COMP");
	}
	if (flags & RESERVE_FLAG_NO_HOLD_JOBS) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_HOLD_JOBS_AFTER_END");
	}
	if (flags & RESERVE_FLAG_MAGNETIC) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "MAGNETIC");
	}
	if (flags & RESERVE_FLAG_NO_MAGNETIC) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_MAGNETIC");
	}
	if (flags & RESERVE_FLAG_USER_DEL) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "USER_DELETE");
	}
	if (flags & RESERVE_FLAG_NO_USER_DEL) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_USER_DELETE");
	}
	if (flags & RESERVE_FLAG_FORCE_START) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "FORCE_START");
	}

	return flag_str;
}

/* user needs to xfree return value */
extern char *priority_flags_string(uint16_t priority_flags)
{
	char *flag_str = xstrdup("");

	if (priority_flags & PRIORITY_FLAGS_ACCRUE_ALWAYS)
		xstrcat(flag_str, "ACCRUE_ALWAYS");
	if (priority_flags & PRIORITY_FLAGS_SIZE_RELATIVE) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "SMALL_RELATIVE_TO_TIME");
	}
	if (priority_flags & PRIORITY_FLAGS_CALCULATE_RUNNING) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "CALCULATE_RUNNING");
	}
	if (priority_flags & PRIORITY_FLAGS_DEPTH_OBLIVIOUS) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "DEPTH_OBLIVIOUS");
	}
	if (!(priority_flags & PRIORITY_FLAGS_FAIR_TREE)) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_FAIR_TREE");
	}
	if (priority_flags & PRIORITY_FLAGS_INCR_ONLY) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "INCR_ONLY");
	}
	if (priority_flags & PRIORITY_FLAGS_MAX_TRES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "MAX_TRES");
	}
	if (priority_flags & PRIORITY_FLAGS_MAX_TRES_GRES) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "MAX_TRES_GRES");
	}
	if ((priority_flags & PRIORITY_FLAGS_NO_NORMAL_ASSOC) &&
	    (priority_flags & PRIORITY_FLAGS_NO_NORMAL_PART) &&
	    (priority_flags & PRIORITY_FLAGS_NO_NORMAL_QOS) &&
	    (priority_flags & PRIORITY_FLAGS_NO_NORMAL_TRES)) {
		if (flag_str[0])
			xstrcat(flag_str, ",");
		xstrcat(flag_str, "NO_NORMAL_ALL");
	} else {
		if (priority_flags & PRIORITY_FLAGS_NO_NORMAL_ASSOC) {
			if (flag_str[0])
				xstrcat(flag_str, ",");
			xstrcat(flag_str, "NO_NORMAL_ASSOC");
		}
		if (priority_flags & PRIORITY_FLAGS_NO_NORMAL_PART) {
			if (flag_str[0])
				xstrcat(flag_str, ",");
			xstrcat(flag_str, "NO_NORMAL_PART");
		}
		if (priority_flags & PRIORITY_FLAGS_NO_NORMAL_QOS) {
			if (flag_str[0])
				xstrcat(flag_str, ",");
			xstrcat(flag_str, "NO_NORMAL_QOS");
		}
		if (priority_flags & PRIORITY_FLAGS_NO_NORMAL_TRES) {
			if (flag_str[0])
				xstrcat(flag_str, ",");
			xstrcat(flag_str, "NO_NORMAL_TRES");
		}
	}

	return flag_str;
}

/* Translate a burst buffer numeric value to its equivalent state string */
extern char *bb_state_string(uint16_t state)
{
	static char buf[16];

	if (state == BB_STATE_PENDING)
		return "pending";
	if (state == BB_STATE_ALLOCATING)
		return "allocating";
	if (state == BB_STATE_ALLOCATED)
		return "allocated";
	if (state == BB_STATE_DELETING)
		return "deleting";
	if (state == BB_STATE_DELETED)
		return "deleted";
	if (state == BB_STATE_STAGING_IN)
		return "staging-in";
	if (state == BB_STATE_STAGED_IN)
		return "staged-in";
	if (state == BB_STATE_PRE_RUN)
		return "pre-run";
	if (state == BB_STATE_ALLOC_REVOKE)
		return "alloc-revoke";
	if (state == BB_STATE_RUNNING)
		return "running";
	if (state == BB_STATE_SUSPEND)
		return "suspended";
	if (state == BB_STATE_POST_RUN)
		return "post-run";
	if (state == BB_STATE_STAGING_OUT)
		return "staging-out";
	if (state == BB_STATE_STAGED_OUT)
		return "staged-out";
	if (state == BB_STATE_TEARDOWN)
		return "teardown";
	if (state == BB_STATE_TEARDOWN_FAIL)
		return "teardown-fail";
	if (state == BB_STATE_COMPLETE)
		return "complete";
	snprintf(buf, sizeof(buf), "%u", state);
	return buf;
}

/* Translate a burst buffer state string to its equivalent numeric value */
extern uint16_t bb_state_num(char *tok)
{
	if (!xstrcasecmp(tok, "pending"))
		return BB_STATE_PENDING;
	if (!xstrcasecmp(tok, "allocating"))
		return BB_STATE_ALLOCATING;
	if (!xstrcasecmp(tok, "allocated"))
		return BB_STATE_ALLOCATED;
	if (!xstrcasecmp(tok, "deleting"))
		return BB_STATE_DELETING;
	if (!xstrcasecmp(tok, "deleted"))
		return BB_STATE_DELETED;
	if (!xstrcasecmp(tok, "staging-in"))
		return BB_STATE_STAGING_IN;
	if (!xstrcasecmp(tok, "staged-in"))
		return BB_STATE_STAGED_IN;
	if (!xstrcasecmp(tok, "pre-run"))
		return BB_STATE_PRE_RUN;
	if (!xstrcasecmp(tok, "alloc-revoke"))
		return BB_STATE_ALLOC_REVOKE;
	if (!xstrcasecmp(tok, "running"))
		return BB_STATE_RUNNING;
	if (!xstrcasecmp(tok, "suspend"))
		return BB_STATE_SUSPEND;
	if (!xstrcasecmp(tok, "post-run"))
		return BB_STATE_POST_RUN;
	if (!xstrcasecmp(tok, "staging-out"))
		return BB_STATE_STAGING_OUT;
	if (!xstrcasecmp(tok, "staged-out"))
		return BB_STATE_STAGED_OUT;
	if (!xstrcasecmp(tok, "teardown"))
		return BB_STATE_TEARDOWN;
	if (!xstrcasecmp(tok, "teardown-fail"))
		return BB_STATE_TEARDOWN_FAIL;
	if (!xstrcasecmp(tok, "complete"))
		return BB_STATE_COMPLETE;
	return 0;
}

extern bool valid_base_state(uint32_t state)
{
	for (int i = 0; i < ARRAY_SIZE(node_states); i++) {
		if (node_states[i].flag == (state & NODE_STATE_BASE))
			return true;
	}
	return false;
}

extern const char *node_state_base_string(uint32_t state)
{
	state &= NODE_STATE_BASE;

	for (int i = 0; i < ARRAY_SIZE(node_states); i++)
		if (node_states[i].flag == state)
			return node_states[i].str;

	return "INVALID";
}

extern const char *node_state_flag_string_single(uint32_t *state)
{
	uint32_t flags = *state & NODE_STATE_FLAGS;

	if (!flags)
		return NULL;

	for (int i = 0; i < ARRAY_SIZE(node_state_flags); i++) {
		if (flags & node_state_flags[i].flag) {
			*state &= ~node_state_flags[i].flag;
			return node_state_flags[i].str;
		}
	}
	/*
	 * clear lowest flag bit, in order to guarantee that flags goes to 0 on
	 * repeated calls. Any uncaught flags are unknown here.
	 */
	*state &= ~(flags & -flags);
	return "?";
}

extern char *node_state_flag_string(uint32_t state)
{
	uint32_t flags = state & NODE_STATE_FLAGS;
	const char *flag_str = NULL;
	char *state_str = NULL;

	while ((flag_str = node_state_flag_string_single(&flags))) {
		xstrfmtcat(state_str, "+%s", flag_str);
	}
	return state_str;
}

extern char *node_state_string_complete(uint32_t state)
{
	char *state_str = NULL, *flags_str = NULL;

	state_str = xstrdup(node_state_base_string(state));
	if ((flags_str = node_state_flag_string(state))) {
		xstrcat(state_str, flags_str);
		xfree(flags_str);
	}

	return state_str;
}

extern uint32_t parse_node_state_flag(char *flag_str)
{
	int flag_str_len = strlen(flag_str);

	for (int i = 0; i < ARRAY_SIZE(node_state_flags); i++) {
		if (!xstrncasecmp(flag_str, node_state_flags[i].str,
				  MIN(flag_str_len,
				      strlen(node_state_flags[i].str)))) {
			return node_state_flags[i].flag;
		}
	}

	return 0; /* No flags */
}

extern char *node_state_string(uint32_t inx)
{
	int  base            = (inx & NODE_STATE_BASE);
	bool blocked_flag = (inx & NODE_STATE_BLOCKED);
	bool comp_flag       = (inx & NODE_STATE_COMPLETING);
	bool drain_flag      = (inx & NODE_STATE_DRAIN);
	bool fail_flag       = (inx & NODE_STATE_FAIL);
	bool maint_flag      = (inx & NODE_STATE_MAINT);
	bool reboot_flag     = (inx & NODE_STATE_REBOOT_REQUESTED);
	bool reboot_issued_flag = (inx & NODE_STATE_REBOOT_ISSUED);
	bool res_flag        = (inx & NODE_STATE_RES);
	bool resume_flag     = (inx & NODE_RESUME);
	bool no_resp_flag    = (inx & NODE_STATE_NO_RESPOND);
	bool planned_flag    = (inx & NODE_STATE_PLANNED);
	bool powered_down_flag = (inx & NODE_STATE_POWERED_DOWN);
	bool power_up_flag   = (inx & NODE_STATE_POWERING_UP);
	bool powering_down_flag = (inx & NODE_STATE_POWERING_DOWN);
	bool power_down_flag = (inx & NODE_STATE_POWER_DOWN);

	if (inx & NODE_STATE_INVALID_REG)
		return "INVAL";

	if (maint_flag) {
		if (drain_flag ||
		    (base == NODE_STATE_ALLOCATED) ||
		    (base == NODE_STATE_DOWN) ||
		    (base == NODE_STATE_MIXED))
			;
		else if (no_resp_flag)
			return "MAINT*";
		else
			return "MAINT";
	}
	if (reboot_flag || reboot_issued_flag) {
		if ((base == NODE_STATE_ALLOCATED) ||
		    (base == NODE_STATE_MIXED))
			;
		else if (reboot_issued_flag)
			return "REBOOT^";
		else if (no_resp_flag)
			return "REBOOT*";
		else
			return "REBOOT";
	}
	if (drain_flag) {
		if (comp_flag
		    || (base == NODE_STATE_ALLOCATED)
		    || (base == NODE_STATE_MIXED)) {
			if (maint_flag)
				return "DRAINING$";
			if (reboot_issued_flag)
				return "DRAINING^";
			if (reboot_flag)
				return "DRAINING@";
			if (power_up_flag)
				return "DRAINING#";
			if (powering_down_flag)
				return "DRAINING%";
			if (powered_down_flag)
				return "DRAINING~";
			if (power_down_flag)
				return "DRAINING!";
			if (no_resp_flag)
				return "DRAINING*";
			return "DRAINING";
		} else {
			if (maint_flag)
				return "DRAINED$";
			if (reboot_issued_flag)
				return "DRAINED^";
			if (reboot_flag)
				return "DRAINED@";
			if (power_up_flag)
				return "DRAINED#";
			if (powering_down_flag)
				return "DRAINED%";
			if (powered_down_flag)
				return "DRAINED~";
			if (power_down_flag)
				return "DRAINED!";
			if (no_resp_flag)
				return "DRAINED*";
			return "DRAINED";
		}
	}
	if (fail_flag) {
		if (comp_flag || (base == NODE_STATE_ALLOCATED) ||
		    (base == NODE_STATE_MIXED)) {
			if (no_resp_flag)
				return "FAILING*";
			return "FAILING";
		} else {
			if (no_resp_flag)
				return "FAIL*";
			return "FAIL";
		}
	}

	if (inx == NODE_STATE_REBOOT_ISSUED)
		return "REBOOT_ISSUED";
	if (inx == NODE_STATE_REBOOT_CANCEL)
		return "CANCEL_REBOOT";
	if (inx == NODE_STATE_CLOUD)
		return "CLOUD";
	if (inx == NODE_STATE_EXTERNAL)
		return "EXTERNAL";
	if (inx == NODE_STATE_POWER_DOWN)
		return "POWER_DOWN";
	if (inx == NODE_STATE_POWER_UP)
		return "POWER_UP";
	if (inx == NODE_STATE_POWERING_DOWN)
		return "POWERING_DOWN";
	if (inx == NODE_STATE_POWERED_DOWN)
		return "POWERED_DOWN";
	if (inx == NODE_STATE_POWERING_UP)
		return "POWERING_UP";
	if (inx == NODE_STATE_UNDRAIN)
		return "UNDRAIN";
	if (inx == NODE_STATE_DYNAMIC_FUTURE)
		return "DYNAMIC_FUTURE";
	if (inx == NODE_STATE_DYNAMIC_NORM)
		return "DYNAMIC_NORM";
	if (base == NODE_STATE_DOWN) {
		if (maint_flag)
			return "DOWN$";
		if (reboot_issued_flag)
			return "DOWN^";
		if (reboot_flag)
			return "DOWN@";
		if (power_up_flag)
			return "DOWN#";
		if (powering_down_flag)
			return "DOWN%";
		if (powered_down_flag)
			return "DOWN~";
		if (power_down_flag)
			return "DOWN!";
		if (no_resp_flag)
			return "DOWN*";
		return "DOWN";
	}

	if (base == NODE_STATE_ALLOCATED) {
		if (maint_flag)
			return "ALLOCATED$";
		if (reboot_issued_flag)
			return "ALLOCATED^";
		if (reboot_flag)
			return "ALLOCATED@";
		if (power_up_flag)
			return "ALLOCATED#";
		if (powering_down_flag)
			return "ALLOCATED%";
		if (powered_down_flag)
			return "ALLOCATED~";
		if (power_down_flag)
			return "ALLOCATED!";
		if (no_resp_flag)
			return "ALLOCATED*";
		if (comp_flag)
			return "ALLOCATED+";
		return "ALLOCATED";
	}
	if (comp_flag) {
		if (maint_flag)
			return "COMPLETING$";
		if (reboot_issued_flag)
			return "COMPLETING^";
		if (reboot_flag)
			return "COMPLETING@";
		if (power_up_flag)
			return "COMPLETING#";
		if (powering_down_flag)
			return "COMPLETING%";
		if (powered_down_flag)
			return "COMPLETING~";
		if (power_down_flag)
			return "COMPLETING!";
		if (no_resp_flag)
			return "COMPLETING*";
		return "COMPLETING";
	}
	if (base == NODE_STATE_IDLE) {
		if (maint_flag)
			return "IDLE$";
		if (reboot_issued_flag)
			return "IDLE^";
		if (reboot_flag)
			return "IDLE@";
		if (power_up_flag)
			return "IDLE#";
		if (powering_down_flag)
			return "IDLE%";
		if (powered_down_flag)
			return "IDLE~";
		if (power_down_flag)
			return "IDLE!";
		if (no_resp_flag)
			return "IDLE*";
		if (res_flag)
			return "RESERVED";
		if (blocked_flag)
			return "BLOCKED";
		if (planned_flag)
			return "PLANNED";
		return "IDLE";
	}
	if (base == NODE_STATE_MIXED) {
		if (maint_flag)
			return "MIXED$";
		if (reboot_issued_flag)
			return "MIXED^";
		if (reboot_flag)
			return "MIXED@";
		if (power_up_flag)
			return "MIXED#";
		if (powering_down_flag)
			return "MIXED%";
		if (powered_down_flag)
			return "MIXED~";
		if (power_down_flag)
			return "MIXED!";
		if (no_resp_flag)
			return "MIXED*";
		if (planned_flag)
			return "MIXED-";
		return "MIXED";
	}
	if (base == NODE_STATE_FUTURE) {
		if (maint_flag)
			return "FUTURE$";
		if (reboot_issued_flag)
			return "FUTURE^";
		if (reboot_flag)
			return "FUTURE@";
		if (power_up_flag)
			return "FUTURE#";
		if (powering_down_flag)
			return "FUTURE%";
		if (powered_down_flag)
			return "FUTURE~";
		if (power_down_flag)
			return "FUTURE!";
		if (no_resp_flag)
			return "FUTURE*";
		return "FUTURE";
	}
	if (resume_flag)
		return "RESUME";
	if (base == NODE_STATE_UNKNOWN) {
		if (no_resp_flag)
			return "UNKNOWN*";
		return "UNKNOWN";
	}
	return "?";
}

extern char *node_state_string_compact(uint32_t inx)
{
	bool blocked_flag = (inx & NODE_STATE_BLOCKED);
	bool comp_flag       = (inx & NODE_STATE_COMPLETING);
	bool drain_flag      = (inx & NODE_STATE_DRAIN);
	bool fail_flag       = (inx & NODE_STATE_FAIL);
	bool maint_flag      = (inx & NODE_STATE_MAINT);
	bool reboot_flag     = (inx & NODE_STATE_REBOOT_REQUESTED);
	bool reboot_issued_flag = (inx & NODE_STATE_REBOOT_ISSUED);
	bool res_flag        = (inx & NODE_STATE_RES);
	bool resume_flag     = (inx & NODE_RESUME);
	bool no_resp_flag    = (inx & NODE_STATE_NO_RESPOND);
	bool planned_flag    = (inx & NODE_STATE_PLANNED);
	bool powered_down_flag = (inx & NODE_STATE_POWERED_DOWN);
	bool power_up_flag   = (inx & NODE_STATE_POWERING_UP);
	bool powering_down_flag = (inx & NODE_STATE_POWERING_DOWN);
	bool power_down_flag = (inx & NODE_STATE_POWER_DOWN);

	if (inx & NODE_STATE_INVALID_REG)
		return "INVAL";

	inx = (inx & NODE_STATE_BASE);

	if (maint_flag) {
		if (drain_flag ||
		    (inx == NODE_STATE_ALLOCATED) ||
		    (inx == NODE_STATE_DOWN) ||
		    (inx == NODE_STATE_MIXED))
			;
		else if (no_resp_flag)
			return "MAINT*";
		else
			return "MAINT";
	}
	if (reboot_flag || reboot_issued_flag) {
		if ((inx == NODE_STATE_ALLOCATED) || (inx == NODE_STATE_MIXED))
			;
		else if (reboot_issued_flag)
			return "BOOT^";
		else if (no_resp_flag)
			return "BOOT*";
		else
			return "BOOT";
	}
	if (drain_flag) {
		if (comp_flag
		    || (inx == NODE_STATE_ALLOCATED)
		    || (inx == NODE_STATE_MIXED)) {
			if (maint_flag)
				return "DRNG$";
			if (reboot_issued_flag)
				return "DRNG^";
			if (reboot_flag)
				return "DRNG@";
			if (power_up_flag)
				return "DRNG#";
			if (powering_down_flag)
				return "DRNG%";
			if (powered_down_flag)
				return "DRNG~";
			if (power_down_flag)
				return "DRNG!";
			if (no_resp_flag)
				return "DRNG*";
			return "DRNG";
		} else {
			if (maint_flag)
				return "DRAIN$";
			if (reboot_issued_flag)
				return "DRAIN^";
			if (reboot_flag)
				return "DRAIN@";
			if (power_up_flag)
				return "DRAIN#";
			if (powering_down_flag)
				return "DRAIN%";
			if (powered_down_flag)
				return "DRAIN~";
			if (power_down_flag)
				return "DRAIN!";
			if (no_resp_flag)
				return "DRAIN*";
			return "DRAIN";
		}
	}
	if (fail_flag) {
		if (comp_flag || (inx == NODE_STATE_ALLOCATED) ||
		    (inx == NODE_STATE_MIXED)) {
			if (no_resp_flag)
				return "FAILG*";
			return "FAILG";
		} else {
			if (no_resp_flag)
				return "FAIL*";
			return "FAIL";
		}
	}

	if (inx == NODE_STATE_REBOOT_ISSUED)
		return "BOOT^";
	if (inx == NODE_STATE_REBOOT_CANCEL)
		return "CANC_R";
	if (inx == NODE_STATE_CLOUD)
		return "CLOUD";
	if (inx == NODE_STATE_EXTERNAL)
		return "EXT";
	if (inx == NODE_STATE_POWER_DOWN)
		return "POW_DN";
	if (inx == NODE_STATE_POWER_UP)
		return "POW_UP";
	if (inx == NODE_STATE_POWERING_DOWN)
		return "POWRING_DN";
	if (inx == NODE_STATE_POWERED_DOWN)
		return "POWERED_DN";
	if (inx == NODE_STATE_POWERING_UP)
		return "POWERING_UP";
	if (inx == NODE_STATE_DOWN) {
		if (maint_flag)
			return "DOWN$";
		if (reboot_issued_flag)
			return "DOWN^";
		if (reboot_flag)
			return "DOWN@";
		if (power_up_flag)
			return "DOWN#";
		if (powering_down_flag)
			return "DOWN%";
		if (powered_down_flag)
			return "DOWN~";
		if (power_down_flag)
			return "DOWN!";
		if (no_resp_flag)
			return "DOWN*";
		return "DOWN";
	}

	if (inx == NODE_STATE_ALLOCATED) {
		if (maint_flag)
			return "ALLOC$";
		if (reboot_issued_flag)
			return "ALLOC^";
		if (reboot_flag)
			return "ALLOC@";
		if (power_up_flag)
			return "ALLOC#";
		if (powering_down_flag)
			return "ALLOC%";
		if (powered_down_flag)
			return "ALLOC~";
		if (power_down_flag)
			return "ALLOC!";
		if (no_resp_flag)
			return "ALLOC*";
		if (comp_flag)
			return "ALLOC+";
		return "ALLOC";
	}
	if (comp_flag) {
		if (maint_flag)
			return "COMP$";
		if (reboot_issued_flag)
			return "COMP^";
		if (reboot_flag)
			return "COMP@";
		if (power_up_flag)
			return "COMP#";
		if (powering_down_flag)
			return "COMP%";
		if (powered_down_flag)
			return "COMP~";
		if (power_down_flag)
			return "COMP!";
		if (no_resp_flag)
			return "COMP*";
		return "COMP";
	}
	if (inx == NODE_STATE_IDLE) {
		if (maint_flag)
			return "IDLE$";
		if (reboot_issued_flag)
			return "IDLE^";
		if (reboot_flag)
			return "IDLE@";
		if (power_up_flag)
			return "IDLE#";
		if (powering_down_flag)
			return "IDLE%";
		if (powered_down_flag)
			return "IDLE~";
		if (power_down_flag)
			return "IDLE!";
		if (no_resp_flag)
			return "IDLE*";
		if (res_flag)
			return "RESV";
		if (blocked_flag)
			return "BLOCK";
		if (planned_flag)
			return "PLND";
		return "IDLE";
	}
	if (inx == NODE_STATE_MIXED) {
		if (maint_flag)
			return "MIX$";
		if (reboot_issued_flag)
			return "MIX^";
		if (reboot_flag)
			return "MIX@";
		if (power_up_flag)
			return "MIX#";
		if (powering_down_flag)
			return "MIX%";
		if (powered_down_flag)
			return "MIX~";
		if (power_down_flag)
			return "MIX!";
		if (no_resp_flag)
			return "MIX*";
		if (planned_flag)
			return "MIX-";
		return "MIX";
	}
	if (inx == NODE_STATE_FUTURE) {
		if (maint_flag)
			return "FUTR$";
		if (reboot_issued_flag)
			return "FUTR^";
		if (reboot_flag)
			return "FUTR@";
		if (power_up_flag)
			return "FUTR#";
		if (powering_down_flag)
			return "FUTR%";
		if (powered_down_flag)
			return "FUTR~";
		if (power_down_flag)
			return "FUTR!";
		if (no_resp_flag)
			return "FUTR*";
		return "FUTR";
	}
	if (resume_flag)
		return "RESM";
	if (inx == NODE_STATE_UNKNOWN) {
		if (no_resp_flag)
			return "UNK*";
		return "UNK";
	}
	return "?";
}

extern void private_data_string(uint16_t private_data, char *str, int str_len)
{
	if (str_len > 0)
		str[0] = '\0';
	if (str_len < 69) {
		error("private_data_string: output buffer too small");
		return;
	}

	if (private_data & PRIVATE_DATA_ACCOUNTS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "accounts"); //9 len
	}
	if (private_data & PRIVATE_DATA_EVENTS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "events"); //7 len
	}
	if (private_data & PRIVATE_DATA_JOBS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "jobs"); //5 len
	}
	if (private_data & PRIVATE_DATA_NODES) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "nodes"); //6 len
	}
	if (private_data & PRIVATE_DATA_PARTITIONS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "partitions"); //11 len
	}
	if (private_data & PRIVATE_DATA_RESERVATIONS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "reservations"); //13 len
	}
	if (private_data & PRIVATE_DATA_USAGE) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "usage"); //6 len
	}
	if (private_data & PRIVATE_DATA_USERS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "users"); //6 len
	}

	// total len 69

	if (str[0] == '\0')
		strcat(str, "none");
}

extern void accounting_enforce_string(uint16_t enforce, char *str, int str_len)
{
	if (str_len > 0)
		str[0] = '\0';
	if (str_len < 50) {
		error("enforce: output buffer too small");
		return;
	}

	if (enforce & ACCOUNTING_ENFORCE_ASSOCS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "associations"); //12 len
	}
	if (enforce & ACCOUNTING_ENFORCE_LIMITS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "limits"); //7 len
	}
	if (enforce & ACCOUNTING_ENFORCE_NO_JOBS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "nojobs"); //7 len
	}
	if (enforce & ACCOUNTING_ENFORCE_NO_STEPS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "nosteps"); //8 len
	}
	if (enforce & ACCOUNTING_ENFORCE_QOS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "qos"); //4 len
	}
	if (enforce & ACCOUNTING_ENFORCE_SAFE) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "safe"); //5 len
	}
	if (enforce & ACCOUNTING_ENFORCE_WCKEYS) {
		if (str[0])
			strcat(str, ",");
		strcat(str, "wckeys"); //7 len
	}
	// total len 50

	if (str[0] == '\0')
		strcat(str, "none");
}

extern void slurm_free_resource_allocation_response_msg_members (
	resource_allocation_response_msg_t * msg)
{
	if (msg) {
		xfree(msg->account);
		xfree(msg->batch_host);
		xfree(msg->cpus_per_node);
		xfree(msg->cpu_count_reps);
		env_array_free(msg->environment);
		msg->environment = NULL;
		xfree(msg->group_name);
		xfree(msg->job_submit_user_msg);
		xfree(msg->node_list);
		xfree(msg->partition);
		xfree(msg->qos);
		xfree(msg->resv_name);
		xfree(msg->tres_per_node);
		xfree(msg->tres_per_task);
		slurmdb_destroy_cluster_rec(msg->working_cluster_rec);
		xfree(msg->user_name);
	}
}

/*
 * slurm_free_resource_allocation_response_msg - free slurm resource
 *	allocation response message
 * IN msg - pointer to allocation response message
 * NOTE: buffer is loaded by slurm_allocate_resources
 */
extern void slurm_free_resource_allocation_response_msg (
	resource_allocation_response_msg_t * msg)
{
	slurm_free_resource_allocation_response_msg_members(msg);
	xfree(msg);
}

/*
 * slurm_free_sbcast_cred_msg - free slurm resource allocation response
 *	message including an sbcast credential
 * IN msg - pointer to response message from slurm_sbcast_lookup()
 * NOTE: buffer is loaded by slurm_allocate_resources
 */
extern void slurm_free_sbcast_cred_msg(job_sbcast_cred_msg_t * msg)
{
	if (msg) {
		xfree(msg->node_list);
		delete_sbcast_cred(msg->sbcast_cred);
		xfree(msg);
	}
}

/*
 * slurm_free_job_step_create_response_msg - free slurm
 *	job step create response message
 * IN msg - pointer to job step create response message
 * NOTE: buffer is loaded by slurm_job_step_create
 */
extern void slurm_free_job_step_create_response_msg(
	job_step_create_response_msg_t * msg)
{
	if (msg) {
		xfree(msg->resv_ports);
		xfree(msg->stepmgr);
		slurm_step_layout_destroy(msg->step_layout);
		slurm_cred_destroy(msg->cred);
		if (msg->switch_step)
			switch_g_stepinfo_free(msg->switch_step);

		xfree(msg);
	}

}


/*
 * slurm_free_submit_response_response_msg - free slurm
 *	job submit response message
 * IN msg - pointer to job submit response message
 * NOTE: buffer is loaded by slurm_submit_batch_job
 */
extern void slurm_free_submit_response_response_msg(submit_response_msg_t * msg)
{
	if (msg) {
		xfree(msg->job_submit_user_msg);
		xfree(msg);
	}
}

/*
 * slurm_free_conf - free slurm control information response message
 * IN msg - pointer to slurm control information response message
 * NOTE: buffer is loaded by slurm_load_jobs
 */
extern void slurm_free_conf(slurm_conf_t *config_ptr)
{
	if (config_ptr) {
		free_slurm_conf(config_ptr, 0);
		xfree(config_ptr);
	}
}

/*
 * slurm_free_slurmd_status - free slurmd state information
 * IN msg - pointer to slurmd state information
 * NOTE: buffer is loaded by slurm_load_slurmd_status
 */
extern void slurm_free_slurmd_status(slurmd_status_t* slurmd_status_ptr)
{
	if (slurmd_status_ptr) {
		xfree(slurmd_status_ptr->hostname);
		xfree(slurmd_status_ptr->slurmd_logfile);
		xfree(slurmd_status_ptr->step_list);
		xfree(slurmd_status_ptr->version);
		xfree(slurmd_status_ptr);
	}
}

/*
 * slurm_free_job_info - free the job information response message
 * IN msg - pointer to job information response message
 * NOTE: buffer is loaded by slurm_load_job.
 */
extern void slurm_free_job_info_msg(job_info_msg_t * job_buffer_ptr)
{
	if (job_buffer_ptr) {
		if (job_buffer_ptr->job_array) {
			_free_all_job_info(job_buffer_ptr);
			xfree(job_buffer_ptr->job_array);
		}
		xfree(job_buffer_ptr);
	}
}

static void _free_all_job_info(job_info_msg_t *msg)
{
	int i;

	if ((msg == NULL) ||
	    (msg->job_array == NULL))
		return;

	for (i = 0; i < msg->record_count; i++)
		slurm_free_job_info_members (&msg->job_array[i]);
}

/*
 * slurm_free_job_step_info_response_msg - free the job step
 *	information response message
 * IN msg - pointer to job step information response message
 * NOTE: buffer is loaded by slurm_get_job_steps.
 */
extern void slurm_free_job_step_info_response_msg(job_step_info_response_msg_t *
					   msg)
{
	if (msg != NULL) {
		if (msg->job_steps != NULL) {
			_free_all_step_info(msg);
			xfree(msg->job_steps);
		}
		xfree(msg);
	}
}

static void _free_all_step_info (job_step_info_response_msg_t *msg)
{
	int i;

	if ((msg == NULL) ||
	    (msg->job_steps == NULL))
		return;

	for (i = 0; i < msg->job_step_count; i++)
		slurm_free_job_step_info_members (&msg->job_steps[i]);
}

extern void slurm_free_job_step_info_members(job_step_info_t * msg)
{
	if (msg) {
		xfree(msg->cluster);
		xfree(msg->container);
		xfree(msg->container_id);
		xfree(msg->container_type);
		xfree(msg->job_name);
		xfree(msg->cwd);
		xfree(msg->tres_per_node);
		xfree(msg->mem_per_tres);
		xfree(msg->name);
		xfree(msg->network);
		xfree(msg->nodes);
		xfree(msg->node_inx);
		xfree(msg->partition);
		xfree(msg->resv_ports);
		xfree(msg->srun_host);
		xfree(msg->std_err);
		xfree(msg->std_in);
		xfree(msg->std_out);
		xfree(msg->tres_bind);
		xfree(msg->tres_fmt_alloc_str);
		xfree(msg->tres_freq);
		xfree(msg->tres_per_step);
		xfree(msg->tres_per_node);
		xfree(msg->tres_per_socket);
		xfree(msg->tres_per_task);
	}
}

extern void slurm_init_node_info_t(node_info_t *msg, bool clear)
{
	xassert(msg);

	if (clear)
		memset(msg, 0, sizeof(node_info_t));

	msg->next_state = NO_VAL;
}

/*
 * slurm_free_node_info - free the node information response message
 * IN msg - pointer to node information response message
 * NOTE: buffer is loaded by slurm_load_node.
 */
extern void slurm_free_node_info_msg(node_info_msg_t * msg)
{
	if (msg) {
		if (msg->node_array) {
			_free_all_node_info(msg);
			xfree(msg->node_array);
		}
		xfree(msg);
	}
}

static void _free_all_node_info(node_info_msg_t *msg)
{
	int i;

	if ((msg == NULL) || (msg->node_array == NULL))
		return;

	for (i = 0; i < msg->record_count; i++)
		slurm_free_node_info_members(&msg->node_array[i]);
}

extern void slurm_free_node_info_members(node_info_t * node)
{
	if (node) {
		xfree(node->alloc_tres_fmt_str);
		xfree(node->arch);
		xfree(node->bcast_address);
		xfree(node->cluster_name);
		xfree(node->comment);
		xfree(node->cpu_spec_list);
		acct_gather_energy_destroy(node->energy);
		xfree(node->extra);
		xfree(node->features);
		xfree(node->features_act);
		xfree(node->gpu_spec);
		xfree(node->gres);
		xfree(node->gres_drain);
		xfree(node->gres_used);
		xfree(node->instance_id);
		xfree(node->instance_type);
		xfree(node->mcs_label);
		xfree(node->name);
		xfree(node->node_addr);
		xfree(node->node_hostname);
		xfree(node->os);
		xfree(node->partitions);
		xfree(node->reason);
		xfree(node->resv_name);
		xfree(node->topology_str);
		xfree(node->tres_fmt_str);
		xfree(node->version);
		/* Do NOT free node, it is an element of an array */
	}
}


/*
 * slurm_free_partition_info_msg - free the partition information
 *	response message
 * IN msg - pointer to partition information response message
 * NOTE: buffer is loaded by slurm_load_partitions
 */
extern void slurm_free_partition_info_msg(partition_info_msg_t * msg)
{
	if (msg) {
		if (msg->partition_array) {
			_free_all_partitions(msg);
			xfree(msg->partition_array);
		}
		xfree(msg);
	}
}

static void  _free_all_partitions(partition_info_msg_t *msg)
{
	int i;

	if ((msg == NULL) ||
	    (msg->partition_array == NULL))
		return;

	for (i = 0; i < msg->record_count; i++)
		slurm_free_partition_info_members(
			&msg->partition_array[i]);

}

extern void slurm_free_partition_info_members(partition_info_t * part)
{
	if (part) {
		xfree(part->allow_alloc_nodes);
		xfree(part->allow_accounts);
		xfree(part->allow_groups);
		xfree(part->allow_qos);
		xfree(part->alternate);
		xfree(part->billing_weights_str);
		xfree(part->cluster_name);
		xfree(part->deny_accounts);
		xfree(part->deny_qos);
		FREE_NULL_LIST(part->job_defaults_list);
		xfree(part->job_defaults_str);
		xfree(part->name);
		xfree(part->nodes);
		xfree(part->nodesets);
		xfree(part->node_inx);
		xfree(part->qos_char);
		xfree(part->topology_name);
		xfree(part->tres_fmt_str);
	}
}

/*
 * slurm_free_reserve_info_msg - free the reservation information
 *	response message
 * IN msg - pointer to reservation information response message
 * NOTE: buffer is loaded by slurm_load_reservation
 */
extern void slurm_free_reservation_info_msg(reserve_info_msg_t * msg)
{
	if (msg) {
		if (msg->reservation_array) {
			_free_all_reservations(msg);
			xfree(msg->reservation_array);
		}
		xfree(msg);
	}
}

static void  _free_all_reservations(reserve_info_msg_t *msg)
{
	int i;

	if ((msg == NULL) ||
	    (msg->reservation_array == NULL))
		return;

	for (i = 0; i < msg->record_count; i++) {
		slurm_free_reserve_info_members(
			&msg->reservation_array[i]);
	}

}

extern void slurm_free_reserve_info_members(reserve_info_t * resv)
{
	int i;
	if (resv) {
		xfree(resv->accounts);
		xfree(resv->allowed_parts);
		xfree(resv->burst_buffer);
		xfree(resv->comment);
		if (resv->core_spec) {
			for (i = 0; i < resv->core_spec_cnt; i++) {
				xfree(resv->core_spec[i].node_name);
				xfree(resv->core_spec[i].core_id);
			}
			xfree(resv->core_spec);
		}
		xfree(resv->features);
		xfree(resv->groups);
		xfree(resv->licenses);
		xfree(resv->name);
		xfree(resv->node_inx);
		xfree(resv->node_list);
		xfree(resv->partition);
		xfree(resv->qos);
		xfree(resv->tres_str);
		xfree(resv->users);
	}
}

/*
 * slurm_free_topo_info_msg - free the switch topology configuration
 *	information response message
 * IN msg - pointer to switch topology configuration response message
 * NOTE: buffer is loaded by slurm_load_topo.
 */
extern void slurm_free_topo_info_msg(topo_info_response_msg_t *msg)
{
	if (msg) {
		topology_g_topoinfo_free(msg->topo_info);
		xfree(msg);
	}
}

extern void slurm_free_topo_request_msg(topo_info_request_msg_t *msg)
{
	if (msg) {
		xfree(msg->name);
		xfree(msg);
	}
}

extern void slurm_free_topo_config_msg(topo_config_response_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->config);
	xfree(msg);
}

/*
 * slurm_free_burst_buffer_info_msg - free buffer returned by
 *	slurm_load_burst_buffer
 * IN burst_buffer_info_msg_ptr - pointer to burst_buffer_info_msg_t
 * RET 0 or a slurm error code
 */
extern void slurm_free_burst_buffer_info_msg(burst_buffer_info_msg_t *msg)
{
	int i, j;
	burst_buffer_info_t *bb_info_ptr;
	burst_buffer_resv_t *bb_resv_ptr;
	burst_buffer_pool_t *bb_pool_ptr;

	if (!msg)
		return;

	for (i = 0, bb_info_ptr = msg->burst_buffer_array;
	     ((i < msg->record_count) && bb_info_ptr); i++, bb_info_ptr++) {
		xfree(bb_info_ptr->allow_users);
		xfree(bb_info_ptr->default_pool);
		xfree(bb_info_ptr->create_buffer);
		xfree(bb_info_ptr->deny_users);
		xfree(bb_info_ptr->destroy_buffer);
		xfree(bb_info_ptr->get_sys_state);
		xfree(bb_info_ptr->get_sys_status);
		xfree(bb_info_ptr->name);
		for (j = 0, bb_pool_ptr = bb_info_ptr->pool_ptr;
		     ((j < bb_info_ptr->pool_cnt) && bb_pool_ptr);
		     j++, bb_pool_ptr++) {
			xfree(bb_pool_ptr->name);
		}
		xfree(bb_info_ptr->pool_ptr);
		xfree(bb_info_ptr->start_stage_in);
		xfree(bb_info_ptr->start_stage_out);
		xfree(bb_info_ptr->stop_stage_in);
		xfree(bb_info_ptr->stop_stage_out);
		for (j = 0, bb_resv_ptr = bb_info_ptr->burst_buffer_resv_ptr;
		     ((j < bb_info_ptr->buffer_count) && bb_resv_ptr);
		     j++, bb_resv_ptr++) {
			xfree(bb_resv_ptr->account);
			xfree(bb_resv_ptr->name);
			xfree(bb_resv_ptr->partition);
			xfree(bb_resv_ptr->pool);
			xfree(bb_resv_ptr->qos);
		}
		xfree(bb_info_ptr->burst_buffer_resv_ptr);
		xfree(bb_info_ptr->burst_buffer_use_ptr);
	}
	xfree(msg->burst_buffer_array);

	xfree(msg);
}

extern void slurm_free_file_bcast_msg(file_bcast_msg_t *msg)
{
	if (msg) {
		xfree(msg->block);
		xfree(msg->fname);
		xfree(msg->exe_fname);
		xfree(msg->user_name);
		delete_sbcast_cred(msg->cred);
		xfree(msg);
	}
}

extern void slurm_free_step_complete_msg(step_complete_msg_t *msg)
{
	if (msg) {
		jobacctinfo_destroy(msg->jobacct);
		xfree(msg);
	}
}

extern void slurm_free_job_step_stat(job_step_stat_t *msg)
{
	if (msg) {
		jobacctinfo_destroy(msg->jobacct);
		slurm_free_job_step_pids(msg->step_pids);
		xfree(msg);
	}
}

extern void slurm_free_job_step_pids(void *object)
{
	job_step_pids_t *msg = (job_step_pids_t *)object;
	if (msg) {
		xfree(msg->node_name);
		xfree(msg->pid);
		xfree(msg);
	}
}

extern void slurm_free_network_callerid_msg(network_callerid_msg_t *mesg)
{
	xfree(mesg);
}

extern void slurm_free_network_callerid_resp(network_callerid_resp_t *resp)
{
	if (resp) {
		xfree(resp->node_name);
		xfree(resp);
	}
}

extern void slurm_free_trigger_msg(trigger_info_msg_t *msg)
{
	int i;

	if (msg->trigger_array) {
		for (i = 0; i < msg->record_count; i++) {
			xfree(msg->trigger_array[i].res_id);
			xfree(msg->trigger_array[i].program);
		}
		xfree(msg->trigger_array);
	}
	xfree(msg);
}

extern void slurm_free_set_debug_flags_msg(set_debug_flags_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_set_debug_level_msg(set_debug_level_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_destroy_assoc_shares_object(void *object)
{
	assoc_shares_object_t *obj_ptr =
		(assoc_shares_object_t *)object;

	if (obj_ptr) {
		xfree(obj_ptr->cluster);
		xfree(obj_ptr->name);
		xfree(obj_ptr->parent);
		xfree(obj_ptr->partition);
		xfree(obj_ptr->tres_run_secs);
		xfree(obj_ptr->tres_grp_mins);
		xfree(obj_ptr->usage_tres_raw);
		xfree(obj_ptr);
	}
}

extern void slurm_free_shares_request_msg(shares_request_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->acct_list);
		FREE_NULL_LIST(msg->user_list);
		xfree(msg);
	}
}

extern void slurm_free_shares_response_msg(shares_response_msg_t *msg)
{
	if (msg) {
		int i;
		if (msg->tres_names) {
			for (i=0; i<msg->tres_cnt; i++)
				xfree(msg->tres_names[i]);
			xfree(msg->tres_names);
		}
		FREE_NULL_LIST(msg->assoc_shares_list);
		xfree(msg);
	}
}


inline void slurm_free_stats_info_request_msg(stats_info_request_msg_t *msg)
{
	xfree(msg);
}


extern void slurm_destroy_priority_factors(void *object)
{
	priority_factors_t *obj_ptr = object;

	if (!obj_ptr)
		return;

	xfree(obj_ptr->priority_tres);
	if (obj_ptr->tres_cnt && obj_ptr->tres_names) {
		for (int i = 0; i < obj_ptr->tres_cnt; i++)
			xfree(obj_ptr->tres_names[i]);
	}
	xfree(obj_ptr->tres_names);
	xfree(obj_ptr->tres_weights);
	xfree(obj_ptr);
}

extern void slurm_destroy_priority_factors_object(void *object)
{
	priority_factors_object_t *obj_ptr =
		(priority_factors_object_t *)object;

	if (!obj_ptr)
		return;

	xfree(obj_ptr->account);
	xfree(obj_ptr->cluster_name);
	xfree(obj_ptr->partition);
	slurm_destroy_priority_factors(obj_ptr->prio_factors);
	xfree(obj_ptr->qos);
	xfree(obj_ptr);
}

extern void slurm_copy_priority_factors(priority_factors_t *dest,
					priority_factors_t *src)
{
	int size;

	if (!dest || !src)
		return;

	size = sizeof(double) * src->tres_cnt;

	memcpy(dest, src, sizeof(*dest));

	if (src->priority_tres) {
		dest->priority_tres = xmalloc(size);
		memcpy(dest->priority_tres, src->priority_tres, size);
	}

	if (src->tres_names) {
		int char_size = sizeof(char *) * src->tres_cnt;
		dest->tres_names = xmalloc(char_size);
		memcpy(dest->tres_names, src->tres_names, char_size);
	}

	if (src->tres_weights) {
		dest->tres_weights = xmalloc(size);
		memcpy(dest->tres_weights, src->tres_weights, size);
	}
}

extern void slurm_free_priority_factors_response_msg(
	priority_factors_response_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->priority_factors_list);
		xfree(msg);
	}
}


extern void slurm_free_accounting_update_msg(accounting_update_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->update_list);
		xfree(msg);
	}
}

extern void slurm_free_set_fs_dampening_factor_msg(
	set_fs_dampening_factor_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_control_status_msg(control_status_msg_t *msg)
{
	xfree(msg);
}

extern void slurm_free_bb_status_req_msg(bb_status_req_msg_t *msg)
{
	int i;

	if (msg) {
		if (msg->argv) {
			for (i = 0; i < msg->argc; i++)
				xfree(msg->argv[i]);
			xfree(msg->argv);
		}
		xfree(msg);
	}
}

extern void slurm_free_bb_status_resp_msg(bb_status_resp_msg_t *msg)
{
	if (msg) {
		xfree(msg->status_resp);
		xfree(msg);
	}
}

extern void slurm_free_crontab_request_msg(crontab_request_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg);
}

extern void slurm_free_crontab_response_msg(crontab_response_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->crontab);
	xfree(msg->disabled_lines);
	xfree(msg);
}

extern void slurm_free_crontab_update_request_msg(
	crontab_update_request_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->crontab);
	FREE_NULL_LIST(msg->jobs);
	xfree(msg);
}

extern void slurm_free_crontab_update_response_msg(
	crontab_update_response_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->err_msg);
	xfree(msg->job_submit_user_msg);
	xfree(msg->failed_lines);
	xfree(msg->jobids);
	xfree(msg);
}

extern void slurm_free_tls_cert_request_msg(tls_cert_request_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->csr);
	xfree(msg->node_name);
	xfree(msg->token);
	xfree(msg);
}

extern void slurm_free_tls_cert_response_msg_members(tls_cert_response_msg_t
							     *msg)
{
	if (!msg)
		return;

	xfree(msg->signed_cert);
}

extern void slurm_free_tls_cert_response_msg(tls_cert_response_msg_t *msg)
{
	if (!msg)
		return;

	slurm_free_tls_cert_response_msg_members(msg);
	xfree(msg);
}

extern void slurm_free_suspend_exc_update_msg(suspend_exc_update_msg_t *msg)
{
	if (!msg)
		return;

	xfree(msg->update_str);
	xfree(msg);
}

extern void slurm_copy_node_alias_addrs_members(slurm_node_alias_addrs_t *dest,
						slurm_node_alias_addrs_t *src)
{
	xassert(dest);
	xassert(src);

	dest->expiration = src->expiration;
	dest->node_cnt = src->node_cnt;

	if (dest->net_cred)
		dest->net_cred[0] = '\0';
	if (src->net_cred)
		xstrcat(dest->net_cred, src->net_cred);

	xrecalloc(dest->node_addrs, src->node_cnt, sizeof(slurm_addr_t));
	memcpy(dest->node_addrs, src->node_addrs,
	       (sizeof(slurm_addr_t) * src->node_cnt));

	if (dest->node_list)
		dest->node_list[0] = '\0';
	if (src->node_list)
		xstrcat(dest->node_list, src->node_list);
}

extern void slurm_free_node_alias_addrs_members(slurm_node_alias_addrs_t *msg)
{
	if (!msg)
		return;

	xfree(msg->net_cred);
	xfree(msg->node_addrs);
	xfree(msg->node_list);
}

extern void slurm_free_node_alias_addrs(slurm_node_alias_addrs_t *msg)
{
	if (!msg)
		return;

	slurm_free_node_alias_addrs_members(msg);
	xfree(msg);
}

extern void slurm_free_msg_data(slurm_msg_type_t type, void *data)
{
	if (!data)
		return;

	/* this message was never loaded */
	if ((uint16_t)type == NO_VAL16)
		return;

	switch (type) {
	case RESPONSE_LAUNCH_TASKS:
		slurm_free_launch_tasks_response_msg(data);
		break;
	case MESSAGE_TASK_EXIT:
		slurm_free_task_exit_msg(data);
		break;
	case REQUEST_BUILD_INFO:
		slurm_free_last_update_msg(data);
		break;
	case RESPONSE_BUILD_INFO:
		slurm_free_conf(data);
		break;
	case REQUEST_JOB_INFO:
		slurm_free_job_info_request_msg(data);
		break;
	case REQUEST_JOB_STATE:
		slurm_free_job_state_request_msg(data);
		break;
	case RESPONSE_JOB_STATE:
		slurm_free_job_state_response_msg(data);
		break;
	case REQUEST_NODE_INFO:
		slurm_free_node_info_request_msg(data);
		break;
	case REQUEST_NODE_INFO_SINGLE:
		slurm_free_node_info_single_msg(data);
		break;
	case REQUEST_PARTITION_INFO:
		slurm_free_part_info_request_msg(data);
		break;
	case MESSAGE_EPILOG_COMPLETE:
		slurm_free_epilog_complete_msg(data);
		break;
	case RESPONSE_JOB_STEP_INFO:
		slurm_free_job_step_info_response_msg(data);
		break;
	case REQUEST_KILL_JOB:
	case REQUEST_CANCEL_JOB_STEP:
	case SRUN_STEP_SIGNAL:
		slurm_free_job_step_kill_msg(data);
		break;
	case REQUEST_COMPLETE_JOB_ALLOCATION:
		slurm_free_complete_job_allocation_msg(data);
		break;
	case REQUEST_COMPLETE_PROLOG:
		slurm_free_prolog_complete_msg(data);
		break;
	case REQUEST_COMPLETE_BATCH_SCRIPT:
		slurm_free_complete_batch_script_msg(data);
		break;
	case REQUEST_JOB_STEP_CREATE:
		slurm_free_job_step_create_request_msg(data);
		break;
	case REQUEST_JOB_STEP_INFO:
		slurm_free_job_step_info_request_msg(data);
		break;
	case RESPONSE_JOB_STEP_PIDS:
		slurm_free_job_step_pids(data);
		break;
	case REQUEST_LAUNCH_PROLOG:
		slurm_free_prolog_launch_msg(data);
		break;
	case REQUEST_RESOURCE_ALLOCATION:
	case REQUEST_JOB_WILL_RUN:
	case REQUEST_SUBMIT_BATCH_JOB:
	case REQUEST_UPDATE_JOB:
		slurm_free_job_desc_msg(data);
		break;
	case REQUEST_SIB_JOB_LOCK:
	case REQUEST_SIB_JOB_UNLOCK:
	case REQUEST_SIB_MSG:
		slurm_free_sib_msg(data);
		break;
	case REQUEST_SEND_DEP:
		slurm_free_dep_msg(data);
		break;
	case REQUEST_UPDATE_ORIGIN_DEP:
		slurm_free_dep_update_origin_msg(data);
		break;
	case RESPONSE_JOB_WILL_RUN:
		slurm_free_will_run_response_msg(data);
		break;
	case RESPONSE_SUBMIT_BATCH_JOB:
		slurm_free_submit_response_response_msg(data);
		break;
	case RESPONSE_ACCT_GATHER_UPDATE:
	case RESPONSE_ACCT_GATHER_ENERGY:
		slurm_free_acct_gather_node_resp_msg(data);
		break;
	case RESPONSE_NODE_REGISTRATION:
		slurm_free_node_reg_resp_msg(data);
		break;
	case REQUEST_NODE_REGISTRATION_STATUS:
	case MESSAGE_NODE_REGISTRATION_STATUS:
		slurm_free_node_registration_status_msg(data);
		break;
	case REQUEST_JOB_ALLOCATION_INFO:
	case REQUEST_JOB_END_TIME:
	case REQUEST_HET_JOB_ALLOC_INFO:
		slurm_free_job_alloc_info_msg(data);
		break;
	case REQUEST_JOB_SBCAST_CRED:
		slurm_destroy_selected_step(data);
		break;
	case REQUEST_SBCAST_CRED_NO_JOB:
		slurm_free_sbcast_cred_req_msg(data);
		break;
	case REQUEST_SHUTDOWN:
		slurm_free_shutdown_msg(data);
		break;
	case REQUEST_CREATE_NODE:
	case REQUEST_UPDATE_NODE:
	case REQUEST_DELETE_NODE:
		slurm_free_update_node_msg(data);
		break;
	case REQUEST_RUN_POWER_ACTION:
		slurm_free_run_power_action_msg(data);
		break;
	case REQUEST_CREATE_PARTITION:
	case REQUEST_UPDATE_PARTITION:
		slurm_free_update_part_msg(data);
		break;
	case REQUEST_DELETE_PARTITION:
		slurm_free_delete_part_msg(data);
		break;
	case REQUEST_CREATE_RESERVATION:
	case REQUEST_UPDATE_RESERVATION:
		slurm_free_resv_desc_msg(data);
		break;
	case REQUEST_DELETE_RESERVATION:
	case RESPONSE_CREATE_RESERVATION:
		slurm_free_resv_name_msg(data);
		break;
	case REQUEST_RESERVATION_INFO:
		slurm_free_resv_info_request_msg(data);
		break;
	case RESPONSE_RESERVATION_INFO:
		slurm_free_reservation_info_msg(data);
		break;
	case REQUEST_SUSPEND:
	case SRUN_REQUEST_SUSPEND:
		slurm_free_suspend_msg(data);
		break;
	case REQUEST_SUSPEND_INT:
		slurm_free_suspend_int_msg(data);
		break;
	case REQUEST_TOP_JOB:
		slurm_free_top_job_msg(data);
		break;
	case REQUEST_AUTH_TOKEN:
		slurm_free_token_request_msg(data);
		break;
	case RESPONSE_AUTH_TOKEN:
		slurm_free_token_response_msg(data);
		break;
	case REQUEST_KILL_JOBS:
		slurm_free_kill_jobs_msg(data);
		break;
	case RESPONSE_KILL_JOBS:
		slurm_free_kill_jobs_response_msg(data);
		break;
	case REQUEST_JOB_REQUEUE:
		slurm_free_requeue_msg(data);
		break;
	case REQUEST_STEPMGR_RESIZE_KILL_STEPS:
		slurm_free_resize_msg(data);
		break;
	case RESPONSE_RESOURCE_LAYOUT:
		slurm_free_resource_layout_msg(data);
		break;
	case REQUEST_BATCH_SCRIPT:
	case REQUEST_JOB_READY:
	case REQUEST_JOB_INFO_SINGLE:
	case REQUEST_RESOURCE_LAYOUT:
		slurm_free_job_id_msg(data);
		break;
	case RESPONSE_BATCH_SCRIPT:
		slurm_free_batch_script_msg(data);
		break;
	case RESPONSE_PARTITION_INFO:
		slurm_free_partition_info_msg(data);
		break;
	case RESPONSE_NODE_INFO:
		slurm_free_node_info_msg(data);
		break;
	case REQUEST_JOB_USER_INFO:
		slurm_free_job_user_id_msg(data);
		break;
	case REQUEST_SHARE_INFO:
		slurm_free_shares_request_msg(data);
		break;
	case RESPONSE_SHARE_INFO:
		slurm_free_shares_response_msg(data);
		break;
	case REQUEST_PRIORITY_FACTORS:
		break;
	case RESPONSE_PRIORITY_FACTORS:
		slurm_free_priority_factors_response_msg(data);
		break;
	case REQUEST_STEP_COMPLETE:
		slurm_free_step_complete_msg(data);
		break;
	case REQUEST_JOB_STEP_STAT:
	case REQUEST_JOB_STEP_PIDS:
	case REQUEST_STEP_LAYOUT:
		slurm_free_step_id(data);
		break;
	case RESPONSE_STEP_LAYOUT:
		slurm_job_step_layout_free(data);
		break;
	case RESPONSE_JOB_STEP_STAT:
		slurm_free_job_step_stat(data);
		break;
	case REQUEST_BATCH_JOB_LAUNCH:
		slurm_free_job_launch_msg(data);
		break;
	case REQUEST_LAUNCH_TASKS:
		slurm_free_launch_tasks_request_msg(data);
		break;
	case REQUEST_SIGNAL_TASKS:
	case REQUEST_TERMINATE_TASKS:
		slurm_free_signal_tasks_msg(data);
		break;
	case REQUEST_KILL_PREEMPTED:
	case REQUEST_KILL_TIMELIMIT:
		slurm_free_timelimit_msg(data);
		break;
	case REQUEST_JOB_MEM_USAGE:
		slurm_free_job_mem_usage_msg(data);
		break;
	case RESPONSE_JOB_MEM_USAGE:
		slurm_free_job_mem_usage_resp_msg(data);
		break;
	case REQUEST_UPDATE_JOB_MEM:
		slurm_free_update_job_mem_msg(data);
		break;
	case RESPONSE_UPDATE_JOB_MEM:
		slurm_free_response_update_job_mem_msg(data);
		break;
	case REQUEST_REATTACH_TASKS:
		slurm_free_reattach_tasks_request_msg(data);
		break;
	case RESPONSE_REATTACH_TASKS:
		slurm_free_reattach_tasks_response_msg(data);
		break;
	case REQUEST_ABORT_JOB:
	case REQUEST_TERMINATE_JOB:
		slurm_free_kill_job_msg(data);
		break;
	case REQUEST_JOB_ID:
		slurm_free_job_id_request_msg(data);
		break;
	case RESPONSE_JOB_ID:
		slurm_free_job_id_response_msg(data);
		break;
	case REQUEST_CONFIG:
		slurm_free_config_request_msg(data);
		break;
	case REQUEST_RECONFIGURE_SACKD:
	case REQUEST_RECONFIGURE_WITH_CONFIG:
	case RESPONSE_CONFIG:
		slurm_free_config_response_msg(data);
		break;
	case REQUEST_FILE_BCAST:
		slurm_free_file_bcast_msg(data);
		break;
	case RESPONSE_CONTAINER_PTY:
	case RESPONSE_CONTAINER_KILL:
	case RESPONSE_CONTAINER_DELETE:
	case RESPONSE_CONTAINER_EXEC:
	case RESPONSE_PROLOG_EXECUTING:
	case RESPONSE_JOB_READY:
		slurm_free_return_code_msg(data);
		break;
	case RESPONSE_SLURM_RC_MSG:
		slurm_free_return_code2_msg(data);
		break;
	case RESPONSE_SLURM_REROUTE_MSG:
		slurm_free_reroute_msg(data);
		break;
	case RESPONSE_JOB_STEP_CREATE:
		slurm_free_job_step_create_response_msg(data);
		break;
	case RESPONSE_SLURM_RC:
		slurm_free_return_code_msg(data);
		break;
	case REQUEST_SET_DEBUG_FLAGS:
		slurm_free_set_debug_flags_msg(data);
		break;
	case REQUEST_SET_DEBUG_LEVEL:
	case REQUEST_SET_SCHEDLOG_LEVEL:
		slurm_free_set_debug_level_msg(data);
		break;
	case REQUEST_CONTAINER_PTY:
	case REQUEST_CONTAINER_START:
	case REQUEST_CONTAINER_STATE:
	case REQUEST_PING:
	case REQUEST_RECONFIGURE:
	case REQUEST_CONTROL:
	case REQUEST_CONTROL_STATUS:
	case REQUEST_TAKEOVER:
	case RESPONSE_FORWARD_FAILED:
	case REQUEST_DAEMON_STATUS:
	case REQUEST_HEALTH_CHECK:
	case REQUEST_ACCT_GATHER_UPDATE:
	case ACCOUNTING_FIRST_REG:
	case REQUEST_BURST_BUFFER_INFO:
	case ACCOUNTING_REGISTER_CTLD:
	case REQUEST_FED_INFO:
	case SRUN_STEPS_DRAINED:
		/* No body to free */
		break;
	case RESPONSE_FED_INFO:
		slurmdb_destroy_federation_rec(data);
		break;
	case REQUEST_PERSIST_INIT:
		slurm_persist_free_init_req_msg(data);
		break;
	case PERSIST_RC:
		slurm_persist_free_rc_msg(data);
		break;
	case REQUEST_REBOOT_NODES:
		slurm_free_reboot_msg(data);
		break;
	case ACCOUNTING_UPDATE_MSG:
		slurm_free_accounting_update_msg(data);
		break;
	case RESPONSE_TOPO_INFO:
		slurm_free_topo_info_msg(data);
		break;
	case REQUEST_TOPO_INFO:
		slurm_free_topo_request_msg(data);
		break;
	case RESPONSE_JOB_SBCAST_CRED:
		slurm_free_sbcast_cred_msg(data);
		break;
	case REQUEST_UPDATE_JOB_STEP:
		slurm_free_update_step_msg(data);
		break;
	case RESPONSE_PING_SLURMD:
		slurm_free_ping_slurmd_resp(data);
		break;
	case RESPONSE_LICENSE_INFO:
		slurm_free_license_info_msg(data);
		break;
	case RESPONSE_JOB_ARRAY_ERRORS:
		slurm_free_job_array_resp(data);
		break;
	case RESPONSE_BURST_BUFFER_INFO:
		slurm_free_burst_buffer_info_msg(data);
		break;
	case REQUEST_TRIGGER_GET:
	case RESPONSE_TRIGGER_GET:
	case REQUEST_TRIGGER_SET:
	case REQUEST_TRIGGER_CLEAR:
	case REQUEST_TRIGGER_PULL:
		slurm_free_trigger_msg(data);
		break;
	case RESPONSE_SLURMD_STATUS:
		slurm_free_slurmd_status(data);
		break;
	case REQUEST_JOB_NOTIFY:
		slurm_free_job_notify_msg(data);
		break;
	case REQUEST_STATS_INFO:
		slurm_free_stats_info_request_msg(data);
		break;
	case RESPONSE_STATS_INFO:
		slurm_free_stats_response_msg(data);
		break;
	case REQUEST_LICENSE_INFO:
		slurm_free_license_info_request_msg(data);
		break;
	case REQUEST_ACCT_GATHER_ENERGY:
		slurm_free_acct_gather_energy_req_msg(data);
		break;
	case REQUEST_FORWARD_DATA:
		slurm_free_forward_data_msg(data);
		break;
	case REQUEST_NETWORK_CALLERID:
		slurm_free_network_callerid_msg(data);
		break;
	case RESPONSE_NETWORK_CALLERID:
		slurm_free_network_callerid_resp(data);
		break;
	case SRUN_JOB_COMPLETE:
		slurm_free_srun_job_complete_msg(data);
		break;
	case REQUEST_STEPS_DRAINED_SUBSCRIBE:
		slurm_free_steps_drained_sub_msg(data);
		break;
	case SRUN_PING:
		slurm_free_srun_ping_msg(data);
		break;
	case SRUN_TIMEOUT:
		slurm_free_srun_timeout_msg(data);
		break;
	case SRUN_USER_MSG:
		slurm_free_srun_user_msg(data);
		break;
	case SRUN_NODE_FAIL:
		slurm_free_srun_node_fail_msg(data);
		break;
	case SRUN_STEP_MISSING:
		slurm_free_srun_step_missing_msg(data);
		break;
	case SRUN_NET_FORWARD:
		slurm_free_net_forward_msg(data);
		break;
	case PMI_KVS_GET_REQ:
		slurm_free_get_kvs_msg(data);
		break;
	case PMI_KVS_GET_RESP:
	case PMI_KVS_PUT_REQ:
		slurm_free_kvs_comm_set(data);
		break;
	case RESPONSE_JOB_ALLOCATION_INFO:
	case RESPONSE_RESOURCE_ALLOCATION:
		slurm_free_resource_allocation_response_msg(data);
		break;
	case REQUEST_ASSOC_MGR_INFO:
		slurm_free_assoc_mgr_info_request_msg(data);
		break;
	case RESPONSE_ASSOC_MGR_INFO:
		slurm_free_assoc_mgr_info_msg(data);
		break;
	case REQUEST_CTLD_MULT_MSG:
	case RESPONSE_CTLD_MULT_MSG:
		slurm_free_ctld_multi_msg(data);
		break;
	case RESPONSE_JOB_INFO:
		slurm_free_job_info(data);
		break;
	case REQUEST_HET_JOB_ALLOCATION:
	case REQUEST_SUBMIT_BATCH_HET_JOB:
	case RESPONSE_HET_JOB_ALLOCATION:
		FREE_NULL_LIST(data);
		break;
	case REQUEST_SET_FS_DAMPENING_FACTOR:
		slurm_free_set_fs_dampening_factor_msg(data);
		break;
	case REQUEST_SET_SUSPEND_EXC_NODES:
	case REQUEST_SET_SUSPEND_EXC_PARTS:
	case REQUEST_SET_SUSPEND_EXC_STATES:
		slurm_free_suspend_exc_update_msg(data);
		break;
	case REQUEST_DBD_RELAY:
		slurmdbd_free_msg(data);
		xfree(data);
		break;
	case RESPONSE_CONTROL_STATUS:
		slurm_free_control_status_msg(data);
		break;
	case REQUEST_BURST_BUFFER_STATUS:
		slurm_free_bb_status_req_msg(data);
		break;
	case RESPONSE_BURST_BUFFER_STATUS:
		slurm_free_bb_status_resp_msg(data);
		break;
	case REQUEST_CRONTAB:
		slurm_free_crontab_request_msg(data);
		break;
	case RESPONSE_CRONTAB:
		slurm_free_crontab_response_msg(data);
		break;
	case REQUEST_UPDATE_CRONTAB:
		slurm_free_crontab_update_request_msg(data);
		break;
	case RESPONSE_UPDATE_CRONTAB:
		slurm_free_crontab_update_response_msg(data);
		break;
	case REQUEST_TLS_CERT:
		slurm_free_tls_cert_request_msg(data);
		break;
	case RESPONSE_TLS_CERT:
		slurm_free_tls_cert_response_msg(data);
		break;
	case REQUEST_STEP_BY_CONTAINER_ID:
		slurm_free_container_id_request_msg(data);
		break;
	case RESPONSE_STEP_BY_CONTAINER_ID:
		slurm_free_container_id_response_msg(data);
		break;
	case RESPONSE_CONTAINER_STATE:
		slurm_destroy_container_state_msg(data);
		break;
	case REQUEST_CONTAINER_EXEC:
		slurm_destroy_container_exec_msg(data);
		break;
	case REQUEST_HOSTLIST_EXPANSION:
	case RESPONSE_HOSTLIST_EXPANSION:
	case REQUEST_CONTAINER_KILL:
	case REQUEST_CONTAINER_DELETE:
	case RESPONSE_CONTAINER_START:
		/* struct has no members that need to be freed */
		xfree_ptr(data);
		break;
	case REQUEST_NODE_ALIAS_ADDRS:
	case RESPONSE_NODE_ALIAS_ADDRS:
		slurm_free_node_alias_addrs(data);
		break;
	default:
		error("%s: Unable to free unknown msg_type=0x%x @ 0x%" PRIxPTR,
		      __func__, type, (uintptr_t) data);
		break;
	}
}

extern uint32_t slurm_get_return_code(slurm_msg_type_t type, void *data)
{
	uint32_t rc = 0;

	switch (type) {
	case MESSAGE_EPILOG_COMPLETE:
		rc = ((epilog_complete_msg_t *)data)->return_code;
		break;
	case RESPONSE_JOB_STEP_STAT:
		rc = ((job_step_stat_t *)data)->return_code;
		break;
	case RESPONSE_REATTACH_TASKS:
		rc = ((reattach_tasks_response_msg_t *)data)->return_code;
		break;
	case RESPONSE_JOB_ID:
		rc = ((job_id_response_msg_t *)data)->return_code;
		break;
	case RESPONSE_SLURM_RC:
		rc = ((return_code_msg_t *)data)->return_code;
		break;
	case RESPONSE_PING_SLURMD:
		rc = SLURM_SUCCESS;
		break;
	case RESPONSE_ACCT_GATHER_UPDATE:
		rc = SLURM_SUCCESS;
		break;
	case RESPONSE_FORWARD_FAILED:
		/* There may be other reasons for the failure, but
		 * this may be a slurm_msg_t data type lacking the
		 * err field found in ret_data_info_t data type */
		rc = SLURM_COMMUNICATIONS_CONNECTION_ERROR;
		break;
	default:
		error("don't know the rc for type %u returning %u", type, rc);
		xassert(false);
		break;
	}
	return rc;
}

extern void slurm_free_job_notify_msg(job_notify_msg_t * msg)
{
	if (msg) {
		xfree(msg->message);
		xfree(msg);
	}
}

extern void slurm_free_ctld_multi_msg(ctld_list_msg_t *msg)
{
	if (msg) {
		FREE_NULL_LIST(msg->my_list);
		xfree(msg);
	}
}

/*
 *  Sanitize spank_job_env by prepending "SPANK_" to all entries,
 *   thus rendering them harmless in environment of scripts and
 *   programs running with root privileges.
 */
extern bool valid_spank_job_env(char **spank_job_env,
				uint32_t spank_job_env_size, uid_t uid)
{
	int i;
	char *entry;

	for (i=0; i<spank_job_env_size; i++) {
		if (!xstrncmp(spank_job_env[i], "SPANK_", 6))
			continue;
		entry = spank_job_env[i];
		spank_job_env[i] = xstrdup_printf ("SPANK_%s", entry);
		xfree (entry);
	}
	return true;
}

/* slurm_free_license_info()
 *
 * Free the license info returned previously
 * from the controller.
 */
extern void
slurm_free_license_info_msg(license_info_msg_t *msg)
{
	int cc;

	if (msg == NULL)
		return;

	if (msg->lic_array) {
		for (cc = 0; cc < msg->num_lic; cc++) {
			xfree(msg->lic_array[cc].name);
			xfree(msg->lic_array[cc].nodes);
		}
		xfree(msg->lic_array);
	}
	xfree(msg);
}
extern void slurm_free_license_info_request_msg(license_info_request_msg_t *msg)
{
	xfree(msg);
}

extern char *
slurm_bb_flags2str(uint32_t bb_flags)
{
	static char bb_str[1024];

	bb_str[0] = '\0';
	if (bb_flags & BB_FLAG_DISABLE_PERSISTENT) {
		if (bb_str[0])
			strcat(bb_str, ",");
		strcat(bb_str, "DisablePersistent");
	}
	if (bb_flags & BB_FLAG_EMULATE_CRAY) {
		if (bb_str[0])
			strcat(bb_str, ",");
		strcat(bb_str, "EmulateCray");
	}
	if (bb_flags & BB_FLAG_ENABLE_PERSISTENT) {
		if (bb_str[0])
			strcat(bb_str, ",");
		strcat(bb_str, "EnablePersistent");
	}
	if (bb_flags & BB_FLAG_PRIVATE_DATA) {
		if (bb_str[0])
			strcat(bb_str, ",");
		strcat(bb_str, "PrivateData");
	}
	if (bb_flags & BB_FLAG_TEARDOWN_FAILURE) {
		if (bb_str[0])
			strcat(bb_str, ",");
		strcat(bb_str, "TeardownFailure");
	}

	return bb_str;
}

extern uint32_t slurm_bb_str2flags(char *bb_str)
{
	uint32_t bb_flags = 0;

	if (xstrcasestr(bb_str, "DisablePersistent"))
		bb_flags |= BB_FLAG_DISABLE_PERSISTENT;
	if (xstrcasestr(bb_str, "EmulateCray"))
		bb_flags |= BB_FLAG_EMULATE_CRAY;
	if (xstrcasestr(bb_str, "EnablePersistent"))
		bb_flags |= BB_FLAG_ENABLE_PERSISTENT;
	if (xstrcasestr(bb_str, "PrivateData"))
		bb_flags |= BB_FLAG_PRIVATE_DATA;
	if (xstrcasestr(bb_str, "TeardownFailure"))
		bb_flags |= BB_FLAG_TEARDOWN_FAILURE;

	return bb_flags;
}

extern void
slurm_free_assoc_mgr_info_msg(assoc_mgr_info_msg_t *msg)
{
	if (!msg)
		return;

	FREE_NULL_LIST(msg->assoc_list);
	FREE_NULL_LIST(msg->qos_list);
	if (msg->tres_names) {
		int i;
		for (i=0; i<msg->tres_cnt; i++)
			xfree(msg->tres_names[i]);
		xfree(msg->tres_names);
	}
	FREE_NULL_LIST(msg->user_list);
	xfree(msg);
}

extern void slurm_free_assoc_mgr_info_request_members(
	assoc_mgr_info_request_msg_t *msg)
{
	if (!msg)
		return;

	FREE_NULL_LIST(msg->acct_list);
	FREE_NULL_LIST(msg->qos_list);
	FREE_NULL_LIST(msg->user_list);
}

extern void slurm_free_assoc_mgr_info_request_msg(
	assoc_mgr_info_request_msg_t *msg)
{
	if (!msg)
		return;

	slurm_free_assoc_mgr_info_request_members(msg);
	xfree(msg);
}

extern int parse_part_enforce_type(char *enforce_part_type, uint16_t *param)
{
	int rc = SLURM_SUCCESS;

	char *value = xstrdup(enforce_part_type);

	if (!xstrcasecmp(value, "yes")
		|| !xstrcasecmp(value, "up")
		|| !xstrcasecmp(value, "true")
		|| !xstrcasecmp(value, "1") || !xstrcasecmp(value, "any")) {
		*param = PARTITION_ENFORCE_ANY;
	} else if (!xstrcasecmp(value, "no")
		   || !xstrcasecmp(value, "down")
		   || !xstrcasecmp(value, "false")
		   || !xstrcasecmp(value, "0")) {
		*param = PARTITION_ENFORCE_NONE;
	} else if (!xstrcasecmp(value, "all")) {
		*param = PARTITION_ENFORCE_ALL;
	} else {
		error("Bad EnforcePartLimits: %s\n", value);
		rc = SLURM_ERROR;
	}

	xfree(value);
	return rc;
}

extern char * parse_part_enforce_type_2str (uint16_t type)
{
	static char type_str[1024];

	if (type == PARTITION_ENFORCE_NONE) {
		strcpy(type_str, "NO");
	} else if (type == PARTITION_ENFORCE_ANY) {
		strcpy(type_str, "ANY");
	} else if (type == PARTITION_ENFORCE_ALL) {
		strcpy(type_str, "ALL");
	}

	return type_str;
}

/* Return true if this cluster_name is in a federation */
extern bool cluster_in_federation(void *ptr, char *cluster_name)
{
	slurmdb_federation_rec_t *fed = (slurmdb_federation_rec_t *) ptr;
	slurmdb_cluster_rec_t *cluster;
	list_itr_t *iter;
	bool status = false;

	if (!fed || !fed->cluster_list)		/* NULL if no federations */
		return status;
	iter = list_iterator_create(fed->cluster_list);
	while ((cluster = (slurmdb_cluster_rec_t *) list_next(iter))) {
		if (!xstrcasecmp(cluster->name, cluster_name)) {
			status = true;
			break;
		}
	}
	list_iterator_destroy(iter);
	return status;
}

/* Find where cluster_name nodes start in the node_array */
extern int get_cluster_node_offset(char *cluster_name,
				   node_info_msg_t *node_info_ptr)
{
	int offset;

	xassert(cluster_name);
	xassert(node_info_ptr);

	for (offset = 0; offset < node_info_ptr->record_count; offset++)
		if (!xstrcmp(cluster_name,
			     node_info_ptr->node_array[offset].cluster_name))
			return offset;

	return 0;
}

extern void print_multi_line_string(char *user_msg, int inx,
				    log_level_t log_lvl)
{
	char *line, *buf, *ptrptr = NULL;

	if (!user_msg)
		return;

	buf = xstrdup(user_msg);
	line = strtok_r(buf, "\n", &ptrptr);
	while (line) {
		if (inx == -1)
			log_var(log_lvl, "%s", line);
		else
			log_var(log_lvl, "%d: %s", inx, line);
		line = strtok_r(NULL, "\n", &ptrptr);
	}
	xfree(buf);
}

/*
 * Given a numeric suffix, return the equivalent multiplier for the numeric
 * portion. For example: "k" returns 1024, "KB" returns 1000, etc.
 * The return value for an invalid suffix is NO_VAL64.
 */
extern uint64_t suffix_mult(char *suffix)
{
	uint64_t multiplier;

	if (!suffix || (suffix[0] == '\0')) {
		multiplier = 1;

	} else if (!xstrcasecmp(suffix, "k") ||
		   !xstrcasecmp(suffix, "kib")) {
		multiplier = 1024;
	} else if (!xstrcasecmp(suffix, "kb")) {
		multiplier = 1000;

	} else if (!xstrcasecmp(suffix, "m") ||
		   !xstrcasecmp(suffix, "mib")) {
		multiplier = ((uint64_t)1024 * 1024);
	} else if (!xstrcasecmp(suffix, "mb")) {
		multiplier = ((uint64_t)1000 * 1000);

	} else if (!xstrcasecmp(suffix, "g") ||
		   !xstrcasecmp(suffix, "gib")) {
		multiplier = ((uint64_t)1024 * 1024 * 1024);
	} else if (!xstrcasecmp(suffix, "gb")) {
		multiplier = ((uint64_t)1000 * 1000 * 1000);

	} else if (!xstrcasecmp(suffix, "t") ||
		   !xstrcasecmp(suffix, "tib")) {
		multiplier = ((uint64_t)1024 * 1024 * 1024 * 1024);
	} else if (!xstrcasecmp(suffix, "tb")) {
		multiplier = ((uint64_t)1000 * 1000 * 1000 * 1000);

	} else if (!xstrcasecmp(suffix, "p") ||
		   !xstrcasecmp(suffix, "pib")) {
		multiplier = ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024);
	} else if (!xstrcasecmp(suffix, "pb")) {
		multiplier = ((uint64_t)1000 * 1000 * 1000 * 1000 * 1000);

	} else {
		multiplier = NO_VAL64;
	}

	return multiplier;
}

extern bool verify_step_id(slurm_step_id_t *object, slurm_step_id_t *key)
{
	/* If the SLUID is set on both, then reject if they're not equal. */
	if (key->sluid && object->sluid) {
		if (key->sluid != object->sluid)
			return 0;
	} else if (key->job_id != object->job_id) {
		return 0;
	}

	/* Any step will do */
	if (key->step_id == NO_VAL)
		return 1;

	/*
	 * See if we have the same step id.  If we do check to see if we
	 * have the same step_het_comp or if the key's is NO_VAL,
	 * meaning we are not looking directly for a het step.
	 */
	if ((key->step_id == object->step_id) &&
	    ((key->step_het_comp == object->step_het_comp) ||
	     (key->step_het_comp == NO_VAL)))
		return 1;
	else
		return 0;
}

extern char *slurm_get_selected_step_id(
	char *job_id_str, int len,
	slurm_selected_step_t *selected_step)
{
	int pos = 0;

	if (selected_step->step_id.sluid) {
		print_sluid(selected_step->step_id.sluid, job_id_str, len);
		pos = strlen(job_id_str);
	} else {
		pos = snprintf(job_id_str, len, "%u",
			       selected_step->step_id.job_id);

		if (pos > len)
			goto endit;

		if (selected_step->array_task_id != NO_VAL)
			pos += snprintf(job_id_str + pos, len - pos, "_%u",
					selected_step->array_task_id);
		if (pos > len)
			goto endit;

		if (selected_step->het_job_offset != NO_VAL)
			pos += snprintf(job_id_str + pos, len - pos, "+%u",
					selected_step->het_job_offset);
		if (pos > len)
			goto endit;
	}

	if (selected_step->step_id.step_id != NO_VAL) {
		job_id_str[pos++] = '.';

		if (pos > len)
			goto endit;

		log_build_step_id_str(&selected_step->step_id,
				      job_id_str + pos, len - pos,
				      STEP_ID_FLAG_NO_PREFIX |
				      STEP_ID_FLAG_NO_JOB);
	}
endit:
	return job_id_str;
}

extern void xlate_array_task_str(char **array_task_str,
				 uint32_t array_max_tasks,
				 bitstr_t **array_bitmap)
{
	static int bitstr_len = -1;
	int buf_size, len;
	int i, i_first, i_last, i_prev, i_step = 0;
	bitstr_t *task_bitmap;
	char *out_buf = NULL;

	xassert(array_task_str);

	if (!array_task_str || !*array_task_str || !*array_task_str[0]) {
		if (array_bitmap)
			*array_bitmap = NULL;
		return;
	}

	i = strlen(*array_task_str);
	if ((i < 3) || ((*array_task_str)[1] != 'x')) {
		if (array_bitmap)
			*array_bitmap = NULL;
		return;
	}

	task_bitmap = bit_alloc(i * 4);
	if (bit_unfmt_hexmask(task_bitmap, *array_task_str) == -1)
		error("%s: bit_unfmt_hexmask error on '%s'", __func__,
		      *array_task_str);

	if (array_bitmap)
		*array_bitmap = task_bitmap;

	/* Check first for a step function */
	i_first = bit_ffs(task_bitmap);
	i_last  = bit_fls(task_bitmap);
	if (((i_last - i_first) > 10) && (bit_set_count(task_bitmap) > 5) &&
	    !bit_test(task_bitmap, i_first + 1)) {
		bool is_step = true;
		i_prev = i_first;
		for (i = i_first + 1; i <= i_last; i++) {
			if (!bit_test(task_bitmap, i))
				continue;
			if (i_step == 0) {
				i_step = i - i_prev;
			} else if ((i - i_prev) != i_step) {
				is_step = false;
				break;
			}
			i_prev = i;
		}
		if (is_step) {
			xstrfmtcat(out_buf, "%d-%d:%d",
				   i_first, i_last, i_step);
			goto out;
		}
	}

	if (bitstr_len == -1) {
		char *bitstr_len_str = getenv("SLURM_BITSTR_LEN");
		if (bitstr_len_str)
			bitstr_len = atoi(bitstr_len_str);
		if (bitstr_len < 0)
			bitstr_len = 64;
		else
			bitstr_len = MIN(bitstr_len, 4096);
	}

	if (bitstr_len > 0) {
		/* Print the first bitstr_len bytes of the bitmap string */
		buf_size = bitstr_len;
		out_buf = xmalloc(buf_size);
		bit_fmt(out_buf, buf_size, task_bitmap);
		len = strlen(out_buf);
		if (len > (buf_size - 3)) {
			for (i = 0; i < 3; i++)
				out_buf[buf_size - 2 - i] = '.';
		}
	} else {
		/* Print the full bitmap's string representation.
		 * For huge bitmaps this can take roughly one minute,
		 * so let the client do the work */
		out_buf = bit_fmt_full(task_bitmap);
	}

out:
	if (array_max_tasks)
		xstrfmtcat(out_buf, "%%%u", array_max_tasks);

	xfree(*array_task_str);
	*array_task_str = out_buf;

	if (!array_bitmap)
		FREE_NULL_BITMAP(task_bitmap);
}

#define _slurm_integer_array_to_value_reps(type_t, array, array_cnt,	\
					   values, values_reps,		\
					   values_cnt) do {		\
	type_t prev_value;						\
	int values_inx = 0;						\
	xassert(values);						\
	xassert(values_reps);						\
	xassert(values_cnt);						\
									\
	if (!array)							\
		return;							\
									\
	*values_cnt = 1;						\
									\
	/* Figure out how big the compressed arrays should be */	\
	prev_value = array[0];						\
	for (int i = 0; i < array_cnt; i++) {				\
		if (prev_value != array[i]) {				\
			prev_value = array[i];				\
			(*values_cnt)++;				\
		}							\
	}								\
									\
	*values = xcalloc(*values_cnt, sizeof(**values));		\
	*values_reps = xcalloc(*values_cnt, sizeof(**values_reps));	\
									\
	prev_value = (*values)[0] = array[0];				\
	for (int i = 0; i < array_cnt; i++) {				\
		if (prev_value != array[i]) {				\
			prev_value = array[i];				\
			values_inx++;					\
			(*values)[values_inx] = array[i];		\
		}							\
		(*values_reps)[values_inx]++;				\
	}								\
									\
} while (0);

extern void slurm_array64_to_value_reps(uint64_t *array, uint32_t array_cnt,
					uint64_t **values,
					uint32_t **values_reps,
					uint32_t *values_cnt)
{
	_slurm_integer_array_to_value_reps(uint64_t, array, array_cnt, values,
					   values_reps, values_cnt);
}

extern void slurm_array16_to_value_reps(uint16_t *array, uint32_t array_cnt,
					uint16_t **values,
					uint32_t **values_reps,
					uint32_t *values_cnt)
{
	_slurm_integer_array_to_value_reps(uint16_t, array, array_cnt, values,
					   values_reps, values_cnt);
}

extern int slurm_get_rep_count_inx(
	uint32_t *rep_count, uint32_t rep_count_size, int inx)
{
	int rep_count_sum = 0;

	for (int i = 0; i < rep_count_size; i++) {
		if (rep_count[i] == 0) {
			error("%s: rep_count should never be zero",
			      __func__);
			return -1;
		}
		rep_count_sum += rep_count[i];
		if (rep_count_sum > inx)
			return i;
	}

	return -1;
}

extern void slurm_format_tres_string(char **s, char *tres_type)
{
	char *tmp, *save_ptr = NULL, *pos = NULL;
	char *ret_str = NULL;
	char *prefix = NULL;
	size_t tres_prefix_len;
	int colon_inx;

	if (!*s)
		return;
	/* Prime the tres_type */
	prefix = xstrdup_printf("%s:", tres_type);

	if (!xstrstr(*s, prefix)) {
		/* The tres string is already correctly formatted */
		xfree(prefix);
		return;
	}

	tres_prefix_len = strlen(prefix);
	colon_inx = tres_prefix_len - 1;

	for (tmp = strtok_r(*s, ",", &save_ptr); tmp;
	     tmp = strtok_r(NULL, ",", &save_ptr)) {
		if (!xstrncmp(tmp, prefix, tres_prefix_len))
			tmp[colon_inx] = '/';
		if (ret_str)
			xstrfmtcatat(ret_str, &pos, ",%s", tmp);
		else
			xstrcatat(ret_str, &pos, tmp);
	}
	xassert(ret_str);
	xfree(*s);
	*s = ret_str;
	xfree(prefix);
}

/*
 * Fuzzy name comparison for remote licenses
 * query IN - query string
 * name IN - license name
 * RET rc - 0 for no match, 1 for exact match, 2 for fuzzy match
 */
extern int slurm_remote_license_fuzzy_match(const char *query, const char *name)
{
	char *split = NULL, *query_split = NULL;
	size_t cnt = 0;

	if ((query == NULL) || (name == NULL))
		return LIC_NO_MATCH;

	query_split = xstrchr(query, '@');
	split = xstrchr(name, '@');
	if (split)
		cnt = split - name;
	/*
	 * fuzzy match cases
	 * check1:
	 * 	"matlab" querying "matlab@test" -> should match
	 * check2:
	 * 	"matla"  querying "matlab@test" -> should fail
	 * check3:
	 * 	"matlab2" querying "matlab@test" -> should fail
	 * check4:
	 * 	"matlab@test" querying "matlab@test" -> should match
	 * thus, the proper fuzzy matching logic is something like
	 * If the query string has an '@', match the whole string,
	 * if the query string does not have an '@', match only the
	 * pre-@ portion of the string
	 */
	if (!query_split) {
		if (strlen(query) != cnt)
			return LIC_NO_MATCH;
		if (xstrncmp(name, query, cnt))
			return LIC_NO_MATCH;
		return LIC_FUZZY_MATCH;
	} else if (xstrcmp(name, query))
		return LIC_NO_MATCH;
	return LIC_EXACT_MATCH;
}

extern int slurm_get_next_tres(
	char **tres_type, char *in_val, char **name_ptr, char **type_ptr,
	uint64_t *cnt, char **save_ptr)
{
	char *comma, *sep, *name = NULL, *type = NULL;
	int rc = SLURM_SUCCESS, tres_type_len = 0;
	uint64_t value = 0;
	bool is_gres = false;

	xassert(tres_type);
	xassert(cnt);
	xassert(save_ptr);

	if (!in_val && (*save_ptr == NULL)) {
		return rc;
	}

	if (*save_ptr == NULL) {
		*save_ptr = in_val;
	}

	if (*tres_type) {
		tres_type_len = strlen(*tres_type);
		if (!tres_type_len) {
			fatal_abort("tres_type is blank. If you don't want to specify a tres_type send in NULL not \"\".");
			return SLURM_ERROR;
		}
	}

next:	if (*save_ptr[0] == '\0') {	/* Empty input token */
		*save_ptr = NULL;
		goto fini;
	}

	if (*tres_type) {
		if (!(sep = xstrstr(*save_ptr, *tres_type))) {
			debug2("%s is not a %s", *save_ptr, *tres_type);
			xfree(name);
			*save_ptr = NULL;
			*name_ptr = NULL;
			goto fini;
		} else {
			sep += tres_type_len; /* strlen "gres" */
			*save_ptr = sep;
		}
	} else {
		char extra = '\0';
		comma = strchr(*save_ptr, ',');

		/*
		 * This is original memory so anything we change here needs to
		 * be put back to the way it was before we starting messing with
		 * it.
		 */
		if (comma)
			comma[0] = '\0';

		if ((sep = strchr(*save_ptr, '/')) ||
		    (sep = strchr(*save_ptr, ':')) ||
		    (sep = strchr(*save_ptr, '='))) {
			extra = sep[0];
			sep[0] = '\0';
		}

		/*
		 * Set the tres_type and set tres_type_len to 0 which indicates
		 * we created this value and we need to free after this
		 * iteration.
		 */
		*tres_type = xstrdup(*save_ptr);
		tres_type_len = 0;

		if (comma)
			comma[0] = ',';
		if (sep) {
			sep[0] = extra;
			*save_ptr = sep;
		} else
			*save_ptr += strlen(*tres_type);
	}

	if (!*tres_type) {
		*save_ptr = NULL;
		*name_ptr = NULL;
		goto fini;
	}

	if (*save_ptr[0] == '/')
		(*save_ptr)++;

	name = xstrdup(*save_ptr);
	comma = strchr(name, ',');
	if (comma) {
		*save_ptr += (comma - name + 1);
		comma[0] = '\0';
	} else {
		*save_ptr += strlen(name);
	}

	if (name[0] == '\0') {
		/* Nothing but a comma */
		if (!tres_type_len)
			xfree(*tres_type);
		xfree(name);
		goto next;
	}

	is_gres = !xstrcasecmp(*tres_type, "gres");

	/* First check to see if the last part is a count or not */
	if ((sep = strrchr(name, '=')) ||
	    (sep = strrchr(name, ':'))) {
		bool equals = (sep[0] == '=') ? true : false, valid_num;
		sep[0] = '\0';
		sep++;
		if (sep[0] == '\0') {
			/* Bad format (e.g. "gpu:") */
			rc = ESLURM_INVALID_TRES;
			goto fini;
		}

		valid_num = _is_valid_number(sep, &value);

		if (!valid_num) {
			if (equals) {
				rc = ESLURM_INVALID_TRES;
				goto fini;
			}
			/* We have type with implicit count of 1 */
			type = xstrdup(sep);
			value = 1;
		}
	} else if (_is_valid_number(name, &value)) {
		xfree(name); /* we got a valid number, we don't have a name */
		goto fini;
	} else
		value = 1;

	if ((sep = strchr(name, ':'))) {
		sep[0] = '\0';
		sep++;

		/*
		 * If we already have a type we know it was 'supposed' to be a
		 * count.
		 */
		if (type) {
			xfree(type);
			rc = ESLURM_INVALID_TRES;
			goto fini;
		}
		type = xstrdup(sep);
	}

	/* Only 'gres' tres have 'types' */
	if (type && !is_gres) {
		error("TRES '%s' can't have a type (%s:%s)",
		      *tres_type, name, type);
		rc = ESLURM_INVALID_TRES;
		xfree(type);
		goto fini;
	}

	/*
	 * We have 0 elements of this type, so completely ignore this entry
	 * and do not return it. For example in the case of "gres/gpu:tesla:0",
	 * we would have: tres_type=gres, name = gpu, type = tesla, value = 0
	 */
	if (value == 0) {
		xfree(name);
		xfree(type);
		/*
		 * If !tres_type_len this function has been called with
		 * *tres_type = NULL, but we found it and gave it a value with
		 * xstrdup().
		 *
		 * As this type contains 0 elements, just reset *tres_type to
		 * NULL so the next iteration looks for the type again, or if we
		 * bailout, just return it empty.
		 */
		if (!tres_type_len)
			xfree(*tres_type);
		goto next;
	}

fini:	if (rc != SLURM_SUCCESS) {
		*save_ptr = NULL;
		if ((rc == ESLURM_INVALID_TRES) && running_in_slurmctld()) {
			info("%s: Invalid TRES job specification %s", __func__,
			     in_val);
		}
		if (!tres_type_len)
			xfree(*tres_type);
		xfree(type);
		xfree(name);
		*type_ptr = NULL;
		*name_ptr = NULL;
	} else {
		*cnt = value;
		*type_ptr = type;
		if (name && name[0] == '\0')
			xfree(name);
		*name_ptr = name;
	}

	return rc;
}

extern char *slurm_get_tres_sub_string(
	char *full_tres_str, char *tres_type, uint32_t num_tasks,
	bool include_tres_type, bool include_type)
{
	char *sub_tres = NULL, *sub_tres_pos = NULL;
	char *name, *type, *save_ptr = NULL;
	uint64_t cnt = 0;
	bool free_tres_type = false;

	if (!tres_type)
		free_tres_type = true;

	while ((slurm_get_next_tres(&tres_type,
				    full_tres_str,
				    &name, &type,
				    &cnt, &save_ptr) == SLURM_SUCCESS) &&
	       save_ptr) {

		if (num_tasks != NO_VAL)
			cnt *= num_tasks;

		if (sub_tres)
			xstrcatat(sub_tres, &sub_tres_pos, ",");
		if (include_tres_type)
			xstrfmtcatat(sub_tres, &sub_tres_pos, "%s%s", tres_type,
				     name ? "/" : "");
		if (name) {
			xstrfmtcatat(sub_tres, &sub_tres_pos, "%s", name);
			if (include_type && type)
				xstrfmtcatat(sub_tres, &sub_tres_pos, ":%s",
					     type);
		}
		xstrfmtcatat(sub_tres, &sub_tres_pos, "=%"PRIu64, cnt);
		if (free_tres_type)
			xfree(tres_type);
		xfree(name);
		xfree(type);
	}

	if (free_tres_type)
		xfree(tres_type);

	return sub_tres;
}

char *schedule_exit2string(uint16_t opcode)
{
	switch (opcode) {
	case SCHEDULE_EXIT_END:
		return "End of job queue";
	case SCHEDULE_EXIT_MAX_DEPTH:
		return "Hit default_queue_depth";
	case SCHEDULE_EXIT_MAX_JOB_START:
		return "Hit sched_max_job_start";
	case SCHEDULE_EXIT_LIC:
		return "Blocked on licenses";
	case SCHEDULE_EXIT_RPC_CNT:
		return "Hit max_rpc_cnt";
	case SCHEDULE_EXIT_TIMEOUT:
		return "Timeout (max_sched_time)";
	default:
		return "unknown";
	}
}

char *bf_exit2string(uint16_t opcode)
{
	switch (opcode) {
	case BF_EXIT_END:
		return "End of job queue";
	case BF_EXIT_MAX_JOB_START:
		return "Hit bf_max_job_start";
	case BF_EXIT_MAX_JOB_TEST:
		return "Hit bf_max_job_test";
	case BF_EXIT_STATE_CHANGED:
		return "System state changed";
	case BF_EXIT_TABLE_LIMIT:
		return "Hit table size limit (bf_node_space_size)";
	case BF_EXIT_TIMEOUT:
		return "Timeout (bf_max_time)";
	default:
		return "unknown";
	}
}

/* Set r_uid of agent_arg */
extern void set_agent_arg_r_uid(agent_arg_t *agent_arg_ptr, uid_t r_uid)
{
	agent_arg_ptr->r_uid = r_uid;
	agent_arg_ptr->r_uid_set = true;
}

extern void purge_agent_args(agent_arg_t *agent_arg_ptr)
{
	if (agent_arg_ptr == NULL)
		return;

	hostlist_destroy(agent_arg_ptr->hostlist);
	xfree(agent_arg_ptr->addr);
	xfree(agent_arg_ptr->tls_cert);
	if (agent_arg_ptr->msg_args) {
		if (agent_arg_ptr->msg_type == REQUEST_BATCH_JOB_LAUNCH) {
			slurm_free_job_launch_msg(agent_arg_ptr->msg_args);
		} else if (agent_arg_ptr->msg_type ==
				RESPONSE_RESOURCE_ALLOCATION) {
			resource_allocation_response_msg_t *alloc_msg =
				agent_arg_ptr->msg_args;
			/* NULL out working_cluster_rec because it's pointing to
			 * the actual cluster_rec. */
			alloc_msg->working_cluster_rec = NULL;
			slurm_free_resource_allocation_response_msg(
					agent_arg_ptr->msg_args);
		} else if (agent_arg_ptr->msg_type ==
				RESPONSE_HET_JOB_ALLOCATION) {
			list_t *alloc_list = agent_arg_ptr->msg_args;
			FREE_NULL_LIST(alloc_list);
		} else if ((agent_arg_ptr->msg_type == REQUEST_ABORT_JOB)    ||
			 (agent_arg_ptr->msg_type == REQUEST_TERMINATE_JOB)  ||
			 (agent_arg_ptr->msg_type == REQUEST_KILL_PREEMPTED) ||
			 (agent_arg_ptr->msg_type == REQUEST_KILL_TIMELIMIT))
			slurm_free_kill_job_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == SRUN_USER_MSG)
			slurm_free_srun_user_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == SRUN_NODE_FAIL)
			slurm_free_srun_node_fail_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == SRUN_STEP_MISSING)
			slurm_free_srun_step_missing_msg(
				agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == SRUN_STEP_SIGNAL)
			slurm_free_job_step_kill_msg(
				agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_JOB_NOTIFY)
			slurm_free_job_notify_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_SUSPEND_INT)
			slurm_free_suspend_int_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_LAUNCH_PROLOG)
			slurm_free_prolog_launch_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_REBOOT_NODES)
			slurm_free_reboot_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_RUN_POWER_ACTION)
			slurm_free_run_power_action_msg(agent_arg_ptr
								->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_RECONFIGURE_SACKD)
			slurm_free_config_response_msg(agent_arg_ptr->msg_args);
		else if (agent_arg_ptr->msg_type == REQUEST_RECONFIGURE_WITH_CONFIG)
			slurm_free_config_response_msg(agent_arg_ptr->msg_args);
		else
			xfree(agent_arg_ptr->msg_args);
	}
	xfree(agent_arg_ptr);
}

/*
 * validate_slurm_user - validate that the uid is authorized to see
 *      privileged data (either user root or SlurmUser)
 * IN uid - user to validate
 * RET true if permitted to run, false otherwise
 */
extern bool validate_slurm_user(uid_t uid)
{
	if ((uid == 0) || (uid == slurm_conf.slurm_user_id))
		return true;
	else
		return false;
}

/*
 * validate_slurmd_user - validate that the uid is authorized to see
 *      privileged data (either user root or SlurmdUser)
 * IN uid - user to validate
 * RET true if permitted to run, false otherwise
 */
extern bool validate_slurmd_user(uid_t uid)
{
	if ((uid == 0) || (uid == slurm_conf.slurmd_user_id))
		return true;
	else
		return false;
}

extern uint16_t get_job_share_value(job_record_t *job_ptr)
{
	uint16_t shared = 0;
	job_details_t *detail_ptr = job_ptr->details;

	if (!detail_ptr)
		shared = NO_VAL16;
	else if (detail_ptr->share_res == 1)	/* User --share */
		shared = JOB_SHARED_OK;
	else if ((detail_ptr->share_res == 0) ||
		 (detail_ptr->whole_node & WHOLE_NODE_REQUIRED))
		shared = JOB_SHARED_NONE;	/* User --exclusive */
	else if (detail_ptr->whole_node & WHOLE_NODE_USER)
		shared = JOB_SHARED_USER;	/* User --exclusive=user */
	else if (detail_ptr->whole_node & WHOLE_NODE_MCS)
		shared = JOB_SHARED_MCS;	/* User --exclusive=mcs */
	else if (detail_ptr->whole_node & WHOLE_TOPO)
		shared = JOB_SHARED_TOPO;	/* User --exclusive=topo */
	else if (job_ptr->part_ptr) {
		/* Report shared status based upon latest partition info */
		if (job_ptr->part_ptr->flags & PART_FLAG_EXCLUSIVE_TOPO)
			shared = JOB_SHARED_TOPO;
		else if (job_ptr->part_ptr->flags & PART_FLAG_EXCLUSIVE_USER)
			shared = JOB_SHARED_USER;
		else if ((job_ptr->part_ptr->max_share & SHARED_FORCE) &&
			 ((job_ptr->part_ptr->max_share & (~SHARED_FORCE)) > 1))
			shared = 1; /* Partition OverSubscribe=force */
		else if (job_ptr->part_ptr->max_share == 0)
			/* Partition OverSubscribe=exclusive */
			shared = JOB_SHARED_NONE;
		else
			shared = NO_VAL16;  /* Part OverSubscribe=yes or no */
	} else
		shared = NO_VAL16;	/* No user or partition info */

	return shared;
}

/*
 * Return oversubscribe display value for cred/env: 0=NO, 1=YES, 2=OK.
 */
extern uint16_t get_job_oversubscribe_value(job_record_t *job_ptr)
{
	job_details_t *d = job_ptr->details;

	if (!d)
		return JOB_OVERSUBSCRIBE_NO;
	if (d->share_res == 1)
		return JOB_OVERSUBSCRIBE_YES;
	if ((d->share_res == NO_VAL8) && job_ptr->part_ptr) {
		uint16_t m = job_ptr->part_ptr->max_share;
		if ((m > 1) || (m & SHARED_FORCE))
			return JOB_OVERSUBSCRIBE_OK;
	}
	return JOB_OVERSUBSCRIBE_NO;
}

/*
 * Return exclusive display value for cred/env (JOB_EXCLUSIVE_*).
 * Single value: NONE|NODE|USER|MCS|TOPO.
 */
extern uint16_t get_job_exclusive_display_value(job_record_t *job_ptr)
{
	job_details_t *d = job_ptr->details;
	part_record_t *p = job_ptr->part_ptr;
	bool part_topo = p && (p->flags & PART_FLAG_EXCLUSIVE_TOPO);

	if (d) {
		if (d->share_res == 1)
			return JOB_EXCLUSIVE_NONE;
		if (d->whole_node & WHOLE_TOPO)
			return JOB_EXCLUSIVE_TOPO;
		/*
		 * WHOLE_NODE_REQUIRED takes precedence over USER/MCS so the
		 * display reflects actual scheduling behavior. The scheduler
		 * (linear, or any partition with max_share=0) may add
		 * WHOLE_NODE_REQUIRED on top of a job's WHOLE_NODE_USER /
		 * WHOLE_NODE_MCS request, in which case the job actually gets
		 * a whole node.
		 */
		if (d->whole_node & WHOLE_NODE_REQUIRED) {
			if (part_topo)
				return JOB_EXCLUSIVE_TOPO;
			return JOB_EXCLUSIVE_NODE;
		}
		if (d->whole_node & WHOLE_NODE_USER)
			return JOB_EXCLUSIVE_USER;
		if (d->whole_node & WHOLE_NODE_MCS)
			return JOB_EXCLUSIVE_MCS;
	}
	if (p) {
		if (p->flags & PART_FLAG_EXCLUSIVE_TOPO)
			return JOB_EXCLUSIVE_TOPO;
		if (p->flags & PART_FLAG_EXCLUSIVE_USER)
			return JOB_EXCLUSIVE_USER;
		if (p->max_share == 0)
			return JOB_EXCLUSIVE_NODE;
	}
	return JOB_EXCLUSIVE_NONE;
}

extern void slurm_free_stepmgr_job_info(stepmgr_job_info_t *object)
{
	if (!object)
		return;

	xfree(object->stepmgr);
	xfree(object);
}

/* Resv creation msg client validation. On error err_msg is set */
extern int validate_resv_create_desc(resv_desc_msg_t *resv_msg, char **err_msg,
				     uint32_t *res_free_flags)
{
	if (resv_msg->start_time == (time_t) NO_VAL) {
		*err_msg = "A start time must be given.  No reservation created.";
		return SLURM_ERROR;
	}
	if ((resv_msg->end_time == (time_t) NO_VAL) &&
	    (resv_msg->duration == NO_VAL)) {
		*err_msg = "An end time or duration must be given.  No reservation created.";
		return SLURM_ERROR;
	}
	if ((resv_msg->end_time != (time_t) NO_VAL) &&
	    (resv_msg->duration != NO_VAL) &&
	    ((resv_msg->start_time + (resv_msg->duration * 60)) !=
	     resv_msg->end_time)) {
		*err_msg = "StartTime + Duration does not equal EndTime.  No reservation created.";
		return SLURM_ERROR;
	}
	if ((resv_msg->start_time > resv_msg->end_time) &&
	    (resv_msg->end_time != (time_t) NO_VAL)) {
		*err_msg = "Start time cannot be after end time.  No reservation created.";
		return SLURM_ERROR;
	}

	/*
	 * If "ALL" is specified for the nodes and a partition is specified,
	 * only allocate all of the nodes the partition.
	 */
	if ((resv_msg->partition != NULL) && (resv_msg->node_list != NULL) &&
	    (xstrcasecmp(resv_msg->node_list, "ALL") == 0)) {
		if (resv_msg->flags == NO_VAL64)
			resv_msg->flags = RESERVE_FLAG_PART_NODES;
		else
			resv_msg->flags |= RESERVE_FLAG_PART_NODES;
	}

	/*
	 * If RESERVE_FLAG_PART_NODES is specified for the reservation,
	 * make sure a partition name is specified and nodes=ALL.
	 */
	if ((resv_msg->flags != NO_VAL64) &&
	    (resv_msg->flags & RESERVE_FLAG_PART_NODES) &&
	    (!resv_msg->partition ||
	     (xstrcasecmp(resv_msg->node_list, "ALL")))) {
		*err_msg = "PART_NODES flag requires specifying a Partition and ALL nodes.  No reservation created.";
		return SLURM_ERROR;
	}

	/*
	 * Ensure RESERVE_FLAG_FORCE_START is specified with a reoccuring flag.
	 */
	if ((resv_msg->flags != NO_VAL64) &&
	    (resv_msg->flags & RESERVE_FLAG_FORCE_START) &&
	    (!(resv_msg->flags & RESERVE_REOCCURRING))) {
		*err_msg = "FORCE_START flag requires a reoccuring reservation. No reservation created.";
		return SLURM_ERROR;
	}

	/*
	 * If the following parameters are null, but a partition is named, then
	 * make the reservation for the whole partition.
	 */
	if ((!resv_msg->core_cnt || (resv_msg->core_cnt == NO_VAL)) &&
	    ((resv_msg->burst_buffer == NULL) ||
	     (resv_msg->burst_buffer[0] == '\0')) &&
	    (!resv_msg->node_cnt || (resv_msg->node_cnt == NO_VAL)) &&
	    ((resv_msg->node_list == NULL) ||
	     (resv_msg->node_list[0] == '\0')) &&
	    ((resv_msg->licenses == NULL) || (resv_msg->licenses[0] == '\0')) &&
	    ((resv_msg->tres_str == NULL) || (resv_msg->tres_str[0] == '\0'))) {
		if (resv_msg->partition == NULL) {
			*err_msg = "CoreCnt, Nodes, NodeCnt, TRES or Watts must be specified.  No reservation created.";
			return SLURM_ERROR;
		}
		if (resv_msg->flags == NO_VAL64)
			resv_msg->flags = RESERVE_FLAG_PART_NODES;
		else
			resv_msg->flags |= RESERVE_FLAG_PART_NODES;

		if (res_free_flags && (*res_free_flags & RESV_FREE_STR_NODES))
			xfree(resv_msg->node_list);

		resv_msg->node_list = xstrdup("ALL");

		if (res_free_flags)
			*res_free_flags |= RESV_FREE_STR_NODES;
	}

	if (((resv_msg->users == NULL) || (resv_msg->users[0] == '\0')) &&
	    ((resv_msg->groups == NULL) || (resv_msg->groups[0] == '\0')) &&
	    (!resv_msg->qos || (resv_msg->qos[0] == '\0')) &&
	    (!resv_msg->allowed_parts ||
	     (resv_msg->allowed_parts[0] == '\0')) &&
	    ((resv_msg->accounts == NULL) || (resv_msg->accounts[0] == '\0'))) {
		*err_msg = "Either Users/Groups, AllowedPartitions, QOS and/or Accounts must be specified.  No reservation created.";
		return SLURM_ERROR;
	} else if (resv_msg->users && resv_msg->groups) {
		*err_msg = "Users and Groups are mutually exclusive.  You can have one or the other, but not both.  No reservation created.";
		return SLURM_ERROR;
	}

	return SLURM_SUCCESS;
}

static int _foreach_get_default(void *x, void *arg)
{
	job_defaults_t *job_defaults = x;
	uint16_t type = *(uint16_t *) arg;

	if (job_defaults->type == type)
		return -1;
	return 0;
}

static uint64_t _get_default(list_t *job_defaults_list, uint16_t type)
{
	uint64_t def_value = NO_VAL64;
	job_defaults_t *job_defaults;

	if (!job_defaults_list)
		return def_value;

	if ((job_defaults = list_find_first(job_defaults_list,
					    _foreach_get_default, &type)))
		def_value = job_defaults->value;

	return def_value;
}

/*
 * Get configured DefCpuPerGPU information from a list
 * (either global or per partition list)
 * Returns NO_VAL64 if configuration parameter not set
 */
extern uint64_t slurm_get_def_cpu_per_gpu(list_t *job_defaults_list)
{
	return _get_default(job_defaults_list, JOB_DEF_CPU_PER_GPU);
}

/*
 * Get configured DefMemPerGPU information from a list
 * (either global or per partition list)
 * Returns NO_VAL64 if configuration parameter not set
 */
extern uint64_t slurm_get_def_mem_per_gpu(list_t *job_defaults_list)
{
	return _get_default(job_defaults_list, JOB_DEF_MEM_PER_GPU);
}
