/*
 * Stonith module for BladeCenter via OpenHPI, an implementation of Service 
 *   Availability Forum's Hardware Platfrom Interface
 *
 * Author: Dave Blaschke <debltc@us.ibm.com>
 *
 * Copyright (c) 2005 International Business Machines
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>
 *
 */

#include <lha_internal.h>

#define DEVICE		"IBM BladeCenter (OpenHPI)"

#include "stonith_plugin_common.h"

#define PIL_PLUGIN		bladehpi
#define PIL_PLUGIN_S            "bladehpi"
#define PIL_PLUGINLICENSE 	LICENSE_LGPL
#define PIL_PLUGINLICENSEURL 	URL_LGPL
#include <pils/plugin.h>

#include <openhpi/SaHpi.h>

/* Maximum number of seconds to wait for host to power off */
#define MAX_POWEROFF_WAIT	60

/* entity_root, the one required plugin parameter */
#define ST_ENTITYROOT		"entity_root"

/* String format of entity_root */
#define SYSTEM_CHASSIS_FMT	"{SYSTEM_CHASSIS,%d}"

/* soft_reset, the one optional plugin parameter */
#define ST_SOFTRESET		"soft_reset"

#define OPENHPIURL		"http://www.openhpi.org/"

/* OpenHPI resource types of interest to this plugin */
#define OHRES_NONE		0
#define OHRES_BLADECENT		1
#define OHRES_MGMTMOD		2
#define OHRES_BLADE		3

/* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that
   saHpiResourcePowerStateSet can return before the desired state has been
   achieved by the blade.  In the SAHPI_POWER_OFF case this is not good,
   as whoever calls this plugin assumes that the power is actually off
   when the plugin returns with a successful return code.  Define this
   constant to build code that loops in one second intervals after calling
   saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is
   really off.
#define IBMBC_WAIT_FOR_OFF */

static StonithPlugin *	bladehpi_new(const char *);
static void		bladehpi_destroy(StonithPlugin *);
static const char *	bladehpi_getinfo(StonithPlugin *, int);
static const char * const *	bladehpi_get_confignames(StonithPlugin *);
static int		bladehpi_status(StonithPlugin *);
static int		bladehpi_reset_req(StonithPlugin *, int, const char *);
static char **		bladehpi_hostlist(StonithPlugin *);
static int		bladehpi_set_config(StonithPlugin *, StonithNVpair *);

static struct stonith_ops bladehpiOps = {
	bladehpi_new,			/* Create new STONITH object	*/
	bladehpi_destroy,		/* Destroy STONITH object	*/
	bladehpi_getinfo,		/* Return STONITH info string	*/
	bladehpi_get_confignames,	/* Return configuration parameters */
	bladehpi_set_config,		/* Set configuration            */
	bladehpi_status,		/* Return STONITH device status	*/
	bladehpi_reset_req,		/* Request a reset */
	bladehpi_hostlist,		/* Return list of supported hosts */
};

PIL_PLUGIN_BOILERPLATE2("1.0", Debug)

static const PILPluginImports *	PluginImports;
static PILPlugin *		OurPlugin;
static PILInterface *		OurInterface;
static StonithImports *		OurImports;
static void *			interfprivate;


PIL_rc
PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports);

PIL_rc
PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports)
{
	/* Force the compiler to do a little type checking */
	(void)(PILPluginInitFun)PIL_PLUGIN_INIT;

	PluginImports = imports;
	OurPlugin = us;

	/* Register ourself as a plugin */
	imports->register_plugin(us, &OurPIExports);  

	/*  Register our interface implementation */
 	return imports->register_interface(us
	,	PIL_PLUGINTYPE_S
	,	PIL_PLUGIN_S
	,	&bladehpiOps
	,	NULL		/* close */
	,	&OurInterface
	,	(void *)&OurImports
	,	&interfprivate); 
}

struct pluginDevice {	
	StonithPlugin		sp;
	const char *		pluginid;
	char *			idinfo;
	char *			device;
	int			softreset;
	GList *		 	hostlist;
	SaHpiVersionT		ohver;		/* OpenHPI interface version */
	SaHpiSessionIdT		ohsession;	/* session ID */
	SaHpiUint32T		ohrptcnt;	/* RPT count for hostlist */
	SaHpiResourceIdT	ohdevid;	/* device resource ID */
	SaHpiResourceIdT	ohsensid;	/* sensor resource ID */
	SaHpiSensorNumT		ohsensnum;	/* sensor number */
};

static int open_hpi_session(struct pluginDevice *dev);
static void close_hpi_session(struct pluginDevice *dev);

static const char *pluginid = "BladeCenterDevice-Stonith";
static const char *NOTpluginID = "IBM BladeCenter device has been destroyed";

#include "stonith_config_xml.h"

#define XML_ENTITYROOT_SHORTDESC \
	XML_PARM_SHORTDESC_BEGIN("en") \
	ST_ENTITYROOT \
	XML_PARM_SHORTDESC_END

#define XML_ENTITYROOT_LONGDESC \
	XML_PARM_LONGDESC_BEGIN("en") \
	"The entity_root of the STONITH device from the OpenHPI config file" \
	XML_PARM_LONGDESC_END

#define XML_ENTITYROOT_PARM \
	XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1", "0") \
	  XML_ENTITYROOT_SHORTDESC \
	  XML_ENTITYROOT_LONGDESC \
	XML_PARAMETER_END

#define XML_SOFTRESET_SHORTDESC \
	XML_PARM_SHORTDESC_BEGIN("en") \
	ST_SOFTRESET \
	XML_PARM_SHORTDESC_END

#define XML_SOFTRESET_LONGDESC \
	XML_PARM_LONGDESC_BEGIN("en") \
	"Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \
	XML_PARM_LONGDESC_END

#define XML_SOFTRESET_PARM \
	XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0", "0") \
	  XML_SOFTRESET_SHORTDESC \
	  XML_SOFTRESET_LONGDESC \
	XML_PARAMETER_END

static const char *bladehpiXML = 
  XML_PARAMETERS_BEGIN
    XML_ENTITYROOT_PARM
    XML_SOFTRESET_PARM
  XML_PARAMETERS_END;

static int get_resource_type(char *, SaHpiRptEntryT *);
static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT);
static int get_bladehpi_hostlist(struct pluginDevice *);
static void free_bladehpi_hostlist(struct pluginDevice *);
static int get_num_tokens(char *str);

struct blade_info {
	char *			name;		/* blade name */
	SaHpiResourceIdT	resourceId;	/* blade resource ID */
	SaHpiCapabilitiesT	resourceCaps;	/* blade capabilities */
};


static int
bladehpi_status(StonithPlugin *s)
{
	struct pluginDevice *	dev;
	SaErrorT		ohrc;
	SaHpiDomainInfoT 	ohdi;
	int rc = S_OK;
	
	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}

	ERRIFWRONGDEV(s, S_OOPS);

	dev = (struct pluginDevice *)s;
	rc = open_hpi_session(dev);
	if( rc != S_OK )
		return rc;

	/* Refresh the hostlist only if RPTs updated */
	ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
		,	__FUNCTION__, ohrc);
		rc = S_BADCONFIG;
		goto done;
	}
	if (dev->ohrptcnt != ohdi.RptUpdateCount) {
		free_bladehpi_hostlist(dev);
		if (get_bladehpi_hostlist(dev) != S_OK) {
			LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
			,	__FUNCTION__);
			rc = S_BADCONFIG;
			goto done;
		}
	}

	/* At this point, hostlist is up to date */
	if (dev->ohsensid && dev->ohsensnum) {
		/*
		 * For accurate status, need to make a call that goes out to
		 * BladeCenter MM because the calls made so far by this
		 * function (and perhaps get_bladehpi_hostlist) only retrieve
		 * information from memory cached by OpenHPI
		 */
		ohrc = saHpiSensorReadingGet(dev->ohsession
			, dev->ohsensid, dev->ohsensnum, NULL, NULL);
		if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) {
			LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s"
			,	__FUNCTION__);
			rc = S_OOPS;
			goto done;
		}
	}

done:
	close_hpi_session(dev);
	return (rc == S_OK) ? (dev->ohdevid ? S_OK : S_OOPS) : rc;
}


/*
 *	Return the list of hosts configured for this HMC device
 */

static char **
bladehpi_hostlist(StonithPlugin *s)
{
	struct pluginDevice *	dev;
	int			numnames = 0, j;
	char **			ret = NULL;
	GList *			node = NULL;
	SaErrorT		ohrc;
	SaHpiDomainInfoT 	ohdi;
	int rc = S_OK;

	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}

	ERRIFWRONGDEV(s, NULL);

	dev = (struct pluginDevice *)s;
	rc = open_hpi_session(dev);
	if( rc != S_OK )
		return NULL;

	/* Refresh the hostlist only if RPTs updated */
	ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
		,	__FUNCTION__, ohrc);
		goto done;
	}
	if (dev->ohrptcnt != ohdi.RptUpdateCount) {
		free_bladehpi_hostlist(dev);
		if (get_bladehpi_hostlist(dev) != S_OK) {
			LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
			,	__FUNCTION__);
			goto done;
		}
	}

	/* At this point, hostlist is up to date */
	numnames = g_list_length(dev->hostlist);
	if (numnames < 0) {
		LOG(PIL_CRIT, "Unconfigured stonith object in %s"
		,	__FUNCTION__);
		goto done;
	}

	ret = (char **)MALLOC((numnames+1) * sizeof(char *));
	if (ret == NULL) {
		LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__);
		goto done;
	}

	memset(ret, 0, (numnames+1) * sizeof(char *));
	for (node = g_list_first(dev->hostlist), j = 0
	;	NULL != node
	;	j++, node = g_list_next(node))	{
		ret[j] = STRDUP(((struct blade_info *)node->data)->name);
		if (ret[j] == NULL) {
			LOG(PIL_CRIT, "Out of memory for strdup in %s"
			,	__FUNCTION__);
			stonith_free_hostlist(ret);
			ret = NULL;
			goto done;
		}
		strdown(ret[j]);
	}

done:
	close_hpi_session(dev);
	return ret;
}


static const char * const *
bladehpi_get_confignames(StonithPlugin *s)
{
	static const char *	names[] = {ST_ENTITYROOT, NULL};

	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}

	return names;
}


/*
 *	Reset the given host, and obey the request type.
 */

static int
bladehpi_reset_req(StonithPlugin *s, int request, const char *host)
{
	GList *			node = NULL;
	struct pluginDevice *	dev = NULL;
	struct blade_info *	bi = NULL;
	SaHpiPowerStateT	ohcurstate, ohnewstate;
	SaHpiDomainInfoT 	ohdi;
	SaErrorT		ohrc;
	int rc = S_OK;
	
	if (Debug) {
		LOG(PIL_DEBUG, "%s: called, request=%d, host=%s"
		,	__FUNCTION__, request, host);
	}
	
	ERRIFWRONGDEV(s, S_OOPS);
	
	if (host == NULL) {
		LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__);
		rc = S_OOPS;
		goto done;
	}

	dev = (struct pluginDevice *)s;
	rc = open_hpi_session(dev);
	if( rc != S_OK )
		return rc;

	ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
		,	__FUNCTION__, ohrc);
		rc = S_BADCONFIG;
		goto done;
	}
	if (dev->ohrptcnt != ohdi.RptUpdateCount) {
		free_bladehpi_hostlist(dev);
		if (get_bladehpi_hostlist(dev) != S_OK) {
			LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
			,	__FUNCTION__);
			rc = S_OOPS;
			goto done;
		}
	}

	for (node = g_list_first(dev->hostlist)
	;	node != NULL
	;	node = g_list_next(node)) {
		bi = ((struct blade_info *)node->data);
		if (Debug) {
			LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name);
		}
		
		if (!strcasecmp(bi->name, host)) {
			break;
		}
	}

	if (!node || !bi) {
		LOG(PIL_CRIT
		,	"Host %s is not configured in this STONITH module, "
			"please check your configuration information", host);
		rc = S_OOPS;
		goto done;
	}

	/* Make sure host has proper capabilities for get */
	if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) {
		LOG(PIL_CRIT
		,	"Host %s does not have power capability", host);
		rc = S_OOPS;
		goto done;
	}

	ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId
			, &ohcurstate);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get host %s power state (%d)"
		,	host, ohrc);
		rc = S_OOPS;
		goto done;
	}

	switch (request) {
		case ST_POWERON:
			if (ohcurstate == SAHPI_POWER_ON) {
				LOG(PIL_INFO, "Host %s already on", host);
				goto done;
			}
			ohnewstate = SAHPI_POWER_ON;

			break;

		case ST_POWEROFF:
			if (ohcurstate == SAHPI_POWER_OFF) {
				LOG(PIL_INFO, "Host %s already off", host);
				goto done;
			}
			ohnewstate = SAHPI_POWER_OFF;
	
			break;

		case ST_GENERIC_RESET:
			if (ohcurstate == SAHPI_POWER_OFF) {
				ohnewstate = SAHPI_POWER_ON;
			} else {
				ohnewstate = SAHPI_POWER_CYCLE;
			}

			break;

		default:
			LOG(PIL_CRIT, "Invalid request argument to %s"
			,	__FUNCTION__);
			rc = S_INVAL;
			goto done;
	}

	if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) {
		int maxwait;

		ohrc = saHpiResourcePowerStateSet(dev->ohsession
				, bi->resourceId, SAHPI_POWER_OFF);
		if (ohrc != SA_OK) {
			LOG(PIL_CRIT, "Unable to set host %s power state to"
				" OFF (%d)", host, ohrc);
			rc = S_OOPS;
			goto done;
		}

		/* 
		 * Must wait for power off here or subsequent power on request
		 * may take place while power is still on and thus ignored
		 */
		maxwait = MAX_POWEROFF_WAIT;
		do {
			maxwait--;
			sleep(1);
			ohrc = saHpiResourcePowerStateGet(dev->ohsession
					, bi->resourceId, &ohcurstate);
		} while ((ohrc == SA_OK)
			&& (ohcurstate != SAHPI_POWER_OFF)
			&& (maxwait > 0));

		if (Debug) {
			LOG(PIL_DEBUG, "Waited %d seconds for power off"
			,	MAX_POWEROFF_WAIT - maxwait);
		}

		ohrc = saHpiResourcePowerStateSet(dev->ohsession
				, bi->resourceId, SAHPI_POWER_ON);
		if (ohrc != SA_OK) {
			LOG(PIL_CRIT, "Unable to set host %s power state to"
			" ON (%d)", host, ohrc);
			rc = S_OOPS;
			goto done;
		}
	} else {
		/* Make sure host has proper capabilities to reset */
		if ((ohnewstate == SAHPI_POWER_CYCLE) &&
		    (!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) {
			LOG(PIL_CRIT
			,	"Host %s does not have reset capability"
			,	host);
			rc = S_OOPS;
			goto done;
		}

		if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession
				, bi->resourceId, ohnewstate)) != SA_OK) {
			LOG(PIL_CRIT, "Unable to set host %s power state (%d)"
			,	host, ohrc);
			rc = S_OOPS;
			goto done;
		}
	}

#ifdef IBMBC_WAIT_FOR_OFF
	if (ohnewstate == SAHPI_POWER_OFF) {
		int maxwait = MAX_POWEROFF_WAIT;

		do {
			maxwait--;
			sleep(1);
			ohrc = saHpiResourcePowerStateGet(dev->ohsession
					, bi->resourceId, &ohcurstate);
		} while ((ohrc == SA_OK)
			&& (ohcurstate != SAHPI_POWER_OFF)
			&& (maxwait > 0));

		if (Debug) {
			LOG(PIL_DEBUG, "Waited %d seconds for power off"
			,	MAX_POWEROFF_WAIT - maxwait);
		}
	}
#endif

	LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);

done:
	close_hpi_session(dev);
	return rc;
}


/*
 *	Parse the information in the given configuration file,
 *	and stash it away...
 */

static int
bladehpi_set_config(StonithPlugin *s, StonithNVpair *list)
{
	struct pluginDevice *	dev = NULL;
	StonithNamesToGet	namestocopy [] =
	{	{ST_ENTITYROOT,	NULL}
	,	{NULL,		NULL}
	};
	int			rc, i;
	
	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}
	
	ERRIFWRONGDEV(s, S_OOPS);

	dev = (struct pluginDevice *)s;

	if (Debug) {
		LOG(PIL_DEBUG, "%s conditionally compiled with:"
#ifdef IBMBC_WAIT_FOR_OFF
		" IBMBC_WAIT_FOR_OFF"
#endif
		, dev->pluginid);
	}
	
	if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
		return rc;
	}

	if (Debug) {
		LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT
		,	namestocopy[0].s_value);	
	}

	if (get_num_tokens(namestocopy[0].s_value) == 1) {
		/* name=value pairs on command line, look for soft_reset */
		const char *softreset = 
			OurImports->GetValue(list, ST_SOFTRESET);
		if (softreset != NULL) {
			if (!strcasecmp(softreset, "true") ||
			    !strcmp(softreset, "1")) {
				dev->softreset = 1;
			} else if (!strcasecmp(softreset, "false") ||
				   !strcmp(softreset, "0")) {
				dev->softreset = 0;
			} else {
				LOG(PIL_CRIT, "Invalid %s %s, must be "
					"true, 1, false or 0"
				,	ST_SOFTRESET, softreset);
				FREE(namestocopy[0].s_value);
				return S_OOPS;
			}
		}
	} else {
		/* -p or -F option with args "entity_root [soft_reset]..." */
		char *pch = namestocopy[0].s_value;

		/* skip over entity_root and null-terminate */
		pch += strcspn(pch, WHITESPACE);
		*pch = EOS;

		/* skip over white-space up to next token */
		pch++;
		pch += strspn(pch, WHITESPACE);
		if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) {
			dev->softreset = 1;
		} else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) {
			dev->softreset = 0;
		} else {
			LOG(PIL_CRIT, "Invalid %s %s, must be "
				"true, 1, false or 0"
			,	ST_SOFTRESET, pch);
			FREE(namestocopy[0].s_value);
			return S_OOPS;
		}
	}

	dev->device = STRDUP(namestocopy[0].s_value);
	FREE(namestocopy[0].s_value);
	if (dev->device == NULL) {
		LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__);
		return S_OOPS;
	}

	if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) ||
	    sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) {
		LOG(PIL_CRIT, "Invalid %s %s, must be of format %s"
		,	ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT);
		return S_BADCONFIG;
	}
	
	dev->ohver = saHpiVersionGet();
	if (dev->ohver > SAHPI_INTERFACE_VERSION) {
		LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than "
			"one used by plugin (%x), incompatibilites may exist"
		,	dev->ohver, SAHPI_INTERFACE_VERSION);
		return S_BADCONFIG;
	}
	return S_OK;
}

static int
open_hpi_session(struct pluginDevice *dev)
{
	SaErrorT		ohrc;

	ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID
				    , &dev->ohsession, NULL);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc);
		return S_BADCONFIG;
	}

	ohrc = saHpiDiscover(dev->ohsession);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc);
		return S_BADCONFIG;
	}

	return S_OK;
}
static void
close_hpi_session(struct pluginDevice *dev)
{
	if (dev && dev->ohsession) {
		saHpiSessionClose(dev->ohsession);
		dev->ohsession = 0;
	}
}

static const char *
bladehpi_getinfo(StonithPlugin *s, int reqtype)
{
	struct pluginDevice *	dev;
	const char *		ret;

	if (Debug) {
		LOG(PIL_DEBUG, "%s: called, reqtype=%d"
		,	__FUNCTION__, reqtype);
	}
	
	ERRIFWRONGDEV(s, NULL);

	dev = (struct pluginDevice *)s;

	switch (reqtype) {
		case ST_DEVICEID:
			ret = dev->idinfo;
			break;

		case ST_DEVICENAME:
			ret = dev->device;
			break;

		case ST_DEVICEDESCR:
			ret = "IBM BladeCenter via OpenHPI\n"
			"Use for IBM xSeries systems managed by BladeCenter\n"
			"  Required parameter name " ST_ENTITYROOT " is "
			"a string (no white-space) of\n"
			"the format \""SYSTEM_CHASSIS_FMT"\" "
			"which is entity_root of BladeCenter\n"
			"from OpenHPI config file, where %d is a positive "
			"integer\n"
			"  Optional parameter name " ST_SOFTRESET " is "
			"true|1 if STONITH device should\n"
			"use soft reset (power cycle) to reset nodes or "
			"false|0 if device should\n"
			"use hard reset (power off, wait, power on); "
			"default is false";
			break;

		case ST_DEVICEURL:
			ret = OPENHPIURL;
			break;

		case ST_CONF_XML:		/* XML metadata */
			ret = bladehpiXML;
			break;

		default:
			ret = NULL;
			break;
	}

	return ret;
}


/*
 *	HMC Stonith destructor...
 */

static void
bladehpi_destroy(StonithPlugin *s)
{
	struct pluginDevice *	dev;

	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}

	VOIDERRIFWRONGDEV(s);

	dev = (struct pluginDevice *)s;

	dev->pluginid = NOTpluginID;
	if (dev->device) {
		FREE(dev->device);
		dev->device = NULL;
	}
	if (dev->idinfo) {
		FREE(dev->idinfo);
		dev->idinfo = NULL;
	}
	free_bladehpi_hostlist(dev);

	if (dev->ohsession) {
		saHpiSessionClose(dev->ohsession);
		dev->ohsession = 0;
	}
	
	FREE(dev);
}


static StonithPlugin *
bladehpi_new(const char *subplugin)
{
	struct pluginDevice *	dev = ST_MALLOCT(struct pluginDevice);
	
	if (Debug) {
		LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
	}
	
	if (dev == NULL) {
		LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__);
		return NULL;
	}

	memset(dev, 0, sizeof(*dev));

	dev->pluginid = pluginid;
	dev->device = NULL;
	dev->hostlist = NULL;
	REPLSTR(dev->idinfo, DEVICE);
	if (dev->idinfo == NULL) {
		FREE(dev);
		return NULL;
	}
	dev->sp.s_ops = &bladehpiOps;

	if (Debug) {
		LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__);
	}

	return ((void *)dev);
}


static int
get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT)
{
	int			i, rc = OHRES_NONE;
	int			foundBlade = 0, foundExp = 0, foundMgmt = 0;
	int			foundRoot = 0, foundOther = 0;
	char			rootName[64];
	SaHpiEntityPathT *	ohep = &ohRPT->ResourceEntity;

	if (ohep == NULL || entityRoot == NULL) {
		return 0;
	}

	/* First find root of entity path, which is last entity in entry */
        for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) {
                if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
                            break;
                }
        }

	/* Then back up through entries looking for specific entity */
        for (i--; i >= 0; i--) {
		switch (ohep->Entry[i].EntityType) {
			case SAHPI_ENT_SBC_BLADE:
				foundBlade = 1;
				break;

			case SAHPI_ENT_SYS_EXPANSION_BOARD:
				foundExp = 1;
				break;

			case SAHPI_ENT_SYS_MGMNT_MODULE:
				if (ohep->Entry[i].EntityLocation == 0) {
					foundMgmt = 1;
				}
				break;

			case SAHPI_ENT_SYSTEM_CHASSIS:
				snprintf(rootName, sizeof(rootName)
				,	SYSTEM_CHASSIS_FMT
				,	(int)ohep->Entry[i].EntityLocation);
				if (!strcmp(entityRoot, rootName)) {
					foundRoot = 1;
				}
				break;

			default:
				foundOther = 1;
				break;
		}
	}

	/* We are only interested in specific entities on specific device */
	if (foundRoot) {
		if (foundMgmt && !(foundBlade||foundExp||foundOther)) {
			rc = OHRES_MGMTMOD;
		} else if (!(foundMgmt||foundBlade||foundExp||foundOther)) {
			rc = OHRES_BLADECENT;
		} else if (foundBlade && !foundExp) {
			rc = OHRES_BLADE;
		}
	}

	return rc;
}


static int
get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid)
{
	SaErrorT	ohrc = SA_OK;
	SaHpiEntryIdT	ohnextid;
	SaHpiRdrT	ohRDR;

	ohnextid = SAHPI_FIRST_ENTRY;
	do {
		ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid
				, &ohnextid, &ohRDR);
		if (ohrc != SA_OK) {
			LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)"
			,	__FUNCTION__, ohrc);
		} else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) {
			return ohRDR.RdrTypeUnion.SensorRec.Num;
		}
	} while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);

	return 0;
}


/*
 *	Get RPT update count
 *	Loop through all RPT entries
 *	  If entry is BladeCenter, save resource ID in dev->ohdevid
 *	  If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid
 *	    and sensor number in dev->ohsensnum
 *	  If entry is blade, save blade_info and add to dev->hostlist
 *	Get RPT update count
 *	If RPT update count changed since start of loop, repeat loop
 *	Save RPT update count in dev->ohrptcnt
 *
 *	Note that not only does this function update hostlist, it also
 *	updates ohrptcnt, ohdevid, ohsensid and ohsensnum.  However, with
 *	this logic it does not need to be called again until the RPT update
 *	count changes.
 */

static int
get_bladehpi_hostlist(struct pluginDevice *dev)
{
	struct blade_info *	bi;
	SaErrorT		ohrc;
	SaHpiEntryIdT		ohnextid;
	SaHpiRptEntryT		ohRPT;
	SaHpiDomainInfoT 	ohdi;
	SaHpiUint32T		ohupdate;

	if (Debug) {
		LOG(PIL_DEBUG, "%s: called, dev->device=%s"
		,	__FUNCTION__,	dev->device);
	}

	if (dev->device == NULL || *dev->device == 0) {
		LOG(PIL_CRIT, "Unconfigured stonith object in %s"
		,	__FUNCTION__);
		return S_BADCONFIG;
	}

	ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
		,	__FUNCTION__, ohrc);
		return S_BADCONFIG;
	}
	
try_again:
	ohupdate = ohdi.RptUpdateCount;
	dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
	ohnextid = SAHPI_FIRST_ENTRY;
	do {
		char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH];
		int  blnum;

		ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid
				       , &ohnextid, &ohRPT);
		if (ohrc != SA_OK) {
			LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)"
			,	__FUNCTION__, ohrc);
			free_bladehpi_hostlist(dev);
			return S_BADCONFIG;
		}

		switch (get_resource_type(dev->device, &ohRPT)) {
		case OHRES_BLADECENT:
			dev->ohdevid = ohRPT.ResourceId;

			if (Debug) {
				LOG(PIL_DEBUG, "BladeCenter '%s' has id %u"
				,	(char*)ohRPT.ResourceTag.Data
				,	dev->ohdevid);
			}
			break;

		case OHRES_MGMTMOD:
			if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){
 				dev->ohsensnum = get_sensor_num(dev->ohsession
							, ohRPT.ResourceId);

				if (dev->ohsensnum) {
					dev->ohsensid = ohRPT.ResourceId;

					if (Debug) {
						LOG(PIL_DEBUG
						, "MgmtModule '%s' has id %u "
						"with sensor #%u"
						, (char*)ohRPT.ResourceTag.Data
						, dev->ohsensid
						, dev->ohsensnum);
					}
				}
			} 
			break;

		case OHRES_BLADE:
			if ((bi = (struct blade_info *)
				MALLOC(sizeof(struct blade_info))) == NULL) {
			        LOG(PIL_CRIT, "Out of memory in %s"
				,	__FUNCTION__);
				free_bladehpi_hostlist(dev);
			        return S_OOPS;
			}

			/*
			 * New format consists of "Blade N - name" while older
			 * format consists only of "name"; we only need to
			 * stash name because ResourceID is the important info
			 */
			if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s"
					, &blnum, blname) == 2) {
				bi->name = STRDUP(blname);
			} else {
				bi->name = STRDUP((char*)ohRPT.ResourceTag.Data);
			}
			if (bi->name == NULL) {
				LOG(PIL_CRIT, "Out of memory for strdup in %s"
				,	__FUNCTION__);
				free_bladehpi_hostlist(dev);
			        return S_OOPS;
			}

			bi->resourceId = ohRPT.ResourceId;
			bi->resourceCaps = ohRPT.ResourceCapabilities;
			dev->hostlist = g_list_append(dev->hostlist, bi);

			if (Debug) {
				LOG(PIL_DEBUG, "Blade '%s' has id %u, caps %x"
				, bi->name, bi->resourceId, bi->resourceCaps);
			}
			break;
		}
	} while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);

	ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
	if (ohrc != SA_OK) {
		LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
		,	__FUNCTION__, ohrc);
		free_bladehpi_hostlist(dev);
		return S_BADCONFIG;
	}

	if (ohupdate != ohdi.RptUpdateCount) {
		free_bladehpi_hostlist(dev);
		if(Debug){
			LOG(PIL_DEBUG, "Looping through entries again,"
				" count changed from %u to %u"
			,	ohupdate, ohdi.RptUpdateCount);
		}
		goto try_again;
	}

	dev->ohrptcnt = ohupdate;

	return S_OK;
}


static void
free_bladehpi_hostlist(struct pluginDevice *dev)
{
	if (dev->hostlist) {
		GList *node;
		while (NULL != (node = g_list_first(dev->hostlist))) {
			dev->hostlist = 
				g_list_remove_link(dev->hostlist, node);
			FREE(((struct blade_info *)node->data)->name);
			FREE(node->data);
			g_list_free(node);
		}
		dev->hostlist = NULL;
	}
	dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
}


static int
get_num_tokens(char *str)
{
	int 	namecount = 0;

	while (*str != EOS) {
		str += strspn(str, WHITESPACE);
		if (*str == EOS)
			break;
		str += strcspn(str, WHITESPACE);
		namecount++;
	}
	return namecount;
}
