#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libxml/xpath.h>
#include <ccs.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <resgroup.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <list.h>
#include <ctype.h>
#include <restart_counter.h>
#include <reslist.h>
#include <pthread.h>
#include <dirent.h>
#include <libgen.h>
#ifndef NO_CCS
#include <logging.h>
#endif


/**
   Store a new resource rule in the given rule list.

   @param rulelist	List of rules to store new rule in.
   @param newrule	New rule to store.
   @return		0 on success or -1 if rule with same name
			already exists in rulelist
 */
static int
store_rule(resource_rule_t **rulelist, resource_rule_t *newrule)
{
	resource_rule_t *curr;

	list_do(rulelist, curr) {
		if (!strcasecmp(newrule->rr_type, curr->rr_type)) {
#ifdef NO_CCS
			fprintf(stderr, "Error storing %s: Duplicate\n",
				newrule->rr_type);
#else
			logt_print(LOG_ERR, "Error storing %s: Duplicate\n",
			       newrule->rr_type);
#endif
			return -1;
		}

	} while (!list_done(rulelist, curr));
	
	/* insert sorted in alphabetical order so rg_test produces
	 * reproducible output all the time */
	list_do(rulelist, curr) {
		if (strcasecmp(newrule->rr_type, curr->rr_type) < 0) {
			list_insert(&curr, newrule);
			/* reset list if we have a new low */
			if (curr == *rulelist)
				*rulelist = newrule;
			return 0;
		}
	} while (!list_done(rulelist, curr));
		
	list_insert(rulelist, newrule);
	return 0;
}


/**
   Obliterate a resource_rule_t structure.

   @param rr		Resource rule to free.
 */
static void
destroy_resource_rule(resource_rule_t *rr)
{
	int x;

	if (rr->rr_type)
		free(rr->rr_type);
	if (rr->rr_agent)
		free(rr->rr_agent);
	if (rr->rr_version)
		free(rr->rr_version);

	if (rr->rr_attrs) {
		for (x = 0; rr->rr_attrs &&
		     rr->rr_attrs[x].ra_name; x++) {
			free(rr->rr_attrs[x].ra_name);
			if (rr->rr_attrs[x].ra_value)
				free(rr->rr_attrs[x].ra_value);
		}

		free(rr->rr_attrs);
	}

	if (rr->rr_actions) {
		for (x = 0; rr->rr_actions &&
		     rr->rr_actions[x].ra_name; x++) {
			free(rr->rr_actions[x].ra_name);
		}

		free(rr->rr_actions);
	}

	if (rr->rr_childtypes) {
		for (x = 0; rr->rr_childtypes &&
		     rr->rr_childtypes[x].rc_name; x++)
			free(rr->rr_childtypes[x].rc_name);
		free(rr->rr_childtypes);
	}

	free(rr);
}


/**
   Destroy a list of resource rules.

   @param rules		List of rules to destroy.
 */
void
destroy_resource_rules(resource_rule_t **rules)
{
	resource_rule_t *rr;

	while ((rr = *rules)) {
		list_remove(rules, rr);
		destroy_resource_rule(rr);
	}
}


/**
   Get and store the maxparents (max instances) attribute for a given
   resource rule set.

   @param doc		Pre-parsed XML document pointer.
   @param ctx		Pre-allocated XML XPath context pointer.
   @param base		XPath prefix to search
   @param rr		Resource rule to store new information in.
 */
static void
_get_maxparents(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
	       	resource_rule_t *rr)
{
	char xpath[256];
	char *ret;

	snprintf(xpath, sizeof(xpath),
		 "%s/attributes/@maxinstances",
		 base);
	ret = xpath_get_one(doc, ctx, xpath);
	if (ret) {
		rr->rr_maxrefs = atoi(ret);
		if (rr->rr_maxrefs < 0)
			rr->rr_maxrefs = 0;
		free(ret);
	}
}


/**
   Get and store a bit field.

   @param doc		Pre-parsed XML document pointer.
   @param ctx		Pre-allocated XML XPath context pointer.
   @param base		XPath prefix to search
   @param rr		Resource rule to store new information in.
 */
static void
_get_rule_flag(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
	       resource_rule_t *rr, const char *flag, int bit)
{
	char xpath[256];
	char *ret;

	snprintf(xpath, sizeof(xpath),
		 "%s/attributes/@%s",
		 base, flag);
	ret = xpath_get_one(doc, ctx, xpath);
	if (ret) {
		if (atoi(ret)) {
			rr->rr_flags |= bit;
		} else {
			rr->rr_flags &= ~bit;
		}
		free(ret);
	}
}


/**
   Get and store the version

   @param doc		Pre-parsed XML document pointer.
   @param ctx		Pre-allocated XML XPath context pointer.
   @param base		XPath prefix to search
   @param rr		Resource rule to store new information in.
 */
static void
_get_version(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
	     resource_rule_t *rr)
{
	char xpath[256];
	char *ret;

	snprintf(xpath, sizeof(xpath), "%s/@version", base);
	ret = xpath_get_one(doc, ctx, xpath);
	if (ret) {
		rr->rr_version = ret;
		free(ret);
	}
	rr->rr_version = NULL;
}


int
expand_time (char *val)
{
	int curval, len;
	int ret = 0;
	char *start = val, ival[16];

	if (!val)
		return (time_t)0;

	while (start[0]) {

		len = 0;
		curval = 0;
		memset(ival, 0, sizeof(ival));

		while (isdigit(start[len])) {
			ival[len] = start[len];
			len++;
		}

		if (len) {
			curval = atoi(ival);
		} else {
			len = 1;
		}

		switch(start[len]) {
		case 0:
		case 'S':
		case 's':
			break;
		case 'M':
        	case 'm':
			curval *= 60;
			break;
		case 'h':
		case 'H':
			curval *= 3600;
			break;
		case 'd':
		case 'D':
			curval *= 86400;
			break;
		case 'w':
		case 'W':
			curval *= 604800;
			break;
		case 'y':
		case 'Y':
			curval *= 31536000;
			break;
		default:
			curval = 0;
		}

		ret += (time_t)curval;
		start += len;
	}

	return ret;
}



/**
 * Store a resource action
 * @param actsp		Action array; may be modified and returned!
 * @param name		Name of the action
 * @param depth		Resource depth (status/monitor; -1 means *ALL LEVELS*
 * 			... this means that only the highest-level check depth
 * 			will ever be performed!)
 * @param timeout	Timeout (not used)
 * @param interval	Time interval for status/monitor
 * @return		0 on success, -1 on failure
 * 
 */
int
store_action(resource_act_t **actsp, char *name, int depth,
	     int timeout, int interval)
{
	int x = 0, replace = 0;
	resource_act_t *acts = *actsp;

	if (!name)
		return -1;
	
	if (depth < 0 && timeout < 0 && interval < 0)
		return -1;

	if (!acts) {
		/* Can't create with anything < 0 */
		if (depth < 0 || timeout < 0 || interval < 0)
			return -1;
		
		acts = malloc(sizeof(resource_act_t) * 2);
		if (!acts)
			return -1;
		acts[0].ra_name = name;
		acts[0].ra_depth = depth;
		acts[0].ra_timeout = timeout;
		acts[0].ra_interval = interval;
		acts[0].ra_last = 0;
		acts[1].ra_name = NULL;

		*actsp = acts;
		return 0;
	}

	for (x = 0; acts[x].ra_name; x++) {
		if (!strcmp(acts[x].ra_name, name) &&
		    (depth == acts[x].ra_depth || depth == -1)) {
			printf("Replacing action '%s' depth %d: ",
			       name, acts[x].ra_depth);
			if (timeout >= 0) {
				printf("timeout: %d->%d ",
				       (int)acts[x].ra_timeout,
				       (int)timeout);
				acts[x].ra_timeout = timeout;
			}
			if (interval >= 0) {
				printf("interval: %d->%d",
				       (int)acts[x].ra_interval,
				       (int)interval);
				acts[x].ra_interval = interval;
			}
			acts[x].ra_last = 0;
			printf("\n");
			replace = 1;
		}
	}
	
	if (replace)
		/* If we replaced something, we're done */
		return 1;
	
	/* Can't create with anything < 0 */
	if (depth < 0 || timeout < 0 || interval < 0)
		return -1;

	acts = realloc(acts, sizeof(resource_act_t) * (x+2));
	if (!acts)
		return -1;
	
	acts[x].ra_name = name;
	acts[x].ra_depth = depth;
	acts[x].ra_timeout = timeout;
	acts[x].ra_interval = interval;
	acts[x].ra_last = 0;

	acts[x+1].ra_name = NULL;

	*actsp = acts;
	return 0;
}


static void
_get_actions(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
		 resource_rule_t *rr)
{
	char xpath[256];
	int idx = 0;
	char *act, *ret;
	int interval, timeout, depth;

	do {
		interval = 0;
		depth = 0;
		act = NULL;
		timeout = 0;

		snprintf(xpath, sizeof(xpath),
			 "%s/action[%d]/@name", base, ++idx);

		act = xpath_get_one(doc, ctx, xpath);
		if (!act)
			break;

		snprintf(xpath, sizeof(xpath),
			 "%s/action[%d]/@timeout", base, idx);
		ret = xpath_get_one(doc, ctx, xpath);
		if (ret) {
			timeout = expand_time(ret);
			if (timeout < 0)
				timeout = 0;
			free(ret);
		}

		snprintf(xpath, sizeof(xpath),
			 "%s/action[%d]/@interval", base, idx);
		ret = xpath_get_one(doc, ctx, xpath);
		if (ret) {
			interval = expand_time(ret);
			if (interval < 1)
				interval = 1;
			free(ret);
		}

		if (!strcmp(act, "status") || !strcmp(act, "monitor")) {
			snprintf(xpath, sizeof(xpath),
				 "%s/action[%d]/@depth", base, idx);
			ret = xpath_get_one(doc, ctx, xpath);
			if (ret) {
				depth = atoi(ret);
				if (depth < 0)
					depth = 0;
				free(ret);
			}
		}

		if (store_action(&rr->rr_actions, act, depth, timeout,
				 interval) != 0)
			free(act);
	} while (1);
}




/**
   Store an attribute with the given name, value, and flags in a resource_t
   structure.
   XXX This could be rewritten to use the list macros.

   @param attrsp	Attribute array to store new attribute in.
   @param name		Name of attribute (must be non-null)
   @param value		Value of attribute
   @param flags		Attribute flags, or 0 if none.
   @return		0 on success, nonzero on error/failure
 */
int
store_attribute(resource_attr_t **attrsp, char *name, char *value, int flags)
{
	int x = 0;
	resource_attr_t *attrs = *attrsp;

	if (!name)
		return -1;

	if (!attrs) {
		attrs = malloc(sizeof(resource_attr_t) * 2);
		if (!attrs)
			return -1;
		attrs[0].ra_name = name;
		attrs[0].ra_value = value;
		attrs[0].ra_flags = flags;
		attrs[1].ra_name = NULL;
		attrs[1].ra_value= NULL;

		*attrsp = attrs;
		return 0;
	}

	for (x = 0; attrs[x].ra_name; x++);

	attrs = realloc(attrs, sizeof(resource_attr_t) * (x+2));
	if (!attrs)
		return -1;

	/* Primary attribute goes first.  This makes this interaction
	with CCS work way faster. */
	if (flags & RA_PRIMARY) {
		attrs[x].ra_name = attrs[0].ra_name;
		attrs[x].ra_value = attrs[0].ra_value;
		attrs[x].ra_flags = attrs[0].ra_flags;
		attrs[0].ra_name = name;
		attrs[0].ra_value = value;
		attrs[0].ra_flags = flags;
	} else {
		attrs[x].ra_name = name;
		attrs[x].ra_value = value;
		attrs[x].ra_flags = flags;
	}
	attrs[x+1].ra_name = NULL;
	attrs[x+1].ra_value = NULL;

	*attrsp = attrs;
	return 0;
}


/**
   Store a child type in the child array of a resource rule.
   XXX Could be rewritten to use list macros.

   @param childp	Child array.  Might be modified.
   @param name		Name of child type
   @param start		Start level
   @param stop		Stop level
   @param forbid	Do NOT allow this child type to exist
   @param flags		set to 1 to note that it was defined inline
   @return		0 on success, nonzero on failure
 */
static int
store_childtype(resource_child_t **childp, char *name, int start,
		int stop, int forbid, int flags)
{
	int x = 0;
	resource_child_t *child = *childp;

	if (!name)
		return -1;

	if (!child) {
		child = malloc(sizeof(resource_child_t) * 2);
		if (!child)
			return -1;
		child[0].rc_name = name;
		child[0].rc_startlevel = start;
		child[0].rc_stoplevel = stop;
		child[0].rc_forbid = forbid;
		child[0].rc_flags = flags;
		child[1].rc_name = NULL;

		*childp = child;
		return 0;
	}

	for (x = 0; child[x].rc_name; x++);

	child = realloc(child, sizeof(resource_child_t) * (x+2));
	if (!child)
		return -1;

	child[x].rc_name = name;
	child[x].rc_startlevel = start;
	child[x].rc_stoplevel = stop;
	child[x].rc_forbid = forbid;
	child[x].rc_flags = flags;
	child[x+1].rc_name = NULL;

	*childp = child;
	return 0;
}


/**
   Print a resource_t structure to stdout

   @param rr		Resource rule to print.
 */
void
print_resource_rule(FILE *fp, resource_rule_t *rr)
{
	int x;

	fprintf(fp, "Resource Rules for \"%s\"\n", rr->rr_type);

	if (rr->rr_version)
		fprintf(fp, "OCF API Version: %s\n", rr->rr_version);

	if (rr->rr_maxrefs)
		fprintf(fp, "Max instances: %d\n", rr->rr_maxrefs);
	if (rr->rr_agent)
		fprintf(fp, "Agent: %s\n", basename(rr->rr_agent));

	fprintf(fp, "Flags: ");
	if (rr->rr_flags) {
		if (rr->rr_flags & RF_INIT)
			fprintf(fp, "init_on_add ");
		if (rr->rr_flags & RF_DESTROY)
			fprintf(fp, "destroy_on_delete ");
	} else {
		fprintf(fp, "(none)");
	}
	fprintf(fp, "\n");
	
	fprintf(fp, "Attributes:\n");
	if (!rr->rr_attrs) {
		fprintf(fp, "  - None -\n");
		goto actions;
	}

	for (x = 0; rr->rr_attrs[x].ra_name; x++) {
		fprintf(fp, "  %s", rr->rr_attrs[x].ra_name);

		if (!rr->rr_attrs[x].ra_flags && !rr->rr_attrs[x].ra_value) {
			fprintf(fp, "\n");
			continue;
		}

		if (rr->rr_attrs[x].ra_flags) {
			fprintf(fp, " [");
			if (rr->rr_attrs[x].ra_flags & RA_PRIMARY)
				fprintf(fp, " primary");
			if (rr->rr_attrs[x].ra_flags & RA_UNIQUE)
				fprintf(fp, " unique");
			if (rr->rr_attrs[x].ra_flags & RA_REQUIRED)
				fprintf(fp, " required");
			if (rr->rr_attrs[x].ra_flags & RA_INHERIT)
				fprintf(fp, " inherit");
			if (rr->rr_attrs[x].ra_flags & RA_RECONFIG)
				fprintf(fp, " reconfig");
			fprintf(fp, " ]");
		}

		if (rr->rr_attrs[x].ra_value)
			fprintf(fp, " default=\"%s\"\n", rr->rr_attrs[x].ra_value);
		else
			fprintf(fp, "\n");
	}

actions:
	fprintf(fp, "Actions:\n");
	if (!rr->rr_actions) {
		fprintf(fp, "  - None -\n");
		goto children;
	}

	for (x = 0; rr->rr_actions[x].ra_name; x++) {
		fprintf(fp, "  %s\n", rr->rr_actions[x].ra_name);
		if (rr->rr_actions[x].ra_timeout)
			fprintf(fp, "    Timeout (hint): %d seconds\n",
			       (int)rr->rr_actions[x].ra_timeout);
		if (rr->rr_actions[x].ra_depth)
			fprintf(fp, "    OCF Check Depth (status/monitor): "
			       "%d seconds\n",
			       (int)rr->rr_actions[x].ra_depth);
		if (rr->rr_actions[x].ra_interval)
			fprintf(fp, "    Check Interval: %d seconds\n",
			       (int)rr->rr_actions[x].ra_interval);
	}


children:
	fprintf(fp, "Explicitly defined child resource types:\n");
	if (!rr->rr_childtypes) {
		fprintf(fp, "  - None -\n\n");
		return;
	}
	for (x = 0; rr->rr_childtypes[x].rc_name; x++) {
		fprintf(fp, "  %s", rr->rr_childtypes[x].rc_name);
		if (rr->rr_childtypes[x].rc_forbid) {
			fprintf(fp, " (forbidden)\n");
			continue;
		}
		if (rr->rr_childtypes[x].rc_startlevel ||
		    rr->rr_childtypes[x].rc_stoplevel) {
			fprintf(fp, " [");

			if (rr->rr_childtypes[x].rc_startlevel) {
				fprintf(fp, " startlevel = %d",
				       rr->rr_childtypes[x].rc_startlevel);
			}

			if (rr->rr_childtypes[x].rc_stoplevel) {
				fprintf(fp, " stoplevel = %d",
				       rr->rr_childtypes[x].rc_stoplevel);
			}
			fprintf(fp, " ] ");
		}

		fprintf(fp, "\n");
	}

	fprintf(fp, "\n");
}


void
dump_resource_rules(FILE *fp, resource_rule_t **rules)
{
	resource_rule_t *curr;
	int x;

	list_for(rules, curr, x) {
		print_resource_rule(fp, curr);
	}
}


void
print_resource_rules(resource_rule_t **rules)
{
	dump_resource_rules(stdout, rules);
}


/**
   Get and store attributes for a given instance of a resource rule.

   @param doc		Pre-parsed XML document pointer.
   @param ctx		Pre-allocated XML XPath context pointer.
   @param base		XPath prefix to search
   @param rr		Resource rule to store new information in.
   @return		0
 */
static int
_get_rule_attrs(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
		resource_rule_t *rr)
{
	char *ret, *attrname, *dflt = NULL, xpath[256];
	int x, flags, primary_found = 0;

	for (x = 1; 1; x++) {
		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@name",
 			 base, x);

		ret = xpath_get_one(doc,ctx,xpath);
		if (!ret)
			break;

		flags = 0;
		attrname = ret;
		
		/*
		   See if there's a default value.
		 */
		snprintf(xpath, sizeof(xpath),
			 "%s/parameter[%d]/content/@default", base, x);
		dflt = xpath_get_one(doc,ctx,xpath);

		/*
		   See if this is either the primary identifier or
		   a required field.
		 */
		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@required",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			if ((atoi(ret) != 0) || (ret[0] == 'y')) 
				flags |= RA_REQUIRED;
			free(ret);
		}

		/*
		   See if this is supposed to be unique
		 */
		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@unique",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			if ((atoi(ret) != 0) || (ret[0] == 'y'))
				flags |= RA_UNIQUE;
			free(ret);
		}

		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@primary",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			if ((atoi(ret) != 0) || (ret[0] == 'y')) {
				if (primary_found) {
					free(ret);
					printf("Multiple primary "
					       "definitions for "
					       "resource type %s\n",
					       rr->rr_type);
					return -1;
				}
				flags |= RA_PRIMARY;
				primary_found = 1;
			}
			free(ret);
		}

		/*
		   See if this can be reconfigured on the fly without a 
		   stop/start
		 */
		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@reconfig",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			if ((atoi(ret) != 0) || (ret[0] == 'y'))
				flags |= RA_RECONFIG;
			free(ret);
		}

		/*
		   See if this is supposed to be inherited
		 */
		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@inherit",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			flags |= RA_INHERIT;

			if (flags & (RA_REQUIRED | RA_PRIMARY | RA_UNIQUE)) {
				free(ret);
				printf("Can not inherit and be primary, "
				       "unique, or required\n");
				return -1;
			}
			/*
			   don't free ret.  Store as attr value.  If we had
			   a default value specified from above, free it;
			   inheritance supercedes a specified default value.
			 */
			if (dflt)
				free(dflt);
		} else {
			/*
			   Use default value, if specified, as the attribute
			   value.
			 */
			ret = dflt;
		}

		/*
		   Store the attribute.  We'll ensure all required
		   attributes are present soon.
		 */
		if (attrname)
			store_attribute(&rr->rr_attrs, attrname, ret, flags);
	}

	return 0;
}


/**
   Get and store attributes for a given instance of a resource.

   @param doc		Pre-parsed XML document pointer.
   @param ctx		Pre-allocated XML XPath context pointer.
   @param base		XPath prefix to search
   @param rr		Resource rule to store new information in.
   @return		0
 */
static int
_get_childtypes(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
		resource_rule_t *rr)
{
	char *ret, *childname, xpath[256];
	int x, startlevel = 0, stoplevel = 0, forbid = 0;

	for (x = 1; 1; x++) {
		snprintf(xpath, sizeof(xpath), "%s/child[%d]/@type",
			 base, x);

		ret = xpath_get_one(doc,ctx,xpath);
		if (!ret)
			break;

		startlevel = stoplevel = forbid = 0;
		childname = ret;

		/*
		   Try to get the start level if it exists
		 */
		snprintf(xpath, sizeof(xpath), "%s/child[%d]/@start",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			startlevel = atoi(ret);
			free(ret);
		}


		/*
		   Try to get the stop level if it exists
		 */
		snprintf(xpath, sizeof(xpath), "%s/child[%d]/@stop",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			stoplevel = atoi(ret);
			free(ret);
		}

		/*
		   Get the 'forbidden' flag if it exists
		 */
		snprintf(xpath, sizeof(xpath), "%s/child[%d]/@forbid",
			 base, x);
		if ((ret = xpath_get_one(doc,ctx,xpath))) {
			forbid = atoi(ret);
			free(ret);
		}

		/*
		   Store the attribute.  We'll ensure all required
		   attributes are present soon.
		 */
		if (childname)
			store_childtype(&rr->rr_childtypes, childname,
					startlevel, stoplevel, forbid, 0);
	}

	return 0;
}


/**
  Read a file from a stdout pipe.
 */
static int
read_pipe(int fd, char **file, size_t *length)
{
	char buf[4096];
	int n, done = 0;

	*file = NULL;
	*length = 0;

	while (!done) {

		n = read(fd, buf, sizeof(buf));
		if (n < 0) {

			if (errno == EINTR)
				continue;

			if (*file)
				free(*file);
			return -1;
		}

		if (n == 0 && (!*length))
			return 0;

		if (n == 0) {
			done = 1;
		}

		if (*file)
			*file = realloc(*file, (*length) + n + done);
		else
			*file = malloc(n + done);

		if (!*file)
			return -1;

		memcpy((*file) + (*length), buf, n);
		*length += (done + n);
	}

	/* Null terminator */
	(*file)[(*length) - 1] = 0;

	return 0;
}


static xmlDocPtr
read_resource_agent_metadata(char *filename)
{
	int pid;
	int _pipe[2];
	char *data;
	size_t size;
	xmlDocPtr doc;

	if (pipe(_pipe) == -1)
		return NULL;

	pid = fork();
	if (pid == -1) {
		close(_pipe[0]);
		close(_pipe[1]);
	}

	if (pid == 0) {
		/* child */
		close(0);
		close(1);
		close(2);

		close(_pipe[0]);
		dup2(_pipe[1], 1);
		close(_pipe[1]);
		
		/* exec */
		execl(filename, filename, "meta-data", NULL);
		exit(1);
	}

	close(_pipe[1]);
	/* parent */
	if (read_pipe(_pipe[0], &data, &size) == -1) {
		close(_pipe[0]);
		return NULL;
	}

	waitpid(pid, NULL, 0);
	close(_pipe[0]);

	if (!size)
		return NULL;

	doc = xmlParseMemory(data, size);
	free(data);
	return doc;
}


/**
   Load the XML rule set for a resource and store attributes, constructing
   a new resource_t structure.

   @param filename	File name to load rules from
   @param rules		Rule list to add new rules to
   @return		0
 */
static int
load_resource_rulefile(char *filename, resource_rule_t **rules)
{
	resource_rule_t *rr = NULL;
	xmlDocPtr doc = NULL;
	xmlXPathContextPtr ctx = NULL;
	int ruleid=0;
	char *type;
	char base[256];

	doc = read_resource_agent_metadata(filename);
	if (!doc)
		return 0;
	ctx = xmlXPathNewContext(doc);

	do {
		/* Look for resource types */
		snprintf(base, sizeof(base), "/resource-agent[%d]/@name",
			 ++ruleid);
		type = xpath_get_one(doc, ctx, base);
		if (!type)
			break;
		
		if (!strcasecmp(type, "action")) {
#ifdef NO_CCS
			fprintf(stderr,
				"Error: Resource type '%s' is reserved",
				type);
#else
			logt_print(LOG_ERR,
				"Error: Resource type '%s' is reserved",
				type);
#endif
			free(type);
			break;
		}

		rr = malloc(sizeof(*rr));
		if (!rr)
			break;
		memset(rr,0,sizeof(*rr));

		rr->rr_flags = RF_INIT | RF_DESTROY;
		rr->rr_type = type;
		snprintf(base, sizeof(base), "/resource-agent[%d]", ruleid);

		/*
		   First, grab the global attributes if existent
		 */
		_get_version(doc, ctx, base, rr);

		snprintf(base, sizeof(base),
			 "/resource-agent[%d]/special[@tag=\"rgmanager\"]",
			 ruleid);
		_get_maxparents(doc, ctx, base, rr);
		_get_rule_flag(doc, ctx, base, rr, "init_on_add", RF_INIT);
		_get_rule_flag(doc, ctx, base, rr, "destroy_on_delete", RF_DESTROY);
		rr->rr_agent = strdup(filename);

		/*
		   Second, add the children fields
		 */
		_get_childtypes(doc, ctx, base, rr);

		/*
		   Get the OCF status check intervals/monitor.
		 */
		snprintf(base, sizeof(base), "/resource-agent[%d]/actions",
			 ruleid);
		_get_actions(doc, ctx, base, rr);

	
		/*
		   Last, load the attributes from our XML file and their
		   respective instantiations from CCS
		 */
		snprintf(base, sizeof(base),
			 "/resource-agent[%d]/parameters", ruleid);
		if (_get_rule_attrs(doc, ctx, base, rr) < 0) {
			destroy_resource_rule(rr);
			rr = NULL;
		}

		if (!rr)
			continue;

		if (store_rule(rules, rr) != 0) {
			destroy_resource_rule(rr);
			rr = NULL;
		}
	} while (1);

	if (ctx)
		xmlXPathFreeContext(ctx);
	if (doc)
		xmlFreeDoc(doc);

	return 0;
}


/**
   Load all the resource rules we can find from our resource root 
   directory.

   @param rules		Rule list to create/add to
   @return		0 on success, -1 on failure.  Sucess does not
   			imply any rules have been found; only that no
			errors were encountered.
  */
int
load_resource_rules(const char *rpath, resource_rule_t **rules)
{
	DIR *dir;
	struct dirent *de;
	char *fn, *dot;
	char path[2048];
	struct stat st_buf;

	dir = opendir(rpath);
	if (!dir)
		return -1;

	xmlInitParser();
	while ((de = readdir(dir))) {
		
		fn = basename(de->d_name);
		if (!fn)
			continue;
		
		/* Ignore files with common backup extension */
		if ((fn != NULL) && (strlen(fn) > 0) && 
			(fn[strlen(fn)-1] == '~')) 
			continue;

 		dot = strrchr(fn, '.');
 		if (dot) {
 			/* Ignore RPM installed save files, patches,
 			   diffs, etc. */
 			if (!strncasecmp(dot, ".rpm", 4)) {
 				fprintf(stderr, "Warning: "
 					"Ignoring %s/%s: Bad extension %s\n",
 					rpath, de->d_name, dot);
 				continue;
 			}
 		}

		snprintf(path, sizeof(path), "%s/%s",
			 rpath, de->d_name);
		
		if (stat(path, &st_buf))
			continue;
		
		if (S_ISDIR(st_buf.st_mode))
			continue;
		
  		if (st_buf.st_mode & (S_IXUSR|S_IXOTH|S_IXGRP)) {
  			printf("Loading resource rule from %s\n", path);
   			load_resource_rulefile(path, rules);
  		}
	}
	xmlCleanupParser();

	closedir(dir);

	return 0;
}


/**
   Find a resource rule given its type.

   @param rulelist	Rule list to search
   @param type		Rule type identifier
   @return		Resource rule or NULL if not found.
 */
resource_rule_t *
find_rule_by_type(resource_rule_t **rulelist, const char *type)
{
	resource_rule_t *curr = NULL;

	list_do(rulelist, curr) {
		if (!strcmp(curr->rr_type, type))
			return curr;
	} while (!list_done(rulelist, curr));

	return NULL;
}
