/*****************************************************************************\
 *  read_config.c - read the overall slurm configuration file
 *
 *  $Id$
 *****************************************************************************
 *  Copyright (C) 2002-2006 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Morris Jette <jette1@llnl.gov>.
 *  UCRL-CODE-217948.
 *  
 *  This file is part of SLURM, a resource management program.
 *  For details, see <http://www.llnl.gov/linux/slurm/>.
 *  
 *  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.
 *  
 *  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.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
\*****************************************************************************/

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>

#include <slurm/slurm.h>

#include "src/common/hostlist.h"
#include "src/common/slurm_protocol_defs.h"
#include "src/common/slurm_protocol_api.h"
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/parse_spec.h"
#include "src/common/read_config.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/common/slurm_rlimits_info.h"
#include "src/common/parse_config.h"

/* Instantiation of the "extern slurm_ctl_conf_t slurmcltd_conf"
 * found in slurmctld.h */
slurm_ctl_conf_t slurmctld_conf;

static pthread_mutex_t conf_lock = PTHREAD_MUTEX_INITIALIZER;
static s_p_hashtbl_t *conf_hashtbl = NULL;
static slurm_ctl_conf_t *conf_ptr = &slurmctld_conf;
static bool conf_initialized = false;

/*
 * FIXME - If we eliminate the SlurmdPort option altogether, then
 *         default_slurmd_port and parse_slurmd_port can
 *         be removed.
 */
static uint16_t default_slurmd_port;
static int parse_slurmd_port(void **dest, slurm_parser_enum_t type,
			     const char *key, const char *value,
			     const char *line, char **leftover);

static s_p_hashtbl_t *default_nodename_tbl;
static s_p_hashtbl_t *default_partition_tbl;

inline static void _normalize_debug_level(uint16_t *level);
static void _init_slurm_conf(char *file_name);

#define NAME_HASH_LEN 512
typedef struct names_ll_s {
	char *alias;	/* NodeName */
	char *hostname;	/* NodeHostname */
	char *address;	/* NodeAddr */
	uint16_t port;
	slurm_addr addr;
	bool addr_initialized;
	struct names_ll_s *next_alias;
	struct names_ll_s *next_hostname;
} names_ll_t;
bool nodehash_initialized = false;
static names_ll_t *host_to_node_hashtbl[NAME_HASH_LEN] = {NULL};
static names_ll_t *node_to_host_hashtbl[NAME_HASH_LEN] = {NULL};

static int parse_nodename(void **dest, slurm_parser_enum_t type,
			  const char *key, const char *value,
			  const char *line, char **leftover);
static void destroy_nodename(void *ptr);
static int parse_partitionname(void **dest, slurm_parser_enum_t type,
			       const char *key, const char *value,
			       const char *line, char **leftover);
static void destroy_partitionname(void *ptr);
static int parse_downnodes(void **dest, slurm_parser_enum_t type,
			   const char *key, const char *value,
			   const char *line, char **leftover);
static void destroy_downnodes(void *ptr);
static int defunct_option(void **dest, slurm_parser_enum_t type,
			  const char *key, const char *value,
			  const char *line, char **leftover);
static void validate_and_set_defaults(slurm_ctl_conf_t *conf,
				      s_p_hashtbl_t *hashtbl);

s_p_options_t slurm_conf_options[] = {
	{"AuthType", S_P_STRING},
	{"CheckpointType", S_P_STRING},
	{"CacheGroups", S_P_UINT16},
	{"BackupAddr", S_P_STRING},
	{"BackupController", S_P_STRING},
	{"ControlAddr", S_P_STRING},
	{"ControlMachine", S_P_STRING},
	{"Epilog", S_P_STRING},
	{"FastSchedule", S_P_UINT16},
	{"FirstJobId", S_P_UINT32},
	{"HashBase", S_P_LONG, defunct_option},
	{"HeartbeatInterval", S_P_LONG, defunct_option},
	{"InactiveLimit", S_P_UINT16},
	{"JobAcctLogFile", S_P_STRING},
	{"JobAcctFrequency", S_P_UINT16},
	{"JobAcctType", S_P_STRING},
	{"JobCompLoc", S_P_STRING},
	{"JobCompType", S_P_STRING},
	{"JobCredentialPrivateKey", S_P_STRING},
	{"JobCredentialPublicCertificate", S_P_STRING},
	{"KillTree", S_P_UINT16, defunct_option},
	{"KillWait", S_P_UINT16},
	{"MaxJobCount", S_P_UINT16},
	{"MessageTimeout", S_P_UINT16},
	{"MinJobAge", S_P_UINT16},
	{"MpichGmDirectSupport", S_P_LONG},
	{"MpiDefault", S_P_STRING},
	{"PluginDir", S_P_STRING},
	{"PlugStackConfig", S_P_STRING},
	{"ProctrackType", S_P_STRING},
	{"Prolog", S_P_STRING},
	{"PropagatePrioProcess", S_P_UINT16},
	{"PropagateResourceLimitsExcept", S_P_STRING},
	{"PropagateResourceLimits", S_P_STRING},
	{"ReturnToService", S_P_UINT16},
	{"SchedulerAuth", S_P_STRING},
	{"SchedulerPort", S_P_UINT16},
	{"SchedulerRootFilter", S_P_UINT16},
	{"SchedulerType", S_P_STRING},
	{"SelectType", S_P_STRING},
	{"SlurmUser", S_P_STRING},
	{"SlurmctldDebug", S_P_UINT16},
	{"SlurmctldLogFile", S_P_STRING},
	{"SlurmctldPidFile", S_P_STRING},
	{"SlurmctldPort", S_P_UINT32},
	{"SlurmctldTimeout", S_P_UINT16},
	{"SlurmdDebug", S_P_UINT16},
	{"SlurmdLogFile", S_P_STRING},
	{"SlurmdPidFile",  S_P_STRING},
	{"SlurmdPort", S_P_UINT32, parse_slurmd_port},
	{"SlurmdSpoolDir", S_P_STRING},
	{"SlurmdTimeout", S_P_UINT16},
	{"SrunEpilog", S_P_STRING},
	{"SrunProlog", S_P_STRING},
	{"StateSaveLocation", S_P_STRING},
	{"SwitchType", S_P_STRING},
	{"TaskEpilog", S_P_STRING},
	{"TaskProlog", S_P_STRING},
	{"TaskPlugin", S_P_STRING},
	{"TmpFS", S_P_STRING},
	{"TreeWidth", S_P_UINT16},
	{"UsePAM", S_P_BOOLEAN},
	{"WaitTime", S_P_UINT16},

	{"NodeName", S_P_ARRAY, parse_nodename, destroy_nodename},
	{"PartitionName", S_P_ARRAY, parse_partitionname, destroy_partitionname},
	{"DownNodes", S_P_ARRAY, parse_downnodes, destroy_downnodes},

	{NULL}
};


/*
 * This function works almost exactly the same as the
 * default S_P_UINT32 handler, except that it also sets the
 * global variable default_slurmd_port.
 */
static int parse_slurmd_port(void **dest, slurm_parser_enum_t type,
			     const char *key, const char *value,
			     const char *line, char **leftover)
{
	char *endptr;
	unsigned long num;
	uint32_t *ptr;

	errno = 0;
	num = strtoul(value, &endptr, 0);
	if ((num == 0 && errno == EINVAL)
	    || (*endptr != '\0')) {
		error("%s value (%s) is not a valid number", key, value);
		return -1;
	} else if (errno == ERANGE) {
		error("%s value (%s) is out of range", key, value);
		return -1;
	} else if (num < 0) {
		error("value %s (%s) is less than zero", key, value);
		return -1;
	} else if (num > 0xffffffff) {
		error("%s value (%s) is greater than 4294967295", 
			key, value);
		return -1;
	}

	default_slurmd_port = (uint32_t)num;

	ptr = (uint32_t *)xmalloc(sizeof(uint32_t));
	*ptr = (uint32_t)num;
	*dest = (void *)ptr;

	return 1;
}

static int defunct_option(void **dest, slurm_parser_enum_t type,
			  const char *key, const char *value,
			  const char *line, char **leftover)
{
	error("The option \"%s\" is defunct, see man slurm.conf.", key);
	return 0;
}

static int parse_nodename(void **dest, slurm_parser_enum_t type,
			  const char *key, const char *value,
			  const char *line, char **leftover)
{
	s_p_hashtbl_t *tbl, *dflt;
	slurm_conf_node_t *n;
	static s_p_options_t _nodename_options[] = {
		{"NodeHostname", S_P_STRING},
		{"NodeAddr", S_P_STRING},
		{"Feature", S_P_STRING},
		{"Port", S_P_UINT16},
		{"Procs", S_P_UINT32},
		{"RealMemory", S_P_UINT32},
		{"Reason", S_P_STRING},
		{"State", S_P_STRING},
		{"TmpDisk", S_P_UINT32},
		{"Weight", S_P_UINT32},
		{NULL}
	};

	tbl = s_p_hashtbl_create(_nodename_options);
	s_p_parse_line(tbl, *leftover, leftover);
	/* s_p_dump_values(tbl, _nodename_options); */

	if (strcasecmp(value, "DEFAULT") == 0) {
		char *tmp;
		if (s_p_get_string(&tmp, "NodeHostname", tbl)) {
			error("NodeHostname not allowed with NodeName=DEFAULT");
			xfree(tmp);
			s_p_hashtbl_destroy(tbl);
			return -1;
		}
		if (s_p_get_string(&tmp, "NodeAddr", tbl)) {
			error("NodeAddr not allowed with NodeName=DEFAULT");
			xfree(tmp);
			s_p_hashtbl_destroy(tbl);
			return -1;
		}

		if (default_nodename_tbl != NULL)
			s_p_hashtbl_destroy(default_nodename_tbl);
		default_nodename_tbl = tbl;

		return 0;
	} else {
		n = xmalloc(sizeof(slurm_conf_node_t));
		dflt = default_nodename_tbl;

		n->nodenames = xstrdup(value);
		if (!s_p_get_string(&n->hostnames, "NodeHostname", tbl))
			n->hostnames = xstrdup(n->nodenames);
		if (!s_p_get_string(&n->addresses, "NodeAddr", tbl))
			n->addresses = xstrdup(n->hostnames);

		if (!s_p_get_string(&n->feature, "Feature", tbl))
			s_p_get_string(&n->feature, "Feature", dflt);

		if (!s_p_get_uint16(&n->port, "Port", tbl)
		    && !s_p_get_uint16(&n->port, "Port", dflt)) {
			if (default_slurmd_port != 0)
				n->port = default_slurmd_port;
			else
				n->port = SLURMD_PORT;
		}

		if (!s_p_get_uint32(&n->cpus, "Procs", tbl)
		    && !s_p_get_uint32(&n->cpus, "Procs", dflt))
			n->cpus = 1;

		if (!s_p_get_uint32(&n->real_memory, "RealMemory", tbl)
		    && !s_p_get_uint32(&n->real_memory, "RealMemory", dflt))
			n->real_memory = 1;

		if (!s_p_get_string(&n->reason, "Reason", tbl))
			s_p_get_string(&n->reason, "Reason", dflt);

		if (!s_p_get_string(&n->state, "State", tbl)
		    && !s_p_get_string(&n->state, "State", dflt))
			n->state = NULL;

		if (!s_p_get_uint32(&n->tmp_disk, "TmpDisk", tbl)
		    && !s_p_get_uint32(&n->tmp_disk, "TmpDisk", dflt))
			n->tmp_disk = 1;

		if (!s_p_get_uint32(&n->weight, "Weight", tbl)
		    && !s_p_get_uint32(&n->weight, "Weight", dflt))
			n->weight = 1;

		s_p_hashtbl_destroy(tbl);

		*dest = (void *)n;

		return 1;
	}

	/* should not get here */
}

static void destroy_nodename(void *ptr)
{
	slurm_conf_node_t *n = (slurm_conf_node_t *)ptr;
	xfree(n->nodenames);
	xfree(n->hostnames);
	xfree(n->addresses);
	xfree(n->feature);
	xfree(n->reason);
	xfree(n->state);
	xfree(ptr);
}

int slurm_conf_nodename_array(slurm_conf_node_t **ptr_array[])
{
	int count;
	slurm_conf_node_t **ptr;

	if (s_p_get_array((void ***)&ptr, &count, "NodeName", conf_hashtbl)) {
		*ptr_array = ptr;
		return count;
	} else {
		*ptr_array = NULL;
		return 0;
	}
}

static int parse_partitionname(void **dest, slurm_parser_enum_t type,
			       const char *key, const char *value,
			       const char *line, char **leftover)
{
	s_p_hashtbl_t *tbl, *dflt;
	slurm_conf_partition_t *p;
	char *tmp = NULL;
	static s_p_options_t _partition_options[] = {
		{"AllowGroups", S_P_STRING},
		{"Default", S_P_BOOLEAN}, /* YES or NO */
		{"Hidden", S_P_BOOLEAN}, /* YES or NO */
		{"MaxTime", S_P_UINT32}, /* INFINITE or a number */
		{"MaxNodes", S_P_UINT32}, /* INFINITE or a number */
		{"MinNodes", S_P_UINT32},
		{"Nodes", S_P_STRING},
		{"RootOnly", S_P_BOOLEAN}, /* YES or NO */
		{"Shared", S_P_STRING}, /* YES, NO, or FORCE */
		{"State", S_P_BOOLEAN}, /* UP or DOWN */
		{NULL}
	};


	tbl = s_p_hashtbl_create(_partition_options);
	s_p_parse_line(tbl, *leftover, leftover);
	/* s_p_dump_values(tbl, _partition_options); */

	if (strcasecmp(value, "DEFAULT") == 0) {
		if (default_partition_tbl != NULL)
			s_p_hashtbl_destroy(default_partition_tbl);
		default_partition_tbl = tbl;

		return 0;
	} else {
		p = xmalloc(sizeof(slurm_conf_partition_t));
		dflt = default_partition_tbl;

		p->name = xstrdup(value);

		if (!s_p_get_string(&p->allow_groups, "AllowGroups", tbl))
			s_p_get_string(&p->allow_groups, "AllowGroups", dflt);
		if (p->allow_groups && strcasecmp(p->allow_groups, "ALL")==0) {
			xfree(p->allow_groups);
			p->allow_groups = NULL; /* NULL means allow all */
		}

		if (!s_p_get_boolean(&p->default_flag, "Default", tbl)
		    && !s_p_get_boolean(&p->default_flag, "Default", dflt))
			p->default_flag = false;

		if (!s_p_get_boolean(&p->hidden_flag, "Hidden", tbl)
		    && !s_p_get_boolean(&p->hidden_flag, "Hidden", dflt))
			p->hidden_flag = false;

		if (!s_p_get_uint32(&p->max_time, "MaxTime", tbl)
		    && !s_p_get_uint32(&p->max_time, "MaxTime", dflt))
			p->max_time = INFINITE;

		if (!s_p_get_uint32(&p->max_nodes, "MaxNodes", tbl)
		    && !s_p_get_uint32(&p->max_nodes, "MaxNodes", dflt))
			p->max_nodes = INFINITE;

		if (!s_p_get_uint32(&p->min_nodes, "MinNodes", tbl)
		    && !s_p_get_uint32(&p->min_nodes, "MinNodes", dflt))
			p->min_nodes = 1;

		if (!s_p_get_string(&p->nodes, "Nodes", tbl)
		    && !s_p_get_string(&p->nodes, "Nodes", dflt))
			p->nodes = NULL;

		if (!s_p_get_boolean(&p->root_only_flag, "RootOnly", tbl)
		    && !s_p_get_boolean(&p->root_only_flag, "RootOnly", dflt))
			p->root_only_flag = false;

		if (!s_p_get_string(&tmp, "Shared", tbl)
		    && !s_p_get_string(&tmp, "Shared", dflt)) {
			p->shared = SHARED_NO;
		} else {
			if (strcasecmp(tmp, "NO") == 0)
				p->shared = SHARED_NO;
#ifndef HAVE_XCPU
			/* Only "Shared=NO" is valid on XCPU systems */
			else if (strcasecmp(tmp, "YES") == 0)
				p->shared = SHARED_YES;
			else if (strcasecmp(tmp, "FORCE") == 0)
				p->shared = SHARED_FORCE;
#endif
			else {
				error("Bad value \"%s\" for Shared", tmp);
				destroy_partitionname(p);
				s_p_hashtbl_destroy(tbl);
				xfree(tmp);
				return -1;
			}
		}
		xfree(tmp);

		if (!s_p_get_boolean(&p->state_up_flag, "State", tbl)
		    && !s_p_get_boolean(&p->state_up_flag, "State", dflt))
			p->state_up_flag = true;

		s_p_hashtbl_destroy(tbl);

		*dest = (void *)p;

		return 1;
	}

	/* should not get here */
}

static void destroy_partitionname(void *ptr)
{
	slurm_conf_partition_t *p = (slurm_conf_partition_t *)ptr;

	xfree(p->name);
	xfree(p->nodes);
	xfree(p->allow_groups);
	xfree(ptr);
}

int slurm_conf_partition_array(slurm_conf_partition_t **ptr_array[])
{
	int count;
	slurm_conf_partition_t **ptr;

	if (s_p_get_array((void ***)&ptr, &count, "PartitionName",
			  conf_hashtbl)) {
		*ptr_array = ptr;
		return count;
	} else {
		*ptr_array = NULL;
		return 0;
	}
}

static int parse_downnodes(void **dest, slurm_parser_enum_t type,
			   const char *key, const char *value,
			   const char *line, char **leftover)
{
	s_p_hashtbl_t *tbl, *dflt;
	slurm_conf_downnodes_t *n;
	static s_p_options_t _downnodes_options[] = {
		{"Reason", S_P_STRING},
		{"State", S_P_STRING},
		{NULL}
	};

	tbl = s_p_hashtbl_create(_downnodes_options);
	s_p_parse_line(tbl, *leftover, leftover);
	/* s_p_dump_values(tbl, _downnodes_options); */

	n = xmalloc(sizeof(slurm_conf_node_t));
	dflt = default_nodename_tbl;

	n->nodenames = xstrdup(value);

	if (!s_p_get_string(&n->reason, "Reason", tbl))
		n->reason = xstrdup("Set in slurm.conf");

	if (!s_p_get_string(&n->state, "State", tbl))
		n->state = NULL;

	s_p_hashtbl_destroy(tbl);

	*dest = (void *)n;

	return 1;
}

static void destroy_downnodes(void *ptr)
{
	slurm_conf_downnodes_t *n = (slurm_conf_downnodes_t *)ptr;
	xfree(n->nodenames);
	xfree(n->reason);
	xfree(n->state);
	xfree(ptr);
}

int slurm_conf_downnodes_array(slurm_conf_downnodes_t **ptr_array[])
{
	int count;
	slurm_conf_downnodes_t **ptr;

	if (s_p_get_array((void ***)&ptr, &count, "DownNodes", conf_hashtbl)) {
		*ptr_array = ptr;
		return count;
	} else {
		*ptr_array = NULL;
		return 0;
	}
}

static void _free_name_hashtbl()
{
	int i;
	names_ll_t *p, *q;

	for (i=0; i<NAME_HASH_LEN; i++) {
		p = node_to_host_hashtbl[i];
		while (p) {
			xfree(p->alias);
			xfree(p->hostname);
			xfree(p->address);
			q = p->next_alias;
			xfree(p);
			p = q;
		}
		node_to_host_hashtbl[i] = NULL;
		host_to_node_hashtbl[i] = NULL;
	}
	nodehash_initialized = false;
}

static void _init_name_hashtbl()
{
	return;
}

static int _get_hash_idx(const char *s)
{
	int i;

	i = 0;
	while (*s) i += (int)*s++;
	return i % NAME_HASH_LEN;
}

static void _push_to_hashtbls(char *alias, char *hostname,
			      char *address, uint16_t port)
{
	int hostname_idx, alias_idx;
	names_ll_t *p, *new;

	alias_idx = _get_hash_idx(alias);
	hostname_idx = _get_hash_idx(hostname);

#if !defined(HAVE_FRONT_END) && !defined(MULTIPLE_SLURMD)
	/* Ensure only one slurmd configured on each host */
	p = host_to_node_hashtbl[hostname_idx];
	while (p) {
		if (strcmp(p->hostname, hostname)==0) {
			error("Duplicated NodeHostname %s in the config file",
			      hostname);
			return;
		}
		p = p->next_hostname;
	}
#endif
	/* Ensure only one instance of each NodeName */
	p = node_to_host_hashtbl[alias_idx];
	while (p) {
		if (strcmp(p->alias, alias)==0) {
			fatal("Duplicated NodeName %s in the config file",
			      p->alias);
			return;
		}
		p = p->next_alias;
	}

	/* Create the new data structure and link it into the hash tables */
	new = (names_ll_t *)xmalloc(sizeof(*new));
	new->alias = xstrdup(alias);
	new->hostname = xstrdup(hostname);
	new->address = xstrdup(address);
	new->port = port;
	new->addr_initialized = false;
	new->next_hostname = host_to_node_hashtbl[hostname_idx];
	host_to_node_hashtbl[hostname_idx] = new;
	new->next_alias = node_to_host_hashtbl[alias_idx];
	node_to_host_hashtbl[alias_idx] = new;
}

/*
 * Register the given NodeName in the alias table.
 * If node_hostname is NULL, only node_name will be used and 
 * no lookup table record is created.
 */
static int _register_conf_node_aliases(slurm_conf_node_t *node_ptr)
{
	hostlist_t alias_list = NULL;
	hostlist_t hostname_list = NULL;
	hostlist_t address_list = NULL;
	char *alias = NULL;
	char *hostname = NULL;
	char *address = NULL;
	int error_code = SLURM_SUCCESS;

	if (node_ptr->nodenames == NULL || *node_ptr->nodenames == '\0')
		return -1;

	if ((alias_list = hostlist_create(node_ptr->nodenames)) == NULL) {
		error("Unable to create NodeName list from %s",
		      node_ptr->nodenames);
		error_code = errno;
		goto cleanup;
	}
	if ((hostname_list = hostlist_create(node_ptr->hostnames)) == NULL) {
		error("Unable to create NodeHostname list from %s",
		      node_ptr->hostnames);
		error_code = errno;
		goto cleanup;
	}
	if ((address_list = hostlist_create(node_ptr->addresses)) == NULL) {
		error("Unable to create NodeAddr list from %s",
		      node_ptr->addresses);
		error_code = errno;
		goto cleanup;
	}

	/* some sanity checks */
#ifdef HAVE_FRONT_END
	if (hostlist_count(hostname_list) != 1
	    || hostlist_count(address_list) != 1) {
		error("Only one hostname and address allowed "
		      "in FRONT_END mode");
		goto cleanup;
	}
	hostname = node_ptr->hostnames;
	address = node_ptr->addresses;
#else
	if (hostlist_count(hostname_list) < hostlist_count(alias_list)) {
		error("At least as many NodeHostname are required "
		      "as NodeName");
		goto cleanup;
	}
	if (hostlist_count(address_list) < hostlist_count(alias_list)) {
		error("At least as many NodeAddr are required as NodeName");
		goto cleanup;
	}
#endif

	/* now build the individual node structures */
	while ((alias = hostlist_shift(alias_list))) {
#ifndef HAVE_FRONT_END
		hostname = hostlist_shift(hostname_list);
		address = hostlist_shift(address_list);
#endif

		_push_to_hashtbls(alias, hostname, address, node_ptr->port);

		free(alias);
#ifndef HAVE_FRONT_END
		free(hostname);
		free(address);
#endif
	}

	/* free allocated storage */
cleanup:
	if (alias_list)
		hostlist_destroy(alias_list);
	if (hostname_list)
		hostlist_destroy(hostname_list);
	if (address_list)
		hostlist_destroy(address_list);
	return error_code;
}

static void _init_slurmd_nodehash(void)
{
	slurm_conf_node_t **ptr_array;
	int count;
	int i;

	if (nodehash_initialized)
		return;
	else
		nodehash_initialized = true;

	if(!conf_initialized) 
		_init_slurm_conf(NULL);

	count = slurm_conf_nodename_array(&ptr_array);
	if (count == 0) {
		return;
	}

	for (i = 0; i < count; i++) {
		_register_conf_node_aliases(ptr_array[i]);
	}
}

extern void slurm_conf_nodehash_init(void)
{
	slurm_conf_lock();
	_init_slurmd_nodehash();
	slurm_conf_unlock();
}


/*
 * slurm_conf_get_hostname - Return the NodeHostname for given NodeName
 */
extern char *slurm_conf_get_hostname(const char *node_name)
{
	int idx;
	names_ll_t *p;

	slurm_conf_lock();
	_init_slurmd_nodehash();

	idx = _get_hash_idx(node_name);
	p = node_to_host_hashtbl[idx];
	while (p) {
		if (strcmp(p->alias, node_name) == 0) {
			char *hostname = xstrdup(p->hostname);
			slurm_conf_unlock();
			return hostname;
		}
		p = p->next_alias;
	}
	slurm_conf_unlock();

	return NULL;
}

/*
 * slurm_conf_get_nodename - Return the NodeName for given NodeHostname
 */
extern char *slurm_conf_get_nodename(const char *node_hostname)
{
	int idx;
	names_ll_t *p;

	slurm_conf_lock();
	_init_slurmd_nodehash();

	idx = _get_hash_idx(node_hostname);
	p = host_to_node_hashtbl[idx];
	while (p) {
		if (strcmp(p->hostname, node_hostname) == 0) {
			char *alias = xstrdup(p->alias);
			slurm_conf_unlock();
			return alias;
		}
		p = p->next_hostname;
	}
	slurm_conf_unlock();

	return NULL;
}

/*
 * slurm_conf_get_port - Return the port for a given NodeName
 */
extern uint16_t slurm_conf_get_port(const char *node_name)
{
	int idx;
	names_ll_t *p;

	slurm_conf_lock();
	_init_slurmd_nodehash();

	idx = _get_hash_idx(node_name);
	p = node_to_host_hashtbl[idx];
	while (p) {
		if (strcmp(p->alias, node_name) == 0) {
			uint16_t port = p->port;
			slurm_conf_unlock();
			return port;
		}
		p = p->next_alias;
	}
	slurm_conf_unlock();

	return 0;
}

/*
 * slurm_conf_get_addr - Return the slurm_addr for a given NodeName
 * Returns SLURM_SUCCESS on success, SLURM_FAILURE on failure.
 */
extern int slurm_conf_get_addr(const char *node_name, slurm_addr *address)
{
	int idx;
	names_ll_t *p;

	slurm_conf_lock();
	_init_slurmd_nodehash();

	idx = _get_hash_idx(node_name);
	p = node_to_host_hashtbl[idx];
	while (p) {
		if (strcmp(p->alias, node_name) == 0) {
			if (!p->addr_initialized) {
				slurm_set_addr(&p->addr, p->port, p->address);
				p->addr_initialized = true;
			}
			*address = p->addr;
			slurm_conf_unlock();
			return SLURM_SUCCESS;
		}
		p = p->next_alias;
	}
	slurm_conf_unlock();

	return SLURM_FAILURE;
}


/* getnodename - equivalent to gethostname, but return only the first 
 * component of the fully qualified name 
 * (e.g. "linux123.foo.bar" becomes "linux123") 
 * OUT name
 */
int
getnodename (char *name, size_t len)
{
	int error_code, name_len;
	char *dot_ptr, path_name[1024];

	error_code = gethostname (path_name, sizeof(path_name));
	if (error_code)
		return error_code;

	dot_ptr = strchr (path_name, '.');
	if (dot_ptr == NULL)
		dot_ptr = path_name + strlen(path_name);
	else
		dot_ptr[0] = '\0';

	name_len = (dot_ptr - path_name);
	if (name_len > len)
		return ENAMETOOLONG;

	strcpy (name, path_name);
	return 0;
}

/* 
 * free_slurm_conf - free all storage associated with a slurm_ctl_conf_t.   
 * IN/OUT ctl_conf_ptr - pointer to data structure to be freed
 */
void
free_slurm_conf (slurm_ctl_conf_t *ctl_conf_ptr)
{
	xfree (ctl_conf_ptr->authtype);
	xfree (ctl_conf_ptr->checkpoint_type);
	xfree (ctl_conf_ptr->backup_addr);
	xfree (ctl_conf_ptr->backup_controller);
	xfree (ctl_conf_ptr->control_addr);
	xfree (ctl_conf_ptr->control_machine);
	xfree (ctl_conf_ptr->epilog);
	xfree (ctl_conf_ptr->job_acct_logfile);
	xfree (ctl_conf_ptr->job_acct_type);
	xfree (ctl_conf_ptr->job_comp_loc);
	xfree (ctl_conf_ptr->job_comp_type);
	xfree (ctl_conf_ptr->job_credential_private_key);
	xfree (ctl_conf_ptr->job_credential_public_certificate);
	xfree (ctl_conf_ptr->mpi_default);
	xfree (ctl_conf_ptr->plugindir);
	xfree (ctl_conf_ptr->plugstack);
	xfree (ctl_conf_ptr->proctrack_type);
	xfree (ctl_conf_ptr->prolog);
	xfree (ctl_conf_ptr->propagate_rlimits_except);
	xfree (ctl_conf_ptr->propagate_rlimits);
	xfree (ctl_conf_ptr->schedauth);
	xfree (ctl_conf_ptr->schedtype);
	xfree (ctl_conf_ptr->select_type);
	xfree (ctl_conf_ptr->slurm_conf);
	xfree (ctl_conf_ptr->slurm_user_name);
	xfree (ctl_conf_ptr->slurmctld_logfile);
	xfree (ctl_conf_ptr->slurmctld_pidfile);
	xfree (ctl_conf_ptr->slurmd_logfile);
	xfree (ctl_conf_ptr->slurmd_pidfile);
	xfree (ctl_conf_ptr->slurmd_spooldir);
	xfree (ctl_conf_ptr->state_save_location);
	xfree (ctl_conf_ptr->switch_type);
	xfree (ctl_conf_ptr->tmp_fs);
	xfree (ctl_conf_ptr->task_epilog);
	xfree (ctl_conf_ptr->task_prolog);
	xfree (ctl_conf_ptr->task_plugin);
	xfree (ctl_conf_ptr->tmp_fs);
	xfree (ctl_conf_ptr->srun_prolog);
	xfree (ctl_conf_ptr->srun_epilog);
	xfree (ctl_conf_ptr->node_prefix);
	
	_free_name_hashtbl();
}

/* 
 * init_slurm_conf - initialize or re-initialize the slurm configuration 
 *	values to defaults (NULL or NO_VAL). Note that the configuration
 *	file pathname (slurm_conf) is not changed.    
 * IN/OUT ctl_conf_ptr - pointer to data structure to be initialized
 */
void
init_slurm_conf (slurm_ctl_conf_t *ctl_conf_ptr)
{
	ctl_conf_ptr->last_update		= time(NULL);
	xfree (ctl_conf_ptr->authtype);
	ctl_conf_ptr->cache_groups		= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->checkpoint_type);
	xfree (ctl_conf_ptr->backup_addr);
	xfree (ctl_conf_ptr->backup_controller);
	xfree (ctl_conf_ptr->control_addr);
	xfree (ctl_conf_ptr->control_machine);
	xfree (ctl_conf_ptr->epilog);
	ctl_conf_ptr->fast_schedule		= (uint16_t) NO_VAL;
	ctl_conf_ptr->first_job_id		= (uint32_t) NO_VAL;
	ctl_conf_ptr->inactive_limit		= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->job_acct_logfile);
	ctl_conf_ptr->job_acct_freq             = 0;
	xfree (ctl_conf_ptr->job_acct_type);
	xfree (ctl_conf_ptr->job_comp_loc);
	xfree (ctl_conf_ptr->job_comp_type);
	xfree (ctl_conf_ptr->job_credential_private_key);
	xfree (ctl_conf_ptr->job_credential_public_certificate);
	ctl_conf_ptr->kill_wait			= (uint16_t) NO_VAL;
	ctl_conf_ptr->max_job_cnt		= (uint16_t) NO_VAL;
	ctl_conf_ptr->min_job_age		= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->mpi_default);
	ctl_conf_ptr->msg_timeout		= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->plugindir);
	xfree (ctl_conf_ptr->plugstack);
	xfree (ctl_conf_ptr->proctrack_type);
	xfree (ctl_conf_ptr->prolog);
	ctl_conf_ptr->propagate_prio_process	= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->propagate_rlimits_except);
	xfree (ctl_conf_ptr->propagate_rlimits);
	ctl_conf_ptr->ret2service		= (uint16_t) NO_VAL;
	xfree( ctl_conf_ptr->schedauth );
	ctl_conf_ptr->schedport			= (uint16_t) NO_VAL;
	ctl_conf_ptr->schedrootfltr		= (uint16_t) NO_VAL;
	xfree( ctl_conf_ptr->schedtype );
	xfree( ctl_conf_ptr->select_type );
	ctl_conf_ptr->slurm_user_id		= (uint16_t) NO_VAL; 
	xfree (ctl_conf_ptr->slurm_user_name);
	ctl_conf_ptr->slurmctld_debug		= (uint16_t) NO_VAL; 
	xfree (ctl_conf_ptr->slurmctld_logfile);
	xfree (ctl_conf_ptr->slurmctld_pidfile);
	ctl_conf_ptr->slurmctld_port		= (uint32_t) NO_VAL;
	ctl_conf_ptr->slurmctld_timeout		= (uint16_t) NO_VAL;
	ctl_conf_ptr->slurmd_debug		= (uint16_t) NO_VAL; 
	xfree (ctl_conf_ptr->slurmd_logfile);
	xfree (ctl_conf_ptr->slurmd_pidfile);
 	ctl_conf_ptr->slurmd_port		= (uint32_t) NO_VAL;
	xfree (ctl_conf_ptr->slurmd_spooldir);
	ctl_conf_ptr->slurmd_timeout		= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->state_save_location);
	xfree (ctl_conf_ptr->switch_type);
	xfree (ctl_conf_ptr->task_epilog);
	xfree (ctl_conf_ptr->task_prolog);
	xfree (ctl_conf_ptr->task_plugin);
	xfree (ctl_conf_ptr->tmp_fs);
	ctl_conf_ptr->wait_time			= (uint16_t) NO_VAL;
	xfree (ctl_conf_ptr->srun_prolog);
	xfree (ctl_conf_ptr->srun_epilog);
	xfree (ctl_conf_ptr->node_prefix);
	ctl_conf_ptr->tree_width       		= (uint16_t) NO_VAL;
	ctl_conf_ptr->use_pam			= 0;
	
	_free_name_hashtbl();
	_init_name_hashtbl();

	return;
}

/* caller must lock conf_lock */
static void
_init_slurm_conf(char *file_name)
{
	/* conf_ptr = (slurm_ctl_conf_t *)xmalloc(sizeof(slurm_ctl_conf_t)); */
	default_slurmd_port = 0;

	if (file_name == NULL) {
		file_name = getenv("SLURM_CONF");
		if (file_name == NULL)
			file_name = default_slurm_config_file;
	}
       	if(conf_initialized) {
		error("the conf_hashtbl is already inited");	
	}
	conf_hashtbl = s_p_hashtbl_create(slurm_conf_options);
	conf_ptr->last_update = time(NULL);
	if(s_p_parse_file(conf_hashtbl, file_name) == SLURM_ERROR)
		fatal("something wrong with opening/reading conf file");
	/* s_p_dump_values(conf_hashtbl, slurm_conf_options); */
	validate_and_set_defaults(conf_ptr, conf_hashtbl);
	conf_ptr->slurm_conf = xstrdup(file_name);
}

/* caller must lock conf_lock */
static void
_destroy_slurm_conf()
{
	s_p_hashtbl_destroy(conf_hashtbl);
	if (default_nodename_tbl != NULL) {
		s_p_hashtbl_destroy(default_nodename_tbl);
		default_nodename_tbl = NULL;
	}
	if (default_partition_tbl != NULL) {
		s_p_hashtbl_destroy(default_partition_tbl);
		default_partition_tbl = NULL;
	}
	free_slurm_conf(conf_ptr);
	conf_initialized = false;
	
	/* xfree(conf_ptr); */
}

/*
 * slurm_conf_init - load the slurm configuration from the a file.
 * IN file_name - name of the slurm configuration file to be read
 *	If file_name is NULL, then this routine tries to use
 *	the value in the SLURM_CONF env variable.  Failing that,
 *	it uses the compiled-in default file name.
 *	If the conf structures have already been initialized by a call to
 *	slurm_conf_init, any subsequent calls will do nothing until
 *	slurm_conf_destroy is called.
 * RET SLURM_SUCCESS if conf file is initialized.  If the slurm conf
 *       was already initialied, return SLURM_ERROR.
 */
extern int
slurm_conf_init(char *file_name)
{
	pthread_mutex_lock(&conf_lock);

	if (conf_initialized) {
		pthread_mutex_unlock(&conf_lock);
		return SLURM_ERROR;
	}

	_init_slurm_conf(file_name);
	conf_initialized = true;

	pthread_mutex_unlock(&conf_lock);
	return SLURM_SUCCESS;
}

/*
 * slurm_conf_reinit - reload the slurm configuration from a file.
 * IN file_name - name of the slurm configuration file to be read
 *	If file_name is NULL, then this routine tries to use
 *	the value in the SLURM_CONF env variable.  Failing that,
 *	it uses the compiled-in default file name.
 *	Unlike slurm_conf_init, slurm_conf_reinit will always reread the
 *	file and reinitialize the configuration structures.
 * RET SLURM_SUCCESS if conf file is reinitialized, otherwise SLURM_ERROR.
 */
extern int 
slurm_conf_reinit(char *file_name)
{
	pthread_mutex_lock(&conf_lock);

	if (file_name == NULL) {
		file_name = getenv("SLURM_CONF");
		if (file_name == NULL)
			file_name = default_slurm_config_file;
	}

	if (conf_initialized) {
		/* could check modified time on slurm.conf here */
		_destroy_slurm_conf();
	}
	
	_init_slurm_conf(file_name);
	
	conf_initialized = true;

	pthread_mutex_unlock(&conf_lock);
	return SLURM_SUCCESS;

}

extern void
slurm_conf_mutex_init(void)
{
	pthread_mutex_init(&conf_lock, NULL);
}

extern void 
slurm_conf_install_fork_handlers()
{
	int err;
	if ((err = pthread_atfork(NULL, NULL, &slurm_conf_mutex_init)))
		fatal("can't install slurm_conf atfork handler");
	return;
}

extern int
slurm_conf_destroy(void)
{
	pthread_mutex_lock(&conf_lock);

	if (!conf_initialized) {
		pthread_mutex_unlock(&conf_lock);
		return SLURM_SUCCESS;
	}

	_destroy_slurm_conf();

	pthread_mutex_unlock(&conf_lock);

	return SLURM_SUCCESS;
}

extern slurm_ctl_conf_t *
slurm_conf_lock(void)
{
	pthread_mutex_lock(&conf_lock);

	if (!conf_initialized) {
		_init_slurm_conf(NULL);
		conf_initialized = true;
	}

	return conf_ptr;
}

extern void
slurm_conf_unlock(void)
{
	pthread_mutex_unlock(&conf_lock);
}

/* Normalize supplied debug level to be in range per log.h definitions */
static void _normalize_debug_level(uint16_t *level)
{
	if (*level > LOG_LEVEL_END) {
		error("Normalizing debug level from %u to %d", 
		      *level, (LOG_LEVEL_END - 1));
		*level = (LOG_LEVEL_END - 1);
	}
	/* level is uint16, always > LOG_LEVEL_QUIET(0), can't underflow */
}

/* 
 *
 * IN/OUT ctl_conf_ptr - a configuration as loaded by read_slurm_conf_ctl
 *
 * NOTE: a backup_controller or control_machine of "localhost" are over-written
 *	with this machine's name.
 * NOTE: if backup_addr is NULL, it is over-written by backup_controller
 * NOTE: if control_addr is NULL, it is over-written by control_machine
 */
static void
validate_and_set_defaults(slurm_ctl_conf_t *conf, s_p_hashtbl_t *hashtbl)
{
	bool truth;

	if (s_p_get_string(&conf->backup_controller, "BackupController",
			   hashtbl)
	    && strcasecmp("localhost", conf->backup_controller) == 0) {
		xfree(conf->backup_controller);
		conf->backup_controller = xmalloc (MAX_SLURM_NAME);
		if (getnodename(conf->backup_controller, MAX_SLURM_NAME)) 
			fatal("getnodename: %m");
	}
	if (s_p_get_string(&conf->backup_addr, "BackupAddr", hashtbl)) {
		if (conf->backup_controller == NULL) {
			error("BackupAddr specified without BackupController");
			xfree(conf->backup_addr);
		}
	} else {
		if (conf->backup_controller != NULL)
			conf->backup_addr = xstrdup(conf->backup_controller);
	}

	if (!s_p_get_string(&conf->control_machine, "ControlMachine", hashtbl))
		fatal ("validate_and_set_defaults: "
		       "ControlMachine not specified.");
	else if (strcasecmp("localhost", conf->control_machine) == 0) {
		xfree (conf->control_machine);
		conf->control_machine = xmalloc(MAX_SLURM_NAME);
		if (getnodename(conf->control_machine, MAX_SLURM_NAME))
			fatal("getnodename: %m");
	}

	if (!s_p_get_string(&conf->control_addr, "ControlAddr", hashtbl)
	    && conf->control_machine != NULL)
		conf->control_addr = xstrdup (conf->control_machine);

	if ((conf->backup_controller != NULL)
	    && (strcmp(conf->backup_controller, conf->control_machine) == 0)) {
		error("ControlMachine and BackupController identical");
		xfree(conf->backup_addr);
		xfree(conf->backup_controller);
	}

	if (!s_p_get_string(&conf->job_credential_private_key,
			    "JobCredentialPrivateKey", hashtbl))
		fatal("JobCredentialPrivateKey not set");
	if (!s_p_get_string(&conf->job_credential_public_certificate,
			    "JobCredentialPublicCertificate", hashtbl))
		fatal("JobCredentialPublicCertificate not set");

	if (s_p_get_uint16(&conf->max_job_cnt, "MaxJobCount", hashtbl)
	    && conf->max_job_cnt < 1)
		fatal("MaxJobCount=%u, No jobs permitted", conf->max_job_cnt);

	if (!s_p_get_string(&conf->authtype, "AuthType", hashtbl))
		conf->authtype = xstrdup(DEFAULT_AUTH_TYPE);

	if (!s_p_get_uint16(&conf->cache_groups, "CacheGroups", hashtbl))
		conf->cache_groups = DEFAULT_CACHE_GROUPS;

	if (!s_p_get_string(&conf->checkpoint_type, "CheckpointType", hashtbl))
		conf->checkpoint_type = xstrdup(DEFAULT_CHECKPOINT_TYPE);

	s_p_get_string(&conf->epilog, "Epilog", hashtbl);

	if (!s_p_get_uint16(&conf->fast_schedule, "FastSchedule", hashtbl))
		conf->fast_schedule = DEFAULT_FAST_SCHEDULE;

	if (!s_p_get_uint32(&conf->first_job_id, "FirstJobId", hashtbl))
		conf->first_job_id = DEFAULT_FIRST_JOB_ID;

	if (s_p_get_uint16(&conf->inactive_limit, "InactiveLimit", hashtbl)) {
#ifdef HAVE_BG
		/* Inactive limit must be zero on Blue Gene */
		if (conf->inactive_limit) {
			error("InactiveLimit=%ld is invalid on Blue Gene",
				conf->inactive_limit);
		}
		conf->inactive_limit = 0;
#endif
	} else {
#ifdef HAVE_BG
		conf->inactive_limit = 0;
#endif
		conf->inactive_limit = DEFAULT_INACTIVE_LIMIT;
	}

	if (!s_p_get_string(&conf->job_acct_logfile, 
			    "JobAcctLogFile", hashtbl))
		conf->job_acct_logfile = xstrdup(DEFAULT_JOB_ACCT_LOGFILE);

	if (!s_p_get_uint16(&conf->job_acct_freq, "JobAcctFrequency", hashtbl))
		conf->job_acct_freq = DEFAULT_JOB_ACCT_FREQ;

	if (!s_p_get_string(&conf->job_acct_type, "JobAcctType", hashtbl))
		conf->job_acct_type = xstrdup(DEFAULT_JOB_ACCT_TYPE);

	s_p_get_string(&conf->job_comp_loc, "JobCompLoc", hashtbl);

	if (!s_p_get_string(&conf->job_comp_type, "JobCompType", hashtbl))
		conf->job_comp_type = xstrdup(DEFAULT_JOB_COMP_TYPE);

	if (!s_p_get_uint16(&conf->kill_wait, "KillWait", hashtbl))
		conf->kill_wait = DEFAULT_KILL_WAIT;

	if (!s_p_get_uint16(&conf->max_job_cnt, "MaxJobCount", hashtbl))
		conf->max_job_cnt = DEFAULT_MAX_JOB_COUNT;

	if (!s_p_get_uint16(&conf->msg_timeout, "MessageTimeout", hashtbl))
		conf->msg_timeout = DEFAULT_MSG_TIMEOUT;

	if (!s_p_get_uint16(&conf->min_job_age, "MinJobAge", hashtbl))
		conf->min_job_age = DEFAULT_MIN_JOB_AGE;

	if (!s_p_get_string(&conf->mpi_default, "MpiDefault", hashtbl))
		conf->mpi_default = xstrdup(DEFAULT_MPI_DEFAULT);

	if (!s_p_get_string(&conf->plugindir, "PluginDir", hashtbl))
		conf->plugindir = xstrdup(default_plugin_path);

	if (!s_p_get_string(&conf->plugstack, "PlugStackConfig", hashtbl))
		conf->plugstack = xstrdup(default_plugstack);

	if (!s_p_get_string(&conf->switch_type, "SwitchType", hashtbl))
		conf->switch_type = xstrdup(DEFAULT_SWITCH_TYPE);

	if (!s_p_get_string(&conf->proctrack_type, "ProctrackType", hashtbl)) {
		if (!strcmp(conf->switch_type,"switch/elan"))
			conf->proctrack_type = xstrdup("proctrack/rms");
		else
			conf->proctrack_type = 
				xstrdup(DEFAULT_PROCTRACK_TYPE);
	}
	if ((!strcmp(conf->switch_type, "switch/elan"))
	    && (!strcmp(conf->proctrack_type,"proctrack/linuxproc")))
		fatal("proctrack/linuxproc is incompatable with switch/elan");

	s_p_get_string(&conf->prolog, "Prolog", hashtbl);

	if (!s_p_get_uint16(&conf->propagate_prio_process,
			"PropagatePrioProcess", hashtbl)) {
		conf->propagate_prio_process = DEFAULT_PROPAGATE_PRIO_PROCESS;
	} else if (conf->propagate_prio_process > 1) {
		fatal("Bad PropagatePrioProcess: %u",
			conf->propagate_prio_process);
	}

        if (s_p_get_string(&conf->propagate_rlimits_except,
			   "PropagateResourceLimitsExcept", hashtbl)) {
                if ((parse_rlimits(conf->propagate_rlimits_except,
                                   NO_PROPAGATE_RLIMITS)) < 0)
                        fatal("Bad PropagateResourceLimitsExcept: %s",
			      conf->propagate_rlimits_except);
        } else {
                if (!s_p_get_string(&conf->propagate_rlimits,
				    "PropagateResourceLimits", hashtbl))
                        conf->propagate_rlimits = xstrdup( "ALL" );
                if ((parse_rlimits(conf->propagate_rlimits,
                                   PROPAGATE_RLIMITS )) < 0)
                        fatal("Bad PropagateResourceLimits: %s",
			      conf->propagate_rlimits);
        }

	if (!s_p_get_uint16(&conf->ret2service, "ReturnToService", hashtbl))
		conf->ret2service = DEFAULT_RETURN_TO_SERVICE;

	s_p_get_string(&conf->schedauth, "SchedulerAuth", hashtbl);

	if (s_p_get_uint16(&conf->schedport, "SchedulerPort", hashtbl)) {
		if (conf->schedport == 0) {
			error("SchedulerPort=0 is invalid");
			conf->schedport = (uint16_t)NO_VAL;
		}
	}

	if (!s_p_get_uint16(&conf->schedrootfltr,
			    "SchedulerRootFilter", hashtbl))
		conf->schedrootfltr = DEFAULT_SCHEDROOTFILTER;

	if (!s_p_get_string(&conf->schedtype, "SchedulerType", hashtbl))
		conf->schedtype = xstrdup(DEFAULT_SCHEDTYPE);

	if (!s_p_get_string(&conf->select_type, "SelectType", hashtbl))
		conf->select_type = xstrdup(DEFAULT_SELECT_TYPE);

	if (!s_p_get_string( &conf->slurm_user_name, "SlurmUser", hashtbl)) {
		conf->slurm_user_name = xstrdup("root");
		conf->slurm_user_id   = 0;
	} else {
		struct passwd *slurm_passwd;
		slurm_passwd = getpwnam(conf->slurm_user_name);
		if (slurm_passwd == NULL) {
			error ("Invalid user for SlurmUser %s, ignored",
			       conf->slurm_user_name);
			xfree(conf->slurm_user_name);
		} else {
			if (slurm_passwd->pw_uid > 0xffff)
				error("SlurmUser numeric overflow, "
				      "will be fixed soon");
			else
				conf->slurm_user_id = slurm_passwd->pw_uid;
		}
	}

	if (s_p_get_uint16(&conf->slurmctld_debug, "SlurmctldDebug", hashtbl))
		_normalize_debug_level(&conf->slurmctld_debug);
	else
		conf->slurmctld_debug = LOG_LEVEL_INFO;

	if (!s_p_get_string(&conf->slurmctld_pidfile,
			    "SlurmctldPidFile", hashtbl))
		conf->slurmctld_pidfile = xstrdup(DEFAULT_SLURMCTLD_PIDFILE);

	s_p_get_string(&conf->slurmctld_logfile, "SlurmctldLogFile", hashtbl);

	if (!s_p_get_uint32(&conf->slurmctld_port, "SlurmctldPort", hashtbl))
		conf->slurmctld_port = SLURMCTLD_PORT;

	if (!s_p_get_uint16(&conf->slurmctld_timeout,
			    "SlurmctldTimeout", hashtbl))
		conf->slurmctld_timeout = DEFAULT_SLURMCTLD_TIMEOUT;

	if (s_p_get_uint16(&conf->slurmd_debug, "SlurmdDebug", hashtbl))
		_normalize_debug_level(&conf->slurmd_debug);
	else
		conf->slurmd_debug = LOG_LEVEL_INFO;

	s_p_get_string(&conf->slurmd_logfile, "SlurmdLogFile", hashtbl);

	if (!s_p_get_string(&conf->slurmd_pidfile, "SlurmdPidFile", hashtbl))
		conf->slurmd_pidfile = xstrdup(DEFAULT_SLURMD_PIDFILE);

	if (!s_p_get_uint32(&conf->slurmd_port, "SlurmdPort", hashtbl))
		conf->slurmd_port = SLURMD_PORT;

	if (!s_p_get_string(&conf->slurmd_spooldir, "SlurmdSpoolDir", hashtbl))
		conf->slurmd_spooldir = xstrdup(DEFAULT_SPOOLDIR);

	if (!s_p_get_uint16(&conf->slurmd_timeout, "SlurmdTimeout", hashtbl))
		conf->slurmd_timeout = DEFAULT_SLURMD_TIMEOUT;

	s_p_get_string(&conf->srun_prolog, "SrunProlog", hashtbl);
	s_p_get_string(&conf->srun_epilog, "SrunEpilog", hashtbl);

	if (!s_p_get_string(&conf->state_save_location,
			    "StateSaveLocation", hashtbl))
		conf->state_save_location = xstrdup(DEFAULT_SAVE_STATE_LOC);

	/* see above for switch_type, order dependent */

	if (!s_p_get_string(&conf->task_plugin, "TaskPlugin", hashtbl))
		conf->task_plugin = xstrdup(DEFAULT_TASK_PLUGIN);

	s_p_get_string(&conf->task_epilog, "TaskEpilog", hashtbl);
	s_p_get_string(&conf->task_prolog, "TaskProlog", hashtbl);

	if (!s_p_get_string(&conf->tmp_fs, "TmpFS", hashtbl))
		conf->tmp_fs = xstrdup(DEFAULT_TMP_FS);

	if (!s_p_get_uint16(&conf->wait_time, "WaitTime", hashtbl))
		conf->wait_time = DEFAULT_WAIT_TIME;
	
	if (s_p_get_uint16(&conf->tree_width, "TreeWidth", hashtbl)) {
		if (conf->tree_width == 0) {
			error("TreeWidth=0 is invalid");
			conf->tree_width = DEFAULT_TREE_WIDTH; /* default? */
		}
	} else {
		conf->tree_width = DEFAULT_TREE_WIDTH;
	}

	if (s_p_get_boolean(&truth, "UsePAM", hashtbl) && truth) {
		conf->use_pam = 1;
	} else {
		conf->use_pam = 0;
	}
}

