diff --git a/include/crm/common/util.h b/include/crm/common/util.h
index 43485fd1f8..58a23bf36e 100644
--- a/include/crm/common/util.h
+++ b/include/crm/common/util.h
@@ -1,194 +1,195 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program 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.1 of the License, or (at your option) any later version.
  * 
  * This software 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 this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #ifndef CRM_COMMON_UTIL__H
 #define CRM_COMMON_UTIL__H
 
 #include <signal.h>
 #include <crm/common/xml.h>
 
 #if SUPPORT_HEARTBEAT
 #  include <hb_api.h>
 #  include <ocf/oc_event.h>
 #endif
 
 #include <lrm/lrm_api.h>
 
 #include <sys/types.h>
 #include <stdlib.h>
 #include <limits.h>
 
 #define DEBUG_INC SIGUSR1
 #define DEBUG_DEC SIGUSR2
 
 extern unsigned int crm_log_level;
 extern gboolean crm_config_error;
 extern gboolean crm_config_warning;
 
 #define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); }
 #define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); }
 
 extern gboolean crm_log_init(
     const char *entity, int level, gboolean coredir, gboolean to_stderr,
     int argc, char **argv);
 
 /* returns the old value */
 extern unsigned int set_crm_log_level(unsigned int level);
 
 extern unsigned int get_crm_log_level(void);
 
 extern char *crm_itoa(int an_int);
 
 extern char *crm_strdup_fn(const char *a, const char *file, const char *fn, int line);
 
 extern char *generate_hash_key(const char *crm_msg_reference, const char *sys);
 
 extern char *generate_hash_value(const char *src_node, const char *src_subsys);
 
 extern gboolean decodeNVpair(const char *srcstring,
 			     char separator, char **name, char **value);
 
 extern int compare_version(const char *version1, const char *version2);
 
 
 extern char *generateReference(const char *custom1, const char *custom2);
 
 extern void alter_debug(int nsig);
 
 extern void g_hash_destroy_str(gpointer data);
 
 extern gboolean crm_is_true(const char * s);
 
 extern int crm_str_to_boolean(const char * s, int * ret);
 
-extern long crm_get_msec(const char * input);
+extern unsigned long long crm_get_msec(const char * input);
+extern unsigned long long crm_get_interval(const char * input);
 
 extern const char *op_status2text(op_status_t status);
 
 extern char *generate_op_key(
 	const char *rsc_id, const char *op_type, int interval);
 
 extern gboolean parse_op_key(
 	const char *key, char **rsc_id, char **op_type, int *interval);
 
 extern char *generate_notify_key(
 	const char *rsc_id, const char *notify_type, const char *op_type);
 
 extern char *generate_transition_magic_v202(
 	const char *transition_key, int op_status);
 
 extern char *generate_transition_magic(
 	const char *transition_key, int op_status, int op_rc);
 
 extern gboolean decode_transition_magic(
 	const char *magic, char **uuid,
 	int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc);
 
 extern char *generate_transition_key(int action, int transition_id, int target_rc, const char *node);
 
 extern gboolean decode_transition_key(
     const char *key, char **uuid, int *action, int *transition_id, int *target_rc);
 
 extern char *crm_concat(const char *prefix, const char *suffix, char join);
 
 extern gboolean decode_op_key(
 	const char *key, char **rsc_id, char **op_type, int *interval);
 
 extern void filter_action_parameters(xmlNode *param_set, const char *version);
 extern void filter_reload_parameters(xmlNode *param_set, const char *restart_string);
 
 #define safe_str_eq(a, b) crm_str_eq(a, b, FALSE)
 
 extern gboolean crm_str_eq(const char *a, const char *b, gboolean use_case);
 extern gboolean safe_str_neq(const char *a, const char *b);
 extern int crm_parse_int(const char *text, const char *default_text);
 extern long crm_int_helper(const char *text, char **end_text);
 #define crm_atoi(text, default_text) crm_parse_int(text, default_text)
 
 extern void crm_abort(const char *file, const char *function, int line,
 		      const char *condition, gboolean do_core, gboolean do_fork);
 
 extern char *generate_series_filename(
 	const char *directory, const char *series, int sequence, gboolean bzip);
 
 extern int get_last_sequence(const char *directory, const char *series);
 
 extern void write_last_sequence(
 	const char *directory, const char *series, int sequence, int max);
 
 extern void crm_make_daemon(
 	const char *name, gboolean daemonize, const char *pidfile);
 
 typedef struct pe_cluster_option_s {
 	const char *name;
 	const char *alt_name;
 	const char *type;
 	const char *values;
 	const char *default_value;
 
 	gboolean (*is_valid)(const char *);
 
 	const char *description_short;
 	const char *description_long;
 	
 } pe_cluster_option;
 
 extern const char *cluster_option(
 	GHashTable* options, gboolean(*validate)(const char*),
 	const char *name, const char *old_name, const char *def_value);
 
 extern const char *get_cluster_pref(
 	GHashTable *options, pe_cluster_option *option_list, int len, const char *name);
 
 extern void config_metadata(
 	const char *name, const char *version, const char *desc_short, const char *desc_long,
 	pe_cluster_option *option_list, int len);
 
 extern void verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len);
 extern gboolean check_time(const char *value);
 extern gboolean check_timer(const char *value);
 extern gboolean check_boolean(const char *value);
 extern gboolean check_number(const char *value);
 
 extern int char2score(const char *score);
 extern char *score2char(int score);
 
 extern gboolean crm_is_writable(
 	const char *dir, const char *file,
 	const char *user, const char *group, gboolean need_both);
 
 extern long long crm_set_bit(const char *function, long long word, long long bit);
 extern long long crm_clear_bit(const char *function, long long word, long long bit);
 
 
 #define set_bit(word, bit) word = crm_set_bit(__PRETTY_FUNCTION__, word, bit) 
 #define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, word, bit) 
 
 #define set_bit_inplace(word, bit) word |= bit 
 #define clear_bit_inplace(word, bit) word &= ~bit 
 
 extern gboolean is_set(long long action_list, long long action);
 extern gboolean is_not_set(long long action_list, long long action);
 extern gboolean is_set_any(long long action_list, long long action);
 
 extern gboolean is_openais_cluster(void);
 extern gboolean is_heartbeat_cluster(void);
 
 extern xmlNode *cib_recv_remote_msg(void *session);
 extern void cib_send_remote_msg(void *session, xmlNode *msg);
 
 #endif
diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index 61ad6e5417..a0a6642af3 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,263 +1,264 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program 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.1 of the License, or (at your option) any later version.
  * 
  * This software 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 this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #ifndef XML_TAGS__H
 #define XML_TAGS__H
 
 #define CIB_OPTIONS_FIRST "cib-bootstrap-options"
 
 #define F_CRM_TASK			"crm_task"
 #define F_CRM_HOST_TO			"crm_host_to"
 #define F_CRM_MSG_TYPE			F_SUBTYPE
 #define F_CRM_SYS_TO			"crm_sys_to"
 #define F_CRM_SYS_FROM			"crm_sys_from"
 #define F_CRM_HOST_FROM			F_ORIG
 #define F_CRM_REFERENCE			XML_ATTR_REFERENCE
 #define F_CRM_VERSION			XML_ATTR_VERSION
 #define F_CRM_ORIGIN			"origin"
 #define F_CRM_JOIN_ID			"join_id"
 #define F_CRM_ELECTION_ID		"election-id"
 #define F_CRM_ELECTION_OWNER		"election-owner"
 #define F_CRM_TGRAPH			"crm-tgraph"
 #define F_CRM_TGRAPH_INPUT		"crm-tgraph-in"
 
 /*---- Common tags/attrs */
 #define XML_DIFF_MARKER			"__crm_diff_marker__"
 #define XML_ATTR_TAGNAME		F_XML_TAGNAME
 #define XML_TAG_CIB			"cib"
 #define XML_TAG_FAILED			"failed"
 
 #define XML_ATTR_CRM_VERSION		"crm_feature_set"
 #define XML_ATTR_DIGEST			"digest"
 #define XML_ATTR_VALIDATION		"validate-with"
 
 #define XML_ATTR_HAVE_QUORUM		"have-quorum"
 #define XML_ATTR_GENERATION		"epoch"
 #define XML_ATTR_GENERATION_ADMIN	"admin_epoch"
 #define XML_ATTR_NUMUPDATES		"num_updates"
 #define XML_ATTR_TIMEOUT		"timeout"
 #define XML_ATTR_ORIGIN			"crm-debug-origin"
 #define XML_ATTR_TSTAMP			"crm-timestamp"
 #define XML_CIB_ATTR_WRITTEN		"cib-last-written"
 #define XML_ATTR_VERSION		"version"
 #define XML_ATTR_DESC			"description"
 #define XML_ATTR_ID			"id"
 #define XML_ATTR_IDREF			"id-ref"
 #define XML_ATTR_ID_LONG		"long-id"
 #define XML_ATTR_TYPE			"type"
 #define XML_ATTR_FILTER_TYPE		"type-filter"
 #define XML_ATTR_FILTER_ID		"id-filter"
 #define XML_ATTR_FILTER_PRIORITY	"priority-filter"
 #define XML_ATTR_VERBOSE		"verbose"
 #define XML_ATTR_OP			"op"
 #define XML_ATTR_DC			"is_dc"
 #define XML_ATTR_DC_UUID		"dc-uuid"
 
 #define XML_BOOLEAN_TRUE		"true"
 #define XML_BOOLEAN_FALSE		"false"
 #define XML_BOOLEAN_YES			XML_BOOLEAN_TRUE
 #define XML_BOOLEAN_NO			XML_BOOLEAN_FALSE
 
 #define XML_TAG_OPTIONS			"options"
 
 /*---- top level tags/attrs */
 #define XML_MSG_TAG			"crm_message"
 #define XML_MSG_TAG_DATA		"msg_data"
 #define XML_ATTR_REQUEST		"request"
 #define XML_ATTR_RESPONSE		"response"
 
 #define XML_ATTR_UNAME			"uname"
 #define XML_ATTR_UUID			"id"
 #define XML_ATTR_REFERENCE		"reference"
 
 #define XML_FAIL_TAG_RESOURCE		"failed_resource"
 
 #define XML_FAILRES_ATTR_RESID		"resource_id"
 #define XML_FAILRES_ATTR_REASON		"reason"
 #define XML_FAILRES_ATTR_RESSTATUS	"resource_status"
 
 #define XML_CRM_TAG_PING		"ping_response"
 #define XML_PING_ATTR_STATUS		"result"
 #define XML_PING_ATTR_SYSFROM		"crm_subsystem"
 
 #define XML_TAG_FRAGMENT		"cib_fragment"
 #define XML_ATTR_RESULT			"result"
 #define XML_ATTR_SECTION		"section"
 
 #define XML_FAIL_TAG_CIB		"failed_update"
 
 #define XML_FAILCIB_ATTR_ID		"id"
 #define XML_FAILCIB_ATTR_OBJTYPE	"object_type"
 #define XML_FAILCIB_ATTR_OP		"operation"
 #define XML_FAILCIB_ATTR_REASON		"reason"
 
 /*---- CIB specific tags/attrs */
 #define XML_CIB_TAG_SECTION_ALL		"all"
 #define XML_CIB_TAG_CONFIGURATION	"configuration"
 #define XML_CIB_TAG_STATUS       	"status"
 #define XML_CIB_TAG_RESOURCES		"resources"
 #define XML_CIB_TAG_NODES         	"nodes"
 #define XML_CIB_TAG_CONSTRAINTS   	"constraints"
 #define XML_CIB_TAG_CRMCONFIG   	"crm_config"
 #define XML_CIB_TAG_OPCONFIG		"op_defaults"
 #define XML_CIB_TAG_RSCCONFIG   	"rsc_defaults"
 
 #define XML_CIB_TAG_STATE         	"node_state"
 #define XML_CIB_TAG_NODE          	"node"
 #define XML_CIB_TAG_CONSTRAINT    	"constraint"
 #define XML_CIB_TAG_NVPAIR        	"nvpair"
 
 #define XML_CIB_TAG_PROPSET	   	"cluster_property_set"
 #define XML_TAG_ATTR_SETS	   	"instance_attributes"
 #define XML_TAG_META_SETS	   	"meta_attributes"
 #define XML_TAG_ATTRS			"attributes"
 #define XML_TAG_PARAMS			"parameters"
 
 #define XML_TAG_RESOURCE_REF		"resource_ref"
 #define XML_CIB_TAG_RESOURCE	  	"primitive"
 #define XML_CIB_TAG_GROUP	  	"group"
 #define XML_CIB_TAG_INCARNATION		"clone"
 #define XML_CIB_TAG_MASTER		"master"
 
 #define XML_RSC_ATTR_RESTART	  	"restart-type"
 #define XML_RSC_ATTR_ORDERED		"ordered"
 #define XML_RSC_ATTR_INTERLEAVE		"interleave"
 #define XML_RSC_ATTR_INCARNATION	"clone"
 #define XML_RSC_ATTR_INCARNATION_MAX	"clone-max"
 #define XML_RSC_ATTR_INCARNATION_NODEMAX	"clone-node-max"
 #define XML_RSC_ATTR_MASTER_MAX		"master-max"
 #define XML_RSC_ATTR_MASTER_NODEMAX	"master-node-max"
 #define XML_RSC_ATTR_STATE		"clone-state"
 #define XML_RSC_ATTR_MANAGED		"is-managed"
 #define XML_RSC_ATTR_TARGET_ROLE	"target-role"
 #define XML_RSC_ATTR_UNIQUE		"globally-unique"
 #define XML_RSC_ATTR_NOTIFY		"notify"
 #define XML_RSC_ATTR_STICKINESS		"resource-stickiness"
 #define XML_RSC_ATTR_FAIL_STICKINESS	"migration-threshold"
 #define XML_RSC_ATTR_FAIL_TIMEOUT	"failure-timeout"
 #define XML_RSC_ATTR_MULTIPLE		"multiple-active"
 #define XML_RSC_ATTR_PRIORITY		"priority"
 #define XML_OP_ATTR_ON_FAIL		"on-fail"
 #define XML_OP_ATTR_START_DELAY		"start-delay"
 #define XML_OP_ATTR_ALLOW_MIGRATE	"allow-migrate"
+#define XML_OP_ATTR_ORIGIN		"interval-origin"
 
 #define XML_CIB_TAG_LRM		  	"lrm"
 #define XML_LRM_TAG_RESOURCES     	"lrm_resources"
 #define XML_LRM_TAG_RESOURCE     	"lrm_resource"
 #define XML_LRM_TAG_AGENTS	     	"lrm_agents"
 #define XML_LRM_TAG_AGENT		"lrm_agent"
 #define XML_LRM_TAG_RSC_OP		"lrm_rsc_op"
 #define XML_AGENT_ATTR_CLASS		"class"
 #define XML_AGENT_ATTR_PROVIDER		"provider"
 #define XML_LRM_TAG_ATTRIBUTES		"attributes"
 
 #define XML_CIB_ATTR_REPLACE       	"replace"
 #define XML_CIB_ATTR_SOURCE       	"source"
 
 #define XML_CIB_ATTR_HEALTH       	"health"
 #define XML_CIB_ATTR_WEIGHT       	"weight"
 #define XML_CIB_ATTR_PRIORITY     	"priority"
 #define XML_CIB_ATTR_CLEAR        	"clear_on"
 #define XML_CIB_ATTR_SOURCE       	"source"
 
 #define XML_CIB_ATTR_JOINSTATE    	"join"
 #define XML_CIB_ATTR_EXPSTATE     	"expected"
 #define XML_CIB_ATTR_INCCM        	"in_ccm"
 #define XML_CIB_ATTR_CRMDSTATE    	"crmd"
 #define XML_CIB_ATTR_HASTATE    	"ha"
 
 #define XML_CIB_ATTR_SHUTDOWN       	"shutdown"
 #define XML_CIB_ATTR_STONITH	    	"stonith"
 
 #define XML_LRM_ATTR_INTERVAL		"interval"
 #define XML_LRM_ATTR_TASK		"operation"
 #define XML_LRM_ATTR_TASK_KEY		"operation_key"
 #define XML_LRM_ATTR_TARGET		"on_node"
 #define XML_LRM_ATTR_TARGET_UUID	"on_node_uuid"
 #define XML_LRM_ATTR_RSCID		"rsc-id"
 #define XML_LRM_ATTR_OPSTATUS		"op-status"
 #define XML_LRM_ATTR_RC			"rc-code"
 #define XML_LRM_ATTR_CALLID		"call-id"
 #define XML_LRM_ATTR_OP_DIGEST		"op-digest"
 #define XML_LRM_ATTR_OP_RESTART		"op-force-restart"
 #define XML_LRM_ATTR_RESTART_DIGEST	"op-restart-digest"
 
 #define XML_TAG_GRAPH			"transition_graph"
 #define XML_GRAPH_TAG_RSC_OP		"rsc_op"
 #define XML_GRAPH_TAG_PSEUDO_EVENT	"pseudo_event"
 #define XML_GRAPH_TAG_CRM_EVENT		"crm_event"
 
 #define XML_TAG_RULE			"rule"
 #define XML_RULE_ATTR_SCORE		"score"
 #define XML_RULE_ATTR_SCORE_ATTRIBUTE	"score-attribute"
 #define XML_RULE_ATTR_SCORE_MANGLED	"score-attribute-mangled"
 #define XML_RULE_ATTR_ROLE		"role"
 #define XML_RULE_ATTR_RESULT		"result"
 #define XML_RULE_ATTR_BOOLEAN_OP	"boolean-op"
 
 #define XML_TAG_EXPRESSION		"expression"
 #define XML_EXPR_ATTR_ATTRIBUTE		"attribute"
 #define XML_EXPR_ATTR_OPERATION		"operation"
 #define XML_EXPR_ATTR_VALUE		"value"
 #define XML_EXPR_ATTR_TYPE		"type"
 
 #define XML_CONS_TAG_RSC_DEPEND		"rsc_colocation"
 #define XML_CONS_TAG_RSC_ORDER		"rsc_order"
 #define XML_CONS_TAG_RSC_LOCATION	"rsc_location"
 #define XML_CONS_ATTR_SYMMETRICAL	"symmetrical"
 
 #define XML_COLOC_ATTR_SOURCE		"rsc"
 #define XML_COLOC_ATTR_SOURCE_ROLE	"rsc-role"
 #define XML_COLOC_ATTR_TARGET		"with-rsc"
 #define XML_COLOC_ATTR_TARGET_ROLE	"with-rsc-role"
 #define XML_COLOC_ATTR_NODE_ATTR	"node_attribute"
 
 #define XML_ORDER_ATTR_FIRST		"first-rsc"
 #define XML_ORDER_ATTR_THEN		"then-rsc"
 #define XML_ORDER_ATTR_FIRST_ACTION	"first-action"
 #define XML_ORDER_ATTR_THEN_ACTION	"then-action"
 
 #define XML_NVPAIR_ATTR_NAME        	"name"
 #define XML_NVPAIR_ATTR_VALUE        	"value"
 
 #define XML_NODE_ATTR_STATE		"state"
 
 #define XML_CONFIG_ATTR_DC_DEADTIME	"dc_deadtime"
 #define XML_CONFIG_ATTR_ELECTION_FAIL	"election_timeout"
 #define XML_CONFIG_ATTR_FORCE_QUIT	"shutdown_escalation"
 #define XML_CONFIG_ATTR_REANNOUNCE	"join_reannouce"
 #define XML_CONFIG_ATTR_RECHECK		"cluster_recheck_interval"
 
 #define XML_CIB_TAG_GENERATION_TUPPLE	"generation_tuple"
 
 #define XML_ATTR_TRANSITION_MAGIC	"transition-magic"
 #define XML_ATTR_TRANSITION_KEY		"transition-key"
 
 #define XML_ATTR_TE_NOWAIT		"op_no_wait"
 #define XML_ATTR_TE_TARGET_RC		"op_target_rc"
 #define XML_ATTR_TE_ALLOWFAIL		"op_allow_fail"
 #define XML_ATTR_LRM_PROBE		"lrm-is-probe"
 #define XML_TAG_TRANSIENT_NODEATTRS	"transient_attributes"
 
 #include <crm/common/xml.h> 
 
 #define ID(x) crm_element_value(x, XML_ATTR_ID)
 #define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE)
 #define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP)
 #define TYPE(x) crm_element_name(x) 
 
 #endif
diff --git a/lib/crm/common/utils.c b/lib/crm/common/utils.c
index 409b3ff512..177d62cc1b 100644
--- a/lib/crm/common/utils.c
+++ b/lib/crm/common/utils.c
@@ -1,1633 +1,1656 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program 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.1 of the License, or (at your option) any later version.
  * 
  * This software 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 this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <crm_internal.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 
 #include <stdlib.h>
 #include <limits.h>
 #include <ctype.h>
 #include <pwd.h>
 #include <grp.h>
 
 #include <ha_msg.h>
 #include <clplumbing/cl_log.h>
 #include <clplumbing/cl_signal.h>
 #include <clplumbing/cl_syslog.h>
 #include <clplumbing/cl_misc.h>
 #include <clplumbing/coredumps.h>
 #include <clplumbing/lsb_exitcodes.h>
 #include <clplumbing/cl_pidfile.h>
 
 #include <time.h> 
 
 #include <clplumbing/Gmain_timeout.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
+#include <crm/common/iso8601.h>
 
 #ifndef MAXLINE
 #    define MAXLINE 512
 #endif
 
 static uint ref_counter = 0;
 gboolean crm_assert_failed = FALSE;
 unsigned int crm_log_level = LOG_INFO;
 gboolean crm_config_error = FALSE;
 gboolean crm_config_warning = FALSE;
 const char *crm_system_name = "unknown";
 
 void crm_set_env_options(void);
 
 gboolean
 check_time(const char *value) 
 {
 	if(crm_get_msec(value) < 5000) {
 		return FALSE;
 	}
 	return TRUE;
 }
 
 gboolean
 check_timer(const char *value) 
 {
 	if(crm_get_msec(value) < 0) {
 		return FALSE;
 	}
 	return TRUE;
 }
 
 gboolean
 check_boolean(const char *value) 
 {
 	int tmp = FALSE;
 	if(crm_str_to_boolean(value, &tmp) != 1) {
 		return FALSE;
 	}
 	return TRUE;
 }
 
 gboolean
 check_number(const char *value) 
 {
 	errno = 0;
 	if(value == NULL) {
 		return FALSE;
 		
 	} else if(safe_str_eq(value, MINUS_INFINITY_S)) {
 		
 	} else if(safe_str_eq(value, INFINITY_S)) {
 
 	} else {
 		crm_int_helper(value, NULL);
 	}
 
 	if(errno != 0) {
 		return FALSE;
 	}
 	return TRUE;
 }
 
 int
 char2score(const char *score) 
 {
 	int score_f = 0;
 	
 	if(score == NULL) {
 		
 	} else if(safe_str_eq(score, MINUS_INFINITY_S)) {
 		score_f = -INFINITY;
 		
 	} else if(safe_str_eq(score, INFINITY_S)) {
 		score_f = INFINITY;
 		
 	} else if(safe_str_eq(score, "+"INFINITY_S)) {
 		score_f = INFINITY;
 		
 	} else {
 		score_f = crm_parse_int(score, NULL);
 		if(score_f > 0 && score_f > INFINITY) {
 			score_f = INFINITY;
 			
 		} else if(score_f < 0 && score_f < -INFINITY) {
 			score_f = -INFINITY;
 		}
 	}
 	
 	return score_f;
 }
 
 
 char *
 score2char(int score) 
 {
 
 	if(score >= INFINITY) {
 		return crm_strdup("+"INFINITY_S);
 
 	} else if(score <= -INFINITY) {
 		return crm_strdup("-"INFINITY_S);
 	} 
 	return crm_itoa(score);
 }
 
 
 const char *
 cluster_option(GHashTable* options, gboolean(*validate)(const char*),
 	       const char *name, const char *old_name, const char *def_value)
 {
 	const char *value = NULL;
 	CRM_ASSERT(name != NULL);
 
 	if(options != NULL) {
 		value = g_hash_table_lookup(options, name);
 	}
 
 	if(value == NULL && old_name && options != NULL) {
 		value = g_hash_table_lookup(options, old_name);
 		if(value != NULL) {
 			crm_config_warn("Using deprecated name '%s' for"
 				       " cluster option '%s'", old_name, name);
 			g_hash_table_insert(
 				options, crm_strdup(name), crm_strdup(value));
 			value = g_hash_table_lookup(options, old_name);
 		}
 	}
 
 	if(value == NULL) {
 		crm_debug_2("Using default value '%s' for cluster option '%s'",
 			    def_value, name);
 
 		if(options == NULL) {
 			return def_value;
 		}
 		
 		g_hash_table_insert(
 			options, crm_strdup(name), crm_strdup(def_value));
 		value = g_hash_table_lookup(options, name);
 	}
 	
 	if(validate && validate(value) == FALSE) {
 		crm_config_err("Value '%s' for cluster option '%s' is invalid."
 			      "  Defaulting to %s", value, name, def_value);
 		g_hash_table_replace(options, crm_strdup(name),
 				     crm_strdup(def_value));
 		value = g_hash_table_lookup(options, name);
 	}
 	
 	return value;
 }
 
 
 const char *
 get_cluster_pref(GHashTable *options, pe_cluster_option *option_list, int len, const char *name)
 {
 	int lpc = 0;
 	const char *value = NULL;
 	gboolean found = FALSE;
 	for(lpc = 0; lpc < len; lpc++) {
 		if(safe_str_eq(name, option_list[lpc].name)) {
 			found = TRUE;
 			value = cluster_option(options, 
 					       option_list[lpc].is_valid,
 					       option_list[lpc].name,
 					       option_list[lpc].alt_name,
 					       option_list[lpc].default_value);
 		}
 	}
 	CRM_CHECK(found, crm_err("No option named: %s", name));
 	CRM_ASSERT(value != NULL);
 	return value;
 }
 
 void
 config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long,
 		pe_cluster_option *option_list, int len)
 {
 	int lpc = 0;
 
 	fprintf(stdout, "<?xml version=\"1.0\"?>"
 		"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
 		"<resource-agent name=\"%s\">\n"
 		"  <version>%s</version>\n"
 		"  <longdesc lang=\"en\">%s</longdesc>\n"
 		"  <shortdesc lang=\"en\">%s</shortdesc>\n"
 		"  <parameters>\n", name, version, desc_long, desc_short);
 	
 	for(lpc = 0; lpc < len; lpc++) {
 		if(option_list[lpc].description_long == NULL
 			&& option_list[lpc].description_short == NULL) {
 			continue;
 		}
 		fprintf(stdout, "    <parameter name=\"%s\" unique=\"0\">\n"
 			"      <shortdesc lang=\"en\">%s</shortdesc>\n"
 			"      <content type=\"%s\" default=\"%s\"/>\n"
 			"      <longdesc lang=\"en\">%s%s%s</longdesc>\n"
 			"    </parameter>\n",
 			option_list[lpc].name,
 			option_list[lpc].description_short,
 			option_list[lpc].type,
 			option_list[lpc].default_value,
 			option_list[lpc].description_long?option_list[lpc].description_long:option_list[lpc].description_short,
 			option_list[lpc].values?"  Allowed values: ":"",
 			option_list[lpc].values?option_list[lpc].values:"");
 	}
 	fprintf(stdout, "  </parameters>\n</resource-agent>\n");
 }
 
 void
 verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len)
 {
 	int lpc = 0;
 	for(lpc = 0; lpc < len; lpc++) {
 		cluster_option(options, 
 			       option_list[lpc].is_valid,
 			       option_list[lpc].name,
 			       option_list[lpc].alt_name,
 			       option_list[lpc].default_value);
 	}
 }
 
 char *
 generateReference(const char *custom1, const char *custom2)
 {
 
 	const char *local_cust1 = custom1;
 	const char *local_cust2 = custom2;
 	int reference_len = 4;
 	char *since_epoch = NULL;
 
 	reference_len += 20; /* too big */
 	reference_len += 40; /* too big */
 	
 	if(local_cust1 == NULL) { local_cust1 = "_empty_"; }
 	reference_len += strlen(local_cust1);
 	
 	if(local_cust2 == NULL) { local_cust2 = "_empty_"; }
 	reference_len += strlen(local_cust2);
 	
 	crm_malloc0(since_epoch, reference_len);
 
 	if(since_epoch != NULL) {
 		sprintf(since_epoch, "%s-%s-%ld-%u",
 			local_cust1, local_cust2,
 			(unsigned long)time(NULL), ref_counter++);
 	}
 
 	return since_epoch;
 }
 
 gboolean
 decodeNVpair(const char *srcstring, char separator, char **name, char **value)
 {
 	int lpc = 0;
 	int len = 0;
 	const char *temp = NULL;
 
 	CRM_ASSERT(name != NULL && value != NULL);
 	*name = NULL;
 	*value = NULL;
 
 	crm_debug_4("Attempting to decode: [%s]", srcstring);
 	if (srcstring != NULL) {
 		len = strlen(srcstring);
 		while(lpc <= len) {
 			if (srcstring[lpc] == separator) {
 				crm_malloc0(*name, lpc+1);
 				if(*name == NULL) {
 					break; /* and return FALSE */
 				}
 				strncpy(*name, srcstring, lpc);
 				(*name)[lpc] = '\0';
 
 /* this sucks but as the strtok manpage says..
  * it *is* a bug
  */
 				len = len-lpc; len--;
 				if(len <= 0) {
 					*value = NULL;
 				} else {
 
 					crm_malloc0(*value, len+1);
 					if(*value == NULL) {
 						crm_free(*name);
 						break; /* and return FALSE */
 					}
 					temp = srcstring+lpc+1;
 					strncpy(*value, temp, len);
 					(*value)[len] = '\0';
 				}
 				return TRUE;
 			}
 			lpc++;
 		}
 	}
 
 	if(*name != NULL) {
 		crm_free(*name);
 	}
 	*name = NULL;
 	*value = NULL;
     
 	return FALSE;
 }
 
 char *
 crm_concat(const char *prefix, const char *suffix, char join) 
 {
 	int len = 0;
 	char *new_str = NULL;
 	CRM_ASSERT(prefix != NULL);
 	CRM_ASSERT(suffix != NULL);
 	len = strlen(prefix) + strlen(suffix) + 2;
 
 	crm_malloc0(new_str, (len));
 	sprintf(new_str, "%s%c%s", prefix, join, suffix);
 	new_str[len-1] = 0;
 	return new_str;
 }
 
 
 char *
 generate_hash_key(const char *crm_msg_reference, const char *sys)
 {
 	char *hash_key = crm_concat(sys?sys:"none", crm_msg_reference, '_');
 	crm_debug_3("created hash key: (%s)", hash_key);
 	return hash_key;
 }
 
 char *
 generate_hash_value(const char *src_node, const char *src_subsys)
 {
 	char *hash_value = NULL;
 	
 	if (src_node == NULL || src_subsys == NULL) {
 		return NULL;
 	}
     
 	if (strcasecmp(CRM_SYSTEM_DC, src_subsys) == 0) {
 		hash_value = crm_strdup(src_subsys);
 		if (!hash_value) {
 			crm_err("memory allocation failed in "
 			       "generate_hash_value()");
 		}
 		return hash_value;
 	}
 
 	hash_value = crm_concat(src_node, src_subsys, '_');
 	crm_info("created hash value: (%s)", hash_value);
 	return hash_value;
 }
 
 char *
 crm_itoa(int an_int)
 {
 	int len = 32;
 	char *buffer = NULL;
 	
 	crm_malloc0(buffer, (len+1));
 	if(buffer != NULL) {
 		snprintf(buffer, len, "%d", an_int);
 	}
 	
 	return buffer;
 }
 
 extern int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
 
 gboolean
 crm_log_init(
     const char *entity, int level, gboolean coredir, gboolean to_stderr,
     int argc, char **argv)
 {
 /* 	const char *test = "Testing log daemon connection"; */
 	/* Redirect messages from glib functions to our handler */
 /*  	cl_malloc_forced_for_glib(); */
 	g_log_set_handler(NULL,
 			  G_LOG_LEVEL_ERROR      | G_LOG_LEVEL_CRITICAL
 			  | G_LOG_LEVEL_WARNING  | G_LOG_LEVEL_MESSAGE
 			  | G_LOG_LEVEL_INFO     | G_LOG_LEVEL_DEBUG
 			  | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL,
 			  cl_glib_msg_handler, NULL);
 
 	/* and for good measure... - this enum is a bit field (!) */
 	g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/
 	
 	crm_system_name = entity;
 	cl_log_set_entity(entity);
 	cl_log_set_facility(HA_LOG_FACILITY);
 
 	if(coredir) {
 		cl_set_corerootdir(HA_COREDIR);
 		cl_cdtocoredir();
 	}
 	
 	set_crm_log_level(level);
 	crm_set_env_options();
 
 	cl_log_args(argc, argv);
 	cl_log_enable_stderr(to_stderr);
 
 	CL_SIGNAL(DEBUG_INC, alter_debug);
 	CL_SIGNAL(DEBUG_DEC, alter_debug);
 
 	return TRUE;
 }
 
 /* returns the old value */
 unsigned int
 set_crm_log_level(unsigned int level)
 {
 	unsigned int old = crm_log_level;
 
 	while(crm_log_level < 100 && crm_log_level < level) {
 		alter_debug(DEBUG_INC);
 	}
 	while(crm_log_level > 0 && crm_log_level > level) {
 		alter_debug(DEBUG_DEC);
 	}
 	
 	return old;
 }
 
 unsigned int
 get_crm_log_level(void)
 {
 	return crm_log_level;
 }
 
 void
 crm_log_message_adv(int level, const char *prefix, const HA_Message *msg) {
 	if((int)crm_log_level >= level) {
 		do_crm_log(level, "#========= %s message start ==========#", prefix?prefix:"");
 		if(level > LOG_DEBUG) {
 			cl_log_message(LOG_DEBUG, msg);
 		} else {
 			cl_log_message(level, msg);
 		}
 	}
 }
 
 static int
 crm_version_helper(const char *text, char **end_text)
 {
 	int atoi_result = -1;
 	CRM_ASSERT(end_text != NULL);
 
 	errno = 0;
 	
 	if(text != NULL && text[0] != 0) {
 	    atoi_result = (int)strtol(text, end_text, 10);
 		
 	    if(errno == EINVAL) {
 		crm_err("Conversion of '%s' %c failed", text, text[0]);
 		atoi_result = -1;
 	    }
 	}
 	return atoi_result;
 }
 
 
 /*
  * version1 < version2 : -1
  * version1 = version2 :  0
  * version1 > version2 :  1
  */
 int
 compare_version(const char *version1, const char *version2)
 {
 	int rc = 0;
 	int lpc = 0;
 	char *ver1_copy = NULL, *ver2_copy = NULL;
 	char *rest1 = NULL, *rest2 = NULL;
 
 	if(version1 == NULL && version2 == NULL) {
 		return 0;
 	} else if(version1 == NULL) {
 		return -1;
 	} else if(version2 == NULL) {
 		return 1;
 	}
 	
 	ver1_copy = crm_strdup(version1);
 	ver2_copy = crm_strdup(version2);
 	rest1 = ver1_copy;
 	rest2 = ver2_copy;
 
 	while(1) {
 		int digit1 = 0;
 		int digit2 = 0;
 
 		lpc++;
 
 		if(rest1 == rest2) {
 		    break;
 		}
 		
 		if(rest1 != NULL) {
 		    digit1 = crm_version_helper(rest1, &rest1);
 		}
 
 		if(rest2 != NULL) {
 		    digit2 = crm_version_helper(rest2, &rest2);
 		}
 
 		if(digit1 < digit2){
 			rc = -1;
 			crm_debug_5("%d < %d", digit1, digit2);
 			break;
 			
 		} else if (digit1 > digit2){
 			rc = 1;
 			crm_debug_5("%d > %d", digit1, digit2);
 			break;
 		}
 
 		if(rest1 != NULL && rest1[0] == '.') {
 		    rest1++;
 		}
 		if(rest1 != NULL && rest1[0] == 0) {
 		    rest1 = NULL;
 		}
 
 		if(rest2 != NULL && rest2[0] == '.') {
 		    rest2++;
 		}
 		if(rest2 != NULL && rest2[0] == 0) {
 		    rest2 = NULL;
 		}
 	}
 	
 	crm_free(ver1_copy);
 	crm_free(ver2_copy);
 
 	if(rc == 0) {
 	    crm_debug_3("%s == %s (%d)", version1, version2, lpc);
 	} else if(rc < 0) {
 	    crm_debug_3("%s < %s (%d)", version1, version2, lpc);
 	} else if(rc > 0) {
 	    crm_debug_3("%s > %s (%d)", version1, version2, lpc);
 	}
 
 	return rc;
 }
 
 gboolean do_stderr = FALSE;
 
 void
 alter_debug(int nsig) 
 {
 	CL_SIGNAL(DEBUG_INC, alter_debug);
 	CL_SIGNAL(DEBUG_DEC, alter_debug);
 	
 	switch(nsig) {
 		case DEBUG_INC:
 			if (crm_log_level < 100) {
 				crm_log_level++;
 			}
 			break;
 
 		case DEBUG_DEC:
 			if (crm_log_level > 0) {
 				crm_log_level--;
 			}
 			break;	
 
 		default:
 			fprintf(stderr, "Unknown signal %d\n", nsig);
 			cl_log(LOG_ERR, "Unknown signal %d", nsig);
 			break;	
 	}
 }
 
 
 void g_hash_destroy_str(gpointer data)
 {
 	crm_free(data);
 }
 
 #include <sys/types.h>
 #include <stdlib.h>
 #include <limits.h>
 
 long
 crm_int_helper(const char *text, char **end_text)
 {
 	long atoi_result = -1;
 	char *local_end_text = NULL;
 
 	errno = 0;
 	
 	if(text != NULL) {
 		if(end_text != NULL) {
 			atoi_result = strtoul(text, end_text, 10);
 		} else {
 			atoi_result = strtoul(text, &local_end_text, 10);
 		}
 		
 /* 		CRM_CHECK(errno != EINVAL); */
 		if(errno == EINVAL) {
 			crm_err("Conversion of %s failed", text);
 			atoi_result = -1;
 			
 		} else {
 			if(errno == ERANGE) {
 				crm_err("Conversion of %s was clipped: %ld",
 					text, atoi_result);
 			}
 			if(end_text == NULL && local_end_text[0] != '\0') {
 				crm_err("Characters left over after parsing "
 					"\"%s\": \"%s\"", text, local_end_text);
 			}
 				
 		}
 	}
 	return atoi_result;
 }
 
 int
 crm_parse_int(const char *text, const char *default_text)
 {
 	int atoi_result = -1;
 	if(text != NULL) {
 		atoi_result = crm_int_helper(text, NULL);
 		if(errno == 0) {
 			return atoi_result;
 		}
 	}
 	
 	if(default_text != NULL) {
 		atoi_result = crm_int_helper(default_text, NULL);
 		if(errno == 0) {
 			return atoi_result;
 		}
 
 	} else {
 		crm_err("No default conversion value supplied");
 	}
 
 	return -1;
 }
 
 gboolean
 crm_str_eq(const char *a, const char *b, gboolean use_case) 
 {
 	if(a == NULL || b == NULL) {
 		/* shouldn't be comparing NULLs */
 		CRM_CHECK(a != b, return TRUE);
 		return FALSE;
 
 	} else if(use_case && a[0] != b[0]) {
 		return FALSE;		
 
 	} else if(a == b) {
 		return TRUE;
 
 	} else if(strcasecmp(a, b) == 0) {
 		return TRUE;
 	}
 	return FALSE;
 }
 
 gboolean
 safe_str_neq(const char *a, const char *b)
 {
 	if(a == b) {
 		return FALSE;
 
 	} else if(a==NULL || b==NULL) {
 		return TRUE;
 
 	} else if(strcasecmp(a, b) == 0) {
 		return FALSE;
 	}
 	return TRUE;
 }
 
 char *
 crm_strdup_fn(const char *src, const char *file, const char *fn, int line)
 {
 	char *dup = NULL;
 	CRM_CHECK(src != NULL, return NULL);
 	crm_malloc0(dup, strlen(src) + 1);
 	return strcpy(dup, src);
 }
 
 
 
 #define ENV_PREFIX "HA_"
 void
 crm_set_env_options(void) 
 {
 	cl_inherit_logging_environment(500);
 	cl_log_set_logd_channel_source(NULL, NULL);
 
 	if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) {
 	    set_crm_log_level(LOG_INFO + debug_level);
 	}
 }
 
 gboolean
 crm_is_true(const char * s)
 {
 	gboolean ret = FALSE;
 	if(s != NULL) {
 		cl_str_to_boolean(s, &ret);
 	}
 	return ret;
 }
 
 int
 crm_str_to_boolean(const char * s, int * ret)
 {
 	if(s == NULL) {
 		return -1;
 
 	} else if (strcasecmp(s, "true") == 0
 		   ||	strcasecmp(s, "on") == 0
 		   ||	strcasecmp(s, "yes") == 0
 		   ||	strcasecmp(s, "y") == 0
 		   ||	strcasecmp(s, "1") == 0){
 		*ret = TRUE;
 		return 1;
 
 	} else if (strcasecmp(s, "false") == 0
 		   ||	strcasecmp(s, "off") == 0
 		   ||	strcasecmp(s, "no") == 0
 		   ||	strcasecmp(s, "n") == 0
 		   ||	strcasecmp(s, "0") == 0){
 		*ret = FALSE;
 		return 1;
 	}
 	return -1;
 }
 
 #ifndef NUMCHARS
 #    define	NUMCHARS	"0123456789."
 #endif
 
 #ifndef WHITESPACE
 #    define	WHITESPACE	" \t\n\r\f"
 #endif
 
-long
+unsigned long long
+crm_get_interval(const char * input)
+{
+    ha_time_t *interval = NULL;
+    char *input_copy = crm_strdup(input);
+    char *input_copy_mutable = input_copy;
+    unsigned long long msec = 0;
+    
+    if(input == NULL) {
+	return 0;
+
+    } else if(input[0] != 'P') {
+	return crm_get_msec(input);
+    }
+    
+    interval = parse_time_duration(&input_copy_mutable);
+    msec = date_in_seconds(interval);
+    free_ha_date(interval);
+    crm_free(input_copy);
+    return msec * 1000;
+}
+
+unsigned long long
 crm_get_msec(const char * input)
 {
 	const char *	cp = input;
 	const char *	units;
 	long		multiplier = 1000;
 	long		divisor = 1;
 	long		ret = -1;
 	double		dret;
 
 	if(input == NULL) {
 		return 0;
 	}
 	
 	cp += strspn(cp, WHITESPACE);
 	units = cp + strspn(cp, NUMCHARS);
 	units += strspn(units, WHITESPACE);
 
 	if (strchr(NUMCHARS, *cp) == NULL) {
 		return ret;
 	}
 
 	if (strncasecmp(units, "ms", 2) == 0
 	||	strncasecmp(units, "msec", 4) == 0) {
 		multiplier = 1;
 		divisor = 1;
 	}else if (strncasecmp(units, "us", 2) == 0
 	||	strncasecmp(units, "usec", 4) == 0) {
 		multiplier = 1;
 		divisor = 1000;
 	}else if (strncasecmp(units, "s", 1) == 0
 	||	strncasecmp(units, "sec", 3) == 0) {
 		multiplier = 1000;
 		divisor = 1;	
 	}else if (strncasecmp(units, "m", 1) == 0
 	||	strncasecmp(units, "min", 3) == 0) {
 		multiplier = 60*1000;
 		divisor = 1;	
 	}else if (strncasecmp(units, "h", 1) == 0
 	||	strncasecmp(units, "hr", 2) == 0) {
 		multiplier = 60*60*1000;
 		divisor = 1;	
 	}else if (*units != EOS && *units != '\n'
 	&&	*units != '\r') {
 		return ret;
 	}
 	dret = atof(cp);
 	dret *= (double)multiplier;
 	dret /= (double)divisor;
 	dret += 0.5;
 	ret = (long)dret;
 	return(ret);
 }
 
 const char *
 op_status2text(op_status_t status)
 {
 	switch(status) {
 		case LRM_OP_PENDING:
 			return "pending";
 			break;
 		case LRM_OP_DONE:
 			return "complete";
 			break;
 		case LRM_OP_ERROR:
 			return "Error";
 			break;
 		case LRM_OP_TIMEOUT:
 			return "Timed Out";
 			break;
 		case LRM_OP_NOTSUPPORTED:
 			return "NOT SUPPORTED";
 			break;
 		case LRM_OP_CANCELLED:
 			return "Cancelled";
 			break;
 	}
 	CRM_CHECK(status >= LRM_OP_PENDING && status <= LRM_OP_CANCELLED,
 		  crm_err("Unknown status: %d", status));
 	return "UNKNOWN!";
 }
 
 char *
 generate_op_key(const char *rsc_id, const char *op_type, int interval)
 {
 	int len = 35;
 	char *op_id = NULL;
 
 	CRM_CHECK(rsc_id  != NULL, return NULL);
 	CRM_CHECK(op_type != NULL, return NULL);
 	
 	len += strlen(op_type);
 	len += strlen(rsc_id);
 	crm_malloc0(op_id, len);
 	CRM_CHECK(op_id != NULL, return NULL);
 	sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
 	return op_id;
 }
 
 gboolean
 parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
 {
 	char *mutable_key = NULL;
 	char *mutable_key_ptr = NULL;
 	int len = 0, offset = 0, ch = 0;
 
 	CRM_CHECK(key != NULL, return FALSE);
 	
 	*interval = 0;
 	len = strlen(key);
 	offset = len-1;
 
 	crm_debug_3("Source: %s", key);
 	
 	while(offset > 0 && isdigit(key[offset])) {
 		int digits = len-offset;
 		ch = key[offset] - '0';
 		CRM_CHECK(ch < 10, return FALSE);
 		CRM_CHECK(ch >= 0, return FALSE);
 		while(digits > 1) {
 			digits--;
 			ch = ch * 10;
 		}
 		*interval +=  ch;
 		offset--;
 	}
 
 	crm_debug_3("  Interval: %d", *interval);
 	CRM_CHECK(key[offset] == '_', return FALSE);
 
 	mutable_key = crm_strdup(key);
 	mutable_key_ptr = mutable_key_ptr;
 	mutable_key[offset] = 0;
 	offset--;
 
 	while(offset > 0 && key[offset] != '_') {
 		offset--;
 	}
 
 	CRM_CHECK(key[offset] == '_',
 		  crm_free(mutable_key); return FALSE);
 
 	mutable_key_ptr = mutable_key+offset+1;
 
 	crm_debug_3("  Action: %s", mutable_key_ptr);
 	
 	*op_type = crm_strdup(mutable_key_ptr);
 
 	mutable_key[offset] = 0;
 	offset--;
 
 	CRM_CHECK(mutable_key != mutable_key_ptr,
 		  crm_free(mutable_key); return FALSE);
 	
 	crm_debug_3("  Resource: %s", mutable_key);
 	*rsc_id = crm_strdup(mutable_key);
 
 	crm_free(mutable_key);
 	return TRUE;
 }
 
 char *
 generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
 {
 	int len = 12;
 	char *op_id = NULL;
 
 	CRM_CHECK(rsc_id  != NULL, return NULL);
 	CRM_CHECK(op_type != NULL, return NULL);
 	CRM_CHECK(notify_type != NULL, return NULL);
 	
 	len += strlen(op_type);
 	len += strlen(rsc_id);
 	len += strlen(notify_type);
 	crm_malloc0(op_id, len);
 	if(op_id != NULL) {
 		sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
 	}
 	return op_id;
 }
 
 char *
 generate_transition_magic_v202(const char *transition_key, int op_status)
 {
 	int len = 80;
 	char *fail_state = NULL;
 
 	CRM_CHECK(transition_key != NULL, return NULL);
 	
 	len += strlen(transition_key);
 	
 	crm_malloc0(fail_state, len);
 	if(fail_state != NULL) {
 		snprintf(fail_state, len, "%d:%s", op_status,transition_key);
 	}
 	return fail_state;
 }
 
 char *
 generate_transition_magic(const char *transition_key, int op_status, int op_rc)
 {
 	int len = 80;
 	char *fail_state = NULL;
 
 	CRM_CHECK(transition_key != NULL, return NULL);
 	
 	len += strlen(transition_key);
 	
 	crm_malloc0(fail_state, len);
 	if(fail_state != NULL) {
 		snprintf(fail_state, len, "%d:%d;%s",
 			 op_status, op_rc, transition_key);
 	}
 	return fail_state;
 }
 
 gboolean
 decode_transition_magic(
 	const char *magic, char **uuid, int *transition_id, int *action_id,
 	int *op_status, int *op_rc, int *target_rc)
 {
     int res = 0;
     char *key = NULL;
     gboolean result = TRUE;
 
     CRM_CHECK(magic != NULL, return FALSE);
     CRM_CHECK(op_rc != NULL, return FALSE);
     CRM_CHECK(op_status != NULL, return FALSE);
     
     crm_malloc0(key, strlen(magic));
     res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
     if(res != 3) {
 	crm_crit("Only found %d items in: %s", res, magic);
 	return FALSE;
     }
     
     CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc),
 	      result = FALSE;
 	      goto bail;
 	);
     
   bail:
     crm_free(key);
     return result;
 }
 
 char *
 generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
 {
 	int len = 40;
 	char *fail_state = NULL;
 
 	CRM_CHECK(node != NULL, return NULL);
 	
 	len += strlen(node);
 	
 	crm_malloc0(fail_state, len);
 	if(fail_state != NULL) {
 		snprintf(fail_state, len, "%d:%d:%d:%s",
 			 action_id, transition_id, target_rc, node);
 	}
 	return fail_state;
 }
 
 
 gboolean
 decode_transition_key(
 	const char *key, char **uuid, int *transition_id, int *action_id, int *target_rc)
 {
 	int res = 0;
 	gboolean done = FALSE;
 
 	CRM_CHECK(uuid != NULL, return FALSE);
 	CRM_CHECK(target_rc != NULL, return FALSE);
 	CRM_CHECK(action_id != NULL, return FALSE);
 	CRM_CHECK(transition_id != NULL, return FALSE);
 	
 	crm_malloc0(*uuid, strlen(key));
 	res = sscanf(key, "%d:%d:%d:%s", action_id, transition_id, target_rc, *uuid);
 	switch(res) {
 	    case 4:
 		/* Post Pacemaker 0.6 */
 		done = TRUE;
 		break;
 	    case 3:
 	    case 2:
 		/* this can be tricky - the UUID might start with an integer */
 
 		/* Until Pacemaker 0.6 */
 		done = TRUE;
 		*target_rc = -1;
 		res = sscanf(key, "%d:%d:%s", action_id, transition_id, *uuid);
 		if(res == 2) {
 		    *action_id = -1;
 		    res = sscanf(key, "%d:%s", transition_id, *uuid);
 		    CRM_CHECK(res == 2, done = FALSE);
 
 		} else if(res != 3) {
 		    CRM_CHECK(res == 3, done = FALSE);
 		}
 		break;
 		
 	    case 1:
 		/* Prior to Heartbeat 2.0.8 */
 		done = TRUE;
 		*action_id = -1;
 		*target_rc = -1;
 		res = sscanf(key, "%d:%s", transition_id, *uuid);
 		CRM_CHECK(res == 2, done = FALSE);
 		break;
 	    default:
 		crm_crit("Unhandled sscanf result (%d) for %s", res, key);
 		
 	}
 
 	if(strlen(*uuid) != 36) {
 	    crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);		    
 	}
 	
 	if(done == FALSE) {
 	    crm_err("Cannot decode '%s' rc=%d", key, res);
 	    
 	    crm_free(*uuid);
 	    *uuid = NULL;
 	    *target_rc = -1;
 	    *action_id = -1;
 	    *transition_id = -1;
 	}
 	
 	return done;
 }
 
 void
 filter_action_parameters(xmlNode *param_set, const char *version) 
 {
 	char *timeout = NULL;
 	char *interval = NULL;
 #if CRM_DEPRECATED_SINCE_2_0_5
 	const char *filter_205[] = {
 		XML_ATTR_TE_TARGET_RC,
 		XML_ATTR_LRM_PROBE,
 		XML_RSC_ATTR_START,
 		XML_RSC_ATTR_NOTIFY,
 		XML_RSC_ATTR_UNIQUE,
 		XML_RSC_ATTR_MANAGED,
 		XML_RSC_ATTR_PRIORITY,
 		XML_RSC_ATTR_MULTIPLE,
 		XML_RSC_ATTR_STICKINESS,
 		XML_RSC_ATTR_FAIL_STICKINESS,
 		XML_RSC_ATTR_TARGET_ROLE,
 
 /* ignore clone fields */
 		XML_RSC_ATTR_INCARNATION, 
 		XML_RSC_ATTR_INCARNATION_MAX, 
 		XML_RSC_ATTR_INCARNATION_NODEMAX,
 		XML_RSC_ATTR_MASTER_MAX,
 		XML_RSC_ATTR_MASTER_NODEMAX,
 		
 /* old field names */
 		"role",
 		"crm_role",
 		"te-target-rc",
 		
 /* ignore notify fields */
  		"notify_stop_resource",
  		"notify_stop_uname",
  		"notify_start_resource",
  		"notify_start_uname",
  		"notify_active_resource",
  		"notify_active_uname",
  		"notify_inactive_resource",
  		"notify_inactive_uname",
  		"notify_promote_resource",
  		"notify_promote_uname",
  		"notify_demote_resource",
  		"notify_demote_uname",
  		"notify_master_resource",
  		"notify_master_uname",
  		"notify_slave_resource",
  		"notify_slave_uname"		
 	};
 #endif
 	
 	const char *attr_filter[] = {
 		XML_ATTR_ID,
 		XML_ATTR_CRM_VERSION,
 		XML_LRM_ATTR_OP_DIGEST,
 	};
 
 	gboolean do_delete = FALSE;
 	int lpc = 0;
 	static int meta_len = 0;
 	if(meta_len == 0) {
 		meta_len  = strlen(CRM_META);
 	}	
 	
 	if(param_set == NULL) {
 		return;
 	}
 
 #if CRM_DEPRECATED_SINCE_2_0_5
  	if(version == NULL || compare_version("1.0.5", version) > 0) {
 		for(lpc = 0; lpc < DIMOF(filter_205); lpc++) {
 			xml_remove_prop(param_set, filter_205[lpc]); 
 		}
 	}
 #endif
 
 	for(lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
 		xml_remove_prop(param_set, attr_filter[lpc]); 
 	}
 	
 	timeout = crm_element_value_copy(param_set, CRM_META"_timeout");
 	interval = crm_element_value_copy(param_set, CRM_META"_interval");
 
 	xml_prop_iter(param_set, prop_name, prop_value,      
 		      do_delete = FALSE;
 		      if(strncasecmp(prop_name, CRM_META, meta_len) == 0) {
 			      do_delete = TRUE;
 		      }
 
 		      if(do_delete) {
 			      xml_remove_prop(param_set, prop_name);
 		      }
 		);
 
 	if(crm_get_msec(interval) && compare_version(version, "1.0.8") > 0) {
 		/* Re-instate the operation's timeout value */
 		if(timeout != NULL) {
 			crm_xml_add(param_set, CRM_META"_timeout", timeout);
 		}
 	}
 
 	crm_free(interval);
 	crm_free(timeout);
 }
 
 void
 filter_reload_parameters(xmlNode *param_set, const char *restart_string) 
 {
 	int len = 0;
 	char *name = NULL;
 	char *match = NULL;
 	
 	if(param_set == NULL) {
 		return;
 	}
 
 	xml_prop_iter(param_set, prop_name, prop_value,      
 		      name = NULL;
 		      len = strlen(prop_name) + 3;
 
 		      crm_malloc0(name, len);
 		      sprintf(name, " %s ", prop_name);
 		      name[len-1] = 0;
 		      
 		      match = strstr(restart_string, name);
 		      if(match == NULL) {
 			      crm_debug_3("%s not found in %s",
 					  prop_name, restart_string);
 			      xml_remove_prop(param_set, prop_name);
 		      }
 		      crm_free(name);
 		);
 }
 
 void
 crm_abort(const char *file, const char *function, int line,
 	  const char *assert_condition, gboolean do_core, gboolean do_fork)
 {
 	int rc = 0;
 	int pid = 0;
 	int status = 0;
 
 	if(do_core == FALSE) {
 	    do_crm_log(LOG_ERR, "%s: Triggered assert at %s:%d : %s",
 		       function, file, line, assert_condition);
 	    return;
 
 	} else if(do_fork) {
 	    do_crm_log(LOG_ERR, "%s: Triggered non-fatal assert at %s:%d : %s",
 		       function, file, line, assert_condition);
 	    pid=fork();
 
 	} else {
 	    do_crm_log(LOG_ERR, "%s: Triggered fatal assert at %s:%d : %s",
 		       function, file, line, assert_condition);
 	}
 	
 	switch(pid) {
 		case -1:
 			crm_err("Cannot fork!");
 			return;
 
 		default:	/* Parent */
 			do_crm_log(LOG_ERR, 
 				   "%s: Forked child %d to record non-fatal assert at %s:%d : %s",
 				   function, pid, file, line, assert_condition);
 			do {
 			    rc = waitpid(pid, &status, 0);
 			    if(rc < 0 && errno != EINTR) {
 				cl_perror("%s: Cannot wait on forked child %d", function, pid);
 			    }
 			    
 			} while(rc < 0 && errno == EINTR);
 			    
 			return;
 
 		case 0:	/* Child */
 			abort();
 			break;
 	}
 }
 
 char *
 generate_series_filename(
 	const char *directory, const char *series, int sequence, gboolean bzip)
 {
 	int len = 40;
 	char *filename = NULL;
 	const char *ext = "raw";
 
 	CRM_CHECK(directory  != NULL, return NULL);
 	CRM_CHECK(series != NULL, return NULL);
 	
 	len += strlen(directory);
 	len += strlen(series);
 	crm_malloc0(filename, len);
 	CRM_CHECK(filename != NULL, return NULL);
 
 	if(bzip) {
 		ext = "bz2";
 	}
 	sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
 	
 	return filename;
 }
 
 int
 get_last_sequence(const char *directory, const char *series)
 {
 	FILE *file_strm = NULL;
 	int start = 0, length = 0, read_len = 0;
 	char *series_file = NULL;
 	char *buffer = NULL;
 	int seq = 0;
 	int len = 36;
 
 	CRM_CHECK(directory  != NULL, return 0);
 	CRM_CHECK(series != NULL, return 0);
 	
 	len += strlen(directory);
 	len += strlen(series);
 	crm_malloc0(series_file, len);
 	CRM_CHECK(series_file != NULL, return 0);
 	sprintf(series_file, "%s/%s.last", directory, series);
 	
 	file_strm = fopen(series_file, "r");
 	if(file_strm == NULL) {
 		crm_debug("Series file %s does not exist", series_file);
 		crm_free(series_file);
 		return 0;
 	}
 	
 	/* see how big the file is */
 	start  = ftell(file_strm);
 	fseek(file_strm, 0L, SEEK_END);
 	length = ftell(file_strm);
 	fseek(file_strm, 0L, start);
 	
 	CRM_ASSERT(start == ftell(file_strm));
 
 	crm_debug_3("Reading %d bytes from file", length);
 	crm_malloc0(buffer, (length+1));
 	read_len = fread(buffer, 1, length, file_strm);
 
 	if(read_len != length) {
 		crm_err("Calculated and read bytes differ: %d vs. %d",
 			length, read_len);
 		crm_free(buffer);
 		buffer = NULL;
 		
 	} else  if(length <= 0) {
 		crm_info("%s was not valid", series_file);
 		crm_free(buffer);
 		buffer = NULL;
 	}
 	
 	crm_free(series_file);
 	seq = crm_parse_int(buffer, "0");
 	crm_free(buffer);
 	fclose(file_strm);
 	return seq;
 }
 
 void
 write_last_sequence(
 	const char *directory, const char *series, int sequence, int max)
 {
 	int rc = 0;
 	int len = 36;
 	char *buffer = NULL;
 	FILE *file_strm = NULL;
 	char *series_file = NULL;
 
 	CRM_CHECK(directory  != NULL, return);
 	CRM_CHECK(series != NULL, return);
 
 	if(max == 0) {
 		return;
 	}
 	while(max > 0 && sequence > max) {
 		sequence -= max;
 	}
 	buffer = crm_itoa(sequence);
 	
 	len += strlen(directory);
 	len += strlen(series);
 	crm_malloc0(series_file, len);
 	sprintf(series_file, "%s/%s.last", directory, series);
 	
 	file_strm = fopen(series_file, "w");
 	if(file_strm == NULL) {
 		crm_err("Cannout open series file %s for writing", series_file);
 		goto bail;
 	}
 
 	rc = fprintf(file_strm, "%s", buffer);
 	if(rc < 0) {
 		cl_perror("Cannot write to series file %s", series_file);
 	}
 
   bail:
 	if(file_strm != NULL) {
 		fflush(file_strm);
 		fclose(file_strm);
 	}
 	
 	crm_free(series_file);
 	crm_free(buffer);
 }
 
 void
 crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
 {
 	long pid;
 	const char *devnull = "/dev/null";
 
 	if(daemonize == FALSE) {
 		return;
 	}
 	
 	pid = fork();
 	if (pid < 0) {
 		fprintf(stderr, "%s: could not start daemon\n", name);
 		cl_perror("fork");
 		exit(LSB_EXIT_GENERIC);
 
 	} else if (pid > 0) {
 		exit(LSB_EXIT_OK);
 	}
 	
 	if (cl_lock_pidfile(pidfile) < 0 ) {
 		pid = cl_read_pidfile_no_checking(pidfile);
 		crm_warn("%s: already running [pid %ld] (%s).\n",
 			 name, pid, pidfile);
 		exit(LSB_EXIT_OK);
 	}
 	
 	umask(022);
 	close(STDIN_FILENO);
 	(void)open(devnull, O_RDONLY);		/* Stdin:  fd 0 */
 	close(STDOUT_FILENO);
 	(void)open(devnull, O_WRONLY);		/* Stdout: fd 1 */
 	close(STDERR_FILENO);
 	(void)open(devnull, O_WRONLY);		/* Stderr: fd 2 */
 }
 
 gboolean
 crm_is_writable(const char *dir, const char *file,
 		const char *user, const char *group, gboolean need_both)
 {
 	int s_res = -1;
 	struct stat buf;
 	char *full_file = NULL;
 	const char *target = NULL;
 	
 	gboolean pass = TRUE;
 	gboolean readwritable = FALSE;
 
 	CRM_ASSERT(dir != NULL);
 	if(file != NULL) {
 		full_file = crm_concat(dir, file, '/');
 		target = full_file;
 		s_res = stat(full_file, &buf);
 		if( s_res == 0 && S_ISREG(buf.st_mode) == FALSE ) {
 			crm_err("%s must be a regular file", target);
 			pass = FALSE;
 			goto out;
 		}
 	}
 	
 	if (s_res != 0) {
 		target = dir;
 		s_res = stat(dir, &buf);
 		if(s_res != 0) {
 			crm_err("%s must exist and be a directory", dir);
 			pass = FALSE;
 			goto out;
 
 		} else if( S_ISDIR(buf.st_mode) == FALSE ) {
 			crm_err("%s must be a directory", dir);
 			pass = FALSE;
 		}
 	}
 
 	if(user) {
 		struct passwd *sys_user = NULL;
 		sys_user = getpwnam(user);
 		readwritable = (sys_user != NULL
 				&& buf.st_uid == sys_user->pw_uid
 				&& (buf.st_mode & (S_IRUSR|S_IWUSR)));
 		if(readwritable == FALSE) {
 			crm_err("%s must be owned and r/w by user %s",
 				target, user);
 			if(need_both) {
 				pass = FALSE;
 			}
 		}
 	}	
 
 	if(group) {
 		struct group *sys_grp = getgrnam(group);
 		readwritable = (
 			sys_grp != NULL
 			&& buf.st_gid == sys_grp->gr_gid
 			&& (buf.st_mode & (S_IRGRP|S_IWGRP)));		
 		if(readwritable == FALSE) {
 			if(need_both || user == NULL) {
 				pass = FALSE;
 				crm_err("%s must be owned and r/w by group %s",
 					target, group);
 			} else {
 				crm_warn("%s should be owned and r/w by group %s",
 					 target, group);
 			}
 		}
 	}
 
   out:
 	crm_free(full_file);
 	return pass;
 }
 
 static unsigned long long crm_bit_filter = 0; /* 0x00000002ULL; */
 static unsigned int bit_log_level = LOG_DEBUG_5;
 
 long long
 crm_clear_bit(const char *function, long long word, long long bit)
 {
 	unsigned int level = bit_log_level;
 	if(bit & crm_bit_filter) {
 	    level = LOG_ERR;
 	}
 
 	do_crm_log(level, "Bit 0x%.16llx cleared by %s", bit, function);
 	word &= ~bit;
 
 	return word;
 }
 
 long long
 crm_set_bit(const char *function, long long word, long long bit)
 {
 	unsigned int level = bit_log_level;
 	if(bit & crm_bit_filter) {
 	    level = LOG_ERR;
 	}
 
 	do_crm_log(level, "Bit 0x%.16llx set by %s", bit, function);
 	word |= bit;
 	return word;
 }
 
 gboolean
 is_not_set(long long word, long long bit)
 {
 	crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
 	return ((word & bit) == 0);
 }
 
 gboolean
 is_set(long long word, long long bit)
 {
 	crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
 	return ((word & bit) == bit);
 }
 
 gboolean
 is_set_any(long long word, long long bit)
 {
 	crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
 	return ((word & bit) != 0);
 }
 
 gboolean is_openais_cluster(void)
 {
     static const char *cluster_type = NULL;
 
     if(cluster_type == NULL) {
 	cluster_type = getenv("HA_cluster_type");
     }
     
     if(safe_str_eq("openais", cluster_type)) {
 #if SUPPORT_AIS
 	return TRUE;
 #else
 	CRM_ASSERT(safe_str_eq("openais", cluster_type) == FALSE);
 #endif
     }
     return FALSE;
 }
 
 gboolean is_heartbeat_cluster(void)
 {
 #if SUPPORT_HEARTBEAT
     return !is_openais_cluster();
 #else
     CRM_ASSERT(is_openais_cluster());
     return FALSE;
 #endif
 }
 
diff --git a/lib/crm/pengine/utils.c b/lib/crm/pengine/utils.c
index a2c5489880..108811f04d 100644
--- a/lib/crm/pengine/utils.c
+++ b/lib/crm/pengine/utils.c
@@ -1,1305 +1,1342 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program 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.1 of the License, or (at your option) any later version.
  * 
  * This software 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 this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 
 #include <glib.h>
 
 #include <crm/pengine/rules.h>
 #include <utils.h>
 
 extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root);
 void print_str_str(gpointer key, gpointer value, gpointer user_data);
 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
 void unpack_operation(
 	action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set);
 
 void
 pe_free_shallow(GListPtr alist)
 {
 	pe_free_shallow_adv(alist, TRUE);
 }
 
 void
 pe_free_shallow_adv(GListPtr alist, gboolean with_data)
 {
 	GListPtr item;
 	GListPtr item_next = alist;
 
 	if(with_data == FALSE && alist != NULL) {
 		g_list_free(alist);
 		return;
 	}
 	
 	while(item_next != NULL) {
 		item = item_next;
 		item_next = item_next->next;
 		
 		if(with_data) {
 /*			crm_debug_5("freeing %p", item->data); */
 			crm_free(item->data);
 		}
 		
 		item->data = NULL;
 		item->next = NULL;
 		g_list_free_1(item);
 	}
 }
 
 
 node_t *
 node_copy(node_t *this_node) 
 {
 	node_t *new_node  = NULL;
 
 	CRM_CHECK(this_node != NULL, return NULL);
 
 	crm_malloc0(new_node, sizeof(node_t));
 	CRM_ASSERT(new_node != NULL);
 	
 	crm_debug_5("Copying %p (%s) to %p",
 		  this_node, this_node->details->uname, new_node);
 
 	new_node->weight  = this_node->weight; 
 	new_node->fixed   = this_node->fixed;
 	new_node->details = this_node->details;	
 	
 	return new_node;
 }
 
 /* are the contents of list1 and list2 equal 
  * nodes with weight < 0 are ignored if filter == TRUE
  *
  * slow but linear
  *
  */
 gboolean
 node_list_eq(GListPtr list1, GListPtr list2, gboolean filter)
 {
 	node_t *other_node;
 
 	GListPtr lhs = list1;
 	GListPtr rhs = list2;
 	
 	slist_iter(
 		node, node_t, lhs, lpc,
 
 		if(node == NULL || (filter && node->weight < 0)) {
 			continue;
 		}
 
 		other_node = (node_t*)
 			pe_find_node_id(rhs, node->details->id);
 
 		if(other_node == NULL || other_node->weight < 0) {
 			return FALSE;
 		}
 		);
 	
 	lhs = list2;
 	rhs = list1;
 
 	slist_iter(
 		node, node_t, lhs, lpc,
 
 		if(node == NULL || (filter && node->weight < 0)) {
 			continue;
 		}
 
 		other_node = (node_t*)
 			pe_find_node_id(rhs, node->details->id);
 
 		if(other_node == NULL || other_node->weight < 0) {
 			return FALSE;
 		}
 		);
   
 	return TRUE;
 }
 
 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 GListPtr
 node_list_exclude(GListPtr list1, GListPtr list2)
 {
     node_t *other_node = NULL;
     GListPtr result = NULL;
     
     result = node_list_dup(list1, FALSE, FALSE);
     
     slist_iter(
 	node, node_t, result, lpc,
 	
 	other_node = pe_find_node_id(list2, node->details->id);
 	
 	if(other_node == NULL) {
 	    node->weight = -INFINITY;
 	} else {
 	    node->weight = merge_weights(node->weight, other_node->weight);
 	}
 	);
     
     slist_iter(
 	node, node_t, list2, lpc,
 	
 	other_node = pe_find_node_id(result, node->details->id);
 	
 	if(other_node == NULL) {
 	    node_t *new_node = node_copy(node);
 	    new_node->weight = -INFINITY;
 	    result = g_list_append(result, new_node);
 	}
 	);
 
     return result;
 }
 
 /* the intersection of list1 and list2 */
 GListPtr
 node_list_and(GListPtr list1, GListPtr list2, gboolean filter)
 {
 	GListPtr result = NULL;
 	unsigned lpc = 0;
 
 	for(lpc = 0; lpc < g_list_length(list1); lpc++) {
 		node_t *node = (node_t*)g_list_nth_data(list1, lpc);
 		node_t *other_node = pe_find_node_id(list2, node->details->id);
 		node_t *new_node = NULL;
 
 		if(other_node != NULL) {
 			new_node = node_copy(node);
 		}
 		
 		if(new_node != NULL) {
 			crm_debug_4("%s: %d + %d", node->details->uname, 
 				    other_node->weight, new_node->weight);
 			new_node->weight = merge_weights(
 				new_node->weight, other_node->weight);
 
 			crm_debug_3("New node weight for %s: %d",
 				 new_node->details->uname, new_node->weight);
 			
 			if(filter && new_node->weight < 0) {
 				crm_free(new_node);
 				new_node = NULL;
 			}
 		}
 		
 		if(new_node != NULL) {
 			result = g_list_append(result, new_node);
 		}
 	}
 
 	return result;
 }
 
 
 /* list1 - list2 */
 GListPtr
 node_list_minus(GListPtr list1, GListPtr list2, gboolean filter)
 {
 	GListPtr result = NULL;
 
 	slist_iter(
 		node, node_t, list1, lpc,
 		node_t *other_node = pe_find_node_id(list2, node->details->id);
 		node_t *new_node = NULL;
 		
 		if(node == NULL || other_node != NULL
 		   || (filter && node->weight < 0)) {
 			continue;
 			
 		}
 		new_node = node_copy(node);
 		result = g_list_append(result, new_node);
 		);
   
 	crm_debug_3("Minus result len: %d", g_list_length(result));
 
 	return result;
 }
 
 /* list1 + list2 - (intersection of list1 and list2) */
 GListPtr
 node_list_xor(GListPtr list1, GListPtr list2, gboolean filter)
 {
 	GListPtr result = NULL;
 	
 	slist_iter(
 		node, node_t, list1, lpc,
 		node_t *new_node = NULL;
 		node_t *other_node = (node_t*)
 			pe_find_node_id(list2, node->details->id);
 
 		if(node == NULL || other_node != NULL
 		   || (filter && node->weight < 0)) {
 			continue;
 		}
 		new_node = node_copy(node);
 		result = g_list_append(result, new_node);
 		);
 	
  
 	slist_iter(
 		node, node_t, list2, lpc,
 		node_t *new_node = NULL;
 		node_t *other_node = (node_t*)
 			pe_find_node_id(list1, node->details->id);
 
 		if(node == NULL || other_node != NULL
 		   || (filter && node->weight < 0)) {
 			continue;
 		}
 		new_node = node_copy(node);
 		result = g_list_append(result, new_node);
 		);
   
 	crm_debug_3("Xor result len: %d", g_list_length(result));
 	return result;
 }
 
 GListPtr
 node_list_or(GListPtr list1, GListPtr list2, gboolean filter)
 {
 	node_t *other_node = NULL;
 	GListPtr result = NULL;
 	gboolean needs_filter = FALSE;
 
 	result = node_list_dup(list1, FALSE, filter);
 
 	slist_iter(
 		node, node_t, list2, lpc,
 
 		if(node == NULL) {
 			continue;
 		}
 
 		other_node = (node_t*)pe_find_node_id(
 			result, node->details->id);
 
 		if(other_node != NULL) {
 			crm_debug_4("%s + %s: %d + %d",
 				    node->details->uname, 
 				    other_node->details->uname, 
 				    node->weight, other_node->weight);
 			other_node->weight = merge_weights(
 				other_node->weight, node->weight);
 			
 			if(filter && node->weight < 0) {
 				needs_filter = TRUE;
 			}
 
 		} else if(filter == FALSE || node->weight >= 0) {
 			node_t *new_node = node_copy(node);
 			result = g_list_append(result, new_node);
 		}
 		);
 
 	/* not the neatest way, but the most expedient for now */
 	if(filter && needs_filter) {
 		GListPtr old_result = result;
 		result = node_list_dup(old_result, FALSE, filter);
 		pe_free_shallow_adv(old_result, TRUE);
 	}
 	
 
 	return result;
 }
 
 GListPtr 
 node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
 {
 	GListPtr result = NULL;
 
 	slist_iter(
 		this_node, node_t, list1, lpc,
 		node_t *new_node = NULL;
 		if(filter && this_node->weight < 0) {
 			continue;
 		}
 		
 		new_node = node_copy(this_node);
 		if(reset) {
 			new_node->weight = 0;
 		}
 		if(new_node != NULL) {
 			result = g_list_append(result, new_node);
 		}
 		);
 
 	return result;
 }
 
 
 void dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes) 
 {
     GListPtr list = nodes;
     if(rsc) {
 	list = rsc->allowed_nodes;
     }
     
     slist_iter(
 	node, node_t, list, lpc,
 	if(rsc) {
 	    do_crm_log(level, "%s: %s allocation score on %s: %d",
 		       comment, rsc->id, node->details->uname, node->weight);
 	} else {
 	    do_crm_log(level, "%s: %s = %d", comment, node->details->uname, node->weight);
 	}
 	);
 
     if(rsc && rsc->children) {
 	slist_iter(
 	    child, resource_t, rsc->children, lpc,
 	    dump_node_scores(level, child, comment, nodes);
 	    );
     }
 }
 
 gint sort_rsc_index(gconstpointer a, gconstpointer b)
 {
 	const resource_t *resource1 = (const resource_t*)a;
 	const resource_t *resource2 = (const resource_t*)b;
 
 	if(a == NULL && b == NULL) { return 0; }
 	if(a == NULL) { return 1; }
 	if(b == NULL) { return -1; }
   
 	if(resource1->sort_index > resource2->sort_index) {
 		return -1;
 	}
 	
 	if(resource1->sort_index < resource2->sort_index) {
 		return 1;
 	}
 
 	return 0;
 }
 
 gint sort_rsc_priority(gconstpointer a, gconstpointer b)
 {
 	const resource_t *resource1 = (const resource_t*)a;
 	const resource_t *resource2 = (const resource_t*)b;
 
 	if(a == NULL && b == NULL) { return 0; }
 	if(a == NULL) { return 1; }
 	if(b == NULL) { return -1; }
   
 	if(resource1->priority > resource2->priority) {
 		return -1;
 	}
 	
 	if(resource1->priority < resource2->priority) {
 		return 1;
 	}
 
 	return 0;
 }
 
 action_t *
 custom_action(resource_t *rsc, char *key, const char *task,
 	      node_t *on_node, gboolean optional, gboolean save_action,
 	      pe_working_set_t *data_set)
 {
 	action_t *action = NULL;
 	GListPtr possible_matches = NULL;
 	CRM_CHECK(key != NULL, return NULL);
 	CRM_CHECK(task != NULL, return NULL);
 
 	if(save_action && rsc != NULL) {
 		possible_matches = find_actions(rsc->actions, key, on_node);
 	}
 	
 	if(possible_matches != NULL) {
 		crm_free(key);
 		
 		if(g_list_length(possible_matches) > 1) {
 			pe_warn("Action %s for %s on %s exists %d times",
 				task, rsc?rsc->id:"<NULL>",
 				on_node?on_node->details->uname:"<NULL>",
 				g_list_length(possible_matches));
 		}
 		
 		action = g_list_nth_data(possible_matches, 0);
 		crm_debug_4("Found existing action (%d) %s for %s on %s",
 			  action->id, task, rsc?rsc->id:"<NULL>",
 			  on_node?on_node->details->uname:"<NULL>");
 		g_list_free(possible_matches);
 	}
 
 	if(action == NULL) {
 		if(save_action) {
 			crm_debug_4("Creating%s action %d: %s for %s on %s",
 				    optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"<NULL>",
 				    on_node?on_node->details->uname:"<NULL>");
 		}
 		
 		crm_malloc0(action, sizeof(action_t));
 		if(save_action) {
 			action->id   = data_set->action_id++;
 		} else {
 			action->id = 0;
 		}
 		action->rsc  = rsc;
 		CRM_ASSERT(task != NULL);
 		action->task = crm_strdup(task);
 		action->node = on_node;
 		action->uuid = key;
 		
 		action->actions_before   = NULL;
 		action->actions_after    = NULL;
 		action->failure_is_fatal = TRUE;
 		
 		action->pseudo     = FALSE;
 		action->dumped     = FALSE;
 		action->runnable   = TRUE;
 		action->processed  = FALSE;
 		action->optional   = optional;
 		action->seen_count = 0;
 		
 		action->extra = g_hash_table_new_full(
 			g_str_hash, g_str_equal,
 			g_hash_destroy_str, g_hash_destroy_str);
 		
 		action->meta = g_hash_table_new_full(
 			g_str_hash, g_str_equal,
 			g_hash_destroy_str, g_hash_destroy_str);
 		
 		if(save_action) {
 			data_set->actions = g_list_append(
 				data_set->actions, action);
 		}		
 		
 		if(rsc != NULL) {
 			action->op_entry = find_rsc_op_entry(rsc, key);
 			
 			unpack_operation(
 				action, action->op_entry, data_set);
 			
 			if(save_action) {
 				rsc->actions = g_list_append(
 					rsc->actions, action);
 			}
 		}
 		
 		if(save_action) {
 			crm_debug_4("Action %d created", action->id);
 		}
 	}
 
 	if(optional == FALSE && action->optional) {
 		crm_debug_2("Action %d (%s) marked manditory",
 			    action->id, action->uuid);
 		action->optional = FALSE;
 	}
 	
 	if(rsc != NULL) {
 		enum action_tasks a_task = text2task(action->task);
 		int warn_level = LOG_DEBUG_3;
 		if(save_action) {
 			warn_level = LOG_WARNING;
 		}
 
 		if(action->node != NULL && action->op_entry != NULL) {
 			unpack_instance_attributes(
 				action->op_entry, XML_TAG_ATTR_SETS,
 				action->node->details->attrs,
 				action->extra, NULL, FALSE, data_set->now);
 		}
 
 		if(action->pseudo) {
 			/* leave untouched */
 			
 		} else if(action->node == NULL) {
 			action->runnable = FALSE;
 			
 		} else if(g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL
 			  && is_not_set(rsc->flags, pe_rsc_managed)) {
 			do_crm_log(warn_level, "Action %s (unmanaged)",
 				 action->uuid);
 			action->optional = TRUE;
 /*   			action->runnable = FALSE; */
 
 		} else if(action->node->details->online == FALSE) {
 			action->runnable = FALSE;
 			do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
 				 action->uuid, action->node->details->uname);
 			if(is_set(action->rsc->flags, pe_rsc_managed)
 			   && save_action
 			   && a_task == stop_rsc) {
 				do_crm_log(warn_level, "Marking node %s unclean",
 					 action->node->details->uname);
 				action->node->details->unclean = TRUE;
 			}
 			
 		} else if(action->node->details->pending) {
 			action->runnable = FALSE;
 			do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
 				 action->uuid, action->node->details->uname);
 
 		} else if(action->needs == rsc_req_nothing) {
 			crm_debug_3("Action %s doesnt require anything",
 				  action->uuid);
 			action->runnable = TRUE;
 #if 0
 			/*
 			 * No point checking this
 			 * - if we dont have quorum we cant stonith anyway
 			 */
 		} else if(action->needs == rsc_req_stonith) {
 			crm_debug_3("Action %s requires only stonith", action->uuid);
 			action->runnable = TRUE;
 #endif
 		} else if(data_set->have_quorum == FALSE
 			&& data_set->no_quorum_policy == no_quorum_stop) {
 			action->runnable = FALSE;
 			crm_debug("%s\t%s (cancelled : quorum)",
 				  action->node->details->uname,
 				  action->uuid);
 			
 		} else if(data_set->have_quorum == FALSE
 			&& data_set->no_quorum_policy == no_quorum_freeze) {
 			crm_debug_3("Check resource is already active");
 			if(rsc->fns->active(rsc, TRUE) == FALSE) {
 				action->runnable = FALSE;
 				crm_debug("%s\t%s (cancelled : quorum freeze)",
 					  action->node->details->uname,
 					  action->uuid);
 			}
 
 		} else {
 			crm_debug_3("Action %s is runnable", action->uuid);
 			action->runnable = TRUE;
 		}
 
 		if(save_action) {
 			switch(a_task) {
 				case stop_rsc:
 				    set_bit(rsc->flags, pe_rsc_stopping);
 				    break;
 				case start_rsc:
 				    clear_bit(rsc->flags, pe_rsc_starting);
 				    if(action->runnable) {
 					set_bit(rsc->flags, pe_rsc_starting);
 				    }
 				    break;
 				default:
 					break;
 			}
 		}
 	}
 	return action;
 }
 
 void
 unpack_operation(
 	action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set)
 {
 	int value_i = 0;
-	int start_delay = 0;
+	unsigned long long interval = 0;
+	unsigned long long start_delay = 0;
 	char *value_ms = NULL;
 	const char *class = NULL;
 	const char *value = NULL;
 	const char *field = NULL;
 	xmlNode *defaults = get_object_root(XML_CIB_TAG_OPCONFIG, data_set->input);
 
 	CRM_CHECK(action->rsc != NULL, return);
 
 	unpack_instance_attributes(defaults, XML_TAG_META_SETS, NULL,
 				   action->meta, NULL, FALSE, data_set->now);
 
 	xml_prop_iter(xml_obj, name, value,
 		      if(value != NULL && g_hash_table_lookup(action->meta, name) == NULL) {
 			  g_hash_table_insert(action->meta, crm_strdup(name), crm_strdup(value));
 		      }
 	    );
 	
 	unpack_instance_attributes(xml_obj, XML_TAG_META_SETS,
 				   NULL, action->meta, NULL, FALSE, data_set->now);
 	
 	unpack_instance_attributes(xml_obj, XML_TAG_ATTR_SETS,
 				   NULL, action->meta, NULL, FALSE, data_set->now);
 	
 	g_hash_table_remove(action->meta, "id");	
 
 	class = g_hash_table_lookup(action->rsc->meta, "class");
 	
 	value = g_hash_table_lookup(action->meta, "prereq");
 	if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) {
 		/* todo: integrate stop as an option? */
 		action->needs = rsc_req_nothing;
 		value = "nothing (default)";
 
 	} else if(safe_str_eq(value, "nothing")) {
 		action->needs = rsc_req_nothing;
 
 	} else if(safe_str_eq(value, "quorum")) {
 		action->needs = rsc_req_quorum;
 
 	} else if(safe_str_eq(value, "fencing")) {
 		action->needs = rsc_req_stonith;
 		
 	} else if(data_set->no_quorum_policy == no_quorum_ignore
 	    || safe_str_eq(class, "stonith")) {
 		action->needs = rsc_req_nothing;
 		value = "nothing (default)";
 		
 	} else if(data_set->no_quorum_policy == no_quorum_freeze
 		  && data_set->stonith_enabled) {
 		action->needs = rsc_req_stonith;
 		value = "fencing (default)";
 
 	} else {
 		action->needs = rsc_req_quorum;
 		value = "quorum (default)";
 	}
 
 	if(safe_str_eq(class, "stonith")) {
 		if(action->needs == rsc_req_stonith) {
 			crm_config_err("Stonith resources (eg. %s) cannot require"
 				      " fencing to start", action->rsc->id);
 		}
 		action->needs = rsc_req_nothing;
 		value = "nothing (fencing override)";
 	}
 	crm_debug_3("\tAction %s requires: %s", action->task, value);
 
 	value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
 	if(value == NULL) {
 
 	} else if(safe_str_eq(value, "block")) {
 		action->on_fail = action_fail_block;
 
 	} else if(safe_str_eq(value, "fence")) {
 		action->on_fail = action_fail_fence;
 		value = "node fencing";
 		
 		if(data_set->stonith_enabled == FALSE) {
 		    crm_config_err("Specifying on_fail=fence and"
 				   " stonith-enabled=false makes no sense");
 		    action->on_fail = action_fail_stop;
 		    action->fail_role = RSC_ROLE_STOPPED;
 		    value = "stop resource";
 		}
 		
 	} else if(safe_str_eq(value, "ignore")
 		|| safe_str_eq(value, "nothing")) {
 		action->on_fail = action_fail_ignore;
 		value = "ignore";
 
 	} else if(safe_str_eq(value, "migrate")) {
 		action->on_fail = action_fail_migrate;
 		value = "force migration";
 		
 	} else if(safe_str_eq(value, "stop")) {
 		action->on_fail = action_fail_stop;
 		action->fail_role = RSC_ROLE_STOPPED;
 		value = "stop resource";
 		
 	} else if(safe_str_eq(value, "restart")) {
 		action->on_fail = action_fail_recover;
 		value = "restart (and possibly migrate)";
 		
 	} else {
 		pe_err("Resource %s: Unknown failure type (%s)",
 		       action->rsc->id, value);
 		value = NULL;
 	}
 	
 	/* defaults */
 	if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
 		if(data_set->stonith_enabled) {
 			action->on_fail = action_fail_fence;		
 			value = "resource fence (default)";
 			
 		} else {
 			action->on_fail = action_fail_block;		
 			value = "resource block (default)";
 		}
 		
 	} else if(value == NULL
 		  && safe_str_eq(action->task, CRMD_ACTION_MIGRATED)) {
 		action->on_fail = action_migrate_failure;		
 		value = "atomic migration recovery (default)";
 		
 	} else if(value == NULL) {
 		action->on_fail = action_fail_recover;		
 		value = "restart (and possibly migrate) (default)";
 	}
 	
 	crm_debug_3("\t%s failure handling: %s", action->task, value);
 
 	value = NULL;
 	if(xml_obj != NULL) {
 		value = g_hash_table_lookup(action->meta, "role_after_failure");
 	}
 	if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
 		action->fail_role = text2role(value);
 	}
 	/* defaults */
 	if(action->fail_role == RSC_ROLE_UNKNOWN) {
 		if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
 			action->fail_role = RSC_ROLE_SLAVE;
 		} else {
 			action->fail_role = RSC_ROLE_STARTED;
 		}
 	}
 	crm_debug_3("\t%s failure results in: %s",
 		    action->task, role2text(action->fail_role));	
 
 	field = XML_LRM_ATTR_INTERVAL;
 	value = g_hash_table_lookup(action->meta, field);
 	if(value != NULL) {
-		value_i = crm_get_msec(value);
-		CRM_CHECK(value_i >= 0, value_i = 0);
-		value_ms = crm_itoa(value_i);
-		if(value_i > 0) {
+		interval = crm_get_interval(value);
+		if(interval > 0) {
+		    value_ms = crm_itoa(interval);
 		    g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
+
 		} else {
 		    g_hash_table_remove(action->meta, field);
 		}
 	}
 
 	field = XML_OP_ATTR_START_DELAY;
 	value = g_hash_table_lookup(action->meta, field);
 	if(value != NULL) {
 		value_i = crm_get_msec(value);
 		if(value_i < 0) {
 			value_i = 0;
 		}
 		start_delay = value_i;
 		value_ms = crm_itoa(value_i);
 		g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
+
+	} else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
+	    char *date_str = NULL;
+	    char *date_str_mutable = NULL;
+	    ha_time_t *origin = NULL;
+	    value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
+	    date_str = crm_strdup(value);
+	    date_str_mutable = date_str;
+	    origin = parse_date(&date_str_mutable);
+	    crm_free(date_str);
+
+	    if(origin == NULL) {
+		crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s",
+			       ID(xml_obj), value);
+
+	    } else {
+		ha_time_t *delay = NULL;
+		int rc = compare_date(origin, data_set->now);
+		unsigned long long delay_s = 0;
+
+		while(rc < 0) {
+		    add_seconds(origin, interval/1000);
+		    rc = compare_date(origin, data_set->now);
+		}
+
+		delay = subtract_time(origin, data_set->now);
+		delay_s = date_in_seconds(delay);
+		/* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */
+
+		crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
+		g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000));
+		start_delay = delay_s * 1000;
+		free_ha_date(origin);
+		free_ha_date(delay);
+	    }
 	}
+	
 
 	field = XML_ATTR_TIMEOUT;
 	value = g_hash_table_lookup(action->meta, field);
 	if(value == NULL) {
 		value = pe_pref(
 			data_set->config_hash, "default-action-timeout");
 	}
 	value_i = crm_get_msec(value);
 	if(value_i < 0) {
 		value_i = 0;
 	}
 	value_i += start_delay;
 	value_ms = crm_itoa(value_i);
 	g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
 }
 
 xmlNode *
 find_rsc_op_entry(resource_t *rsc, const char *key) 
 {
 	int number = 0;
 	const char *name = NULL;
 	const char *value = NULL;
 	const char *interval = NULL;
 	char *match_key = NULL;
 	xmlNode *op = NULL;
 	
 	xml_child_iter_filter(
 		rsc->ops_xml, operation, "op",
 
 		name = crm_element_value(operation, "name");
 		interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 		value = crm_element_value(operation, "disabled");
 		if(crm_is_true(value)) {
 			crm_debug_2("%s disabled", ID(operation));
 			continue;
 		}
 
-		number = crm_get_msec(interval);
+		number = crm_get_interval(interval);
 		if(number < 0) {
 		    continue;
 		}
 		
 		match_key = generate_op_key(rsc->id, name, number);
 
 		if(safe_str_eq(key, match_key)) {
 			op = operation;
 		}
 		crm_free(match_key);
 
 		if(op != NULL) {
 			return op;
 		}
 		);
 	crm_debug_3("No match for %s", key);
 	return op;
 }
 
 void
 print_node(const char *pre_text, node_t *node, gboolean details)
 { 
 	if(node == NULL) {
 		crm_debug_4("%s%s: <NULL>",
 		       pre_text==NULL?"":pre_text,
 		       pre_text==NULL?"":": ");
 		return;
 	}
 
 	crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)",
 	       pre_text==NULL?"":pre_text,
 	       pre_text==NULL?"":": ",
 	       node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ",
 	       node->details->uname, 
 	       node->weight,
 	       node->fixed?"True":"False"); 
 
 	if(details && node != NULL && node->details != NULL) {
 		char *pe_mutable = crm_strdup("\t\t");
 		crm_debug_4("\t\t===Node Attributes");
 		g_hash_table_foreach(node->details->attrs,
 				     print_str_str, pe_mutable);
 		crm_free(pe_mutable);
 
 		crm_debug_4("\t\t=== Resources");
 		slist_iter(
 			rsc, resource_t, node->details->running_rsc, lpc,
 			print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
 			);
 	}
 }
 
 /*
  * Used by the HashTable for-loop
  */
 void print_str_str(gpointer key, gpointer value, gpointer user_data)
 {
 	crm_debug_4("%s%s %s ==> %s",
 	       user_data==NULL?"":(char*)user_data,
 	       user_data==NULL?"":": ",
 	       (char*)key,
 	       (char*)value);
 }
 
 void
 print_resource(
 	int log_level, const char *pre_text, resource_t *rsc, gboolean details)
 {
 	long options = pe_print_log;
 	
 	if(rsc == NULL) {
 		do_crm_log(log_level-1, "%s%s: <NULL>",
 			      pre_text==NULL?"":pre_text,
 			      pre_text==NULL?"":": ");
 		return;
 	}
 	if(details) {
 		options |= pe_print_details;
 	}
 	rsc->fns->print(rsc, pre_text, options, &log_level);
 }
 
 void
 pe_free_action(action_t *action) 
 {
 	if(action == NULL) {
 		return;
 	}
 	pe_free_shallow(action->actions_before);/* action_warpper_t* */
 	pe_free_shallow(action->actions_after); /* action_warpper_t* */
 	g_hash_table_destroy(action->extra);
 	g_hash_table_destroy(action->meta);
 	crm_free(action->task);
 	crm_free(action->uuid);
 	crm_free(action);
 }
 
 GListPtr
 find_recurring_actions(GListPtr input, node_t *not_on_node)
 {
 	const char *value = NULL;
 	GListPtr result = NULL;
 	CRM_CHECK(input != NULL, return NULL);
 	
 	slist_iter(
 		action, action_t, input, lpc,
 		value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
 		if(value == NULL) {
 			/* skip */
 		} else if(safe_str_eq(value, "0")) {
 			/* skip */
 		} else if(safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
 			/* skip */
 		} else if(not_on_node == NULL) {
 			crm_debug_5("(null) Found: %s", action->uuid);
 			result = g_list_append(result, action);
 			
 		} else if(action->node == NULL) {
 			/* skip */
 		} else if(action->node->details != not_on_node->details) {
 			crm_debug_5("Found: %s", action->uuid);
 			result = g_list_append(result, action);
 		}
 		);
 
 	return result;
 }
 
 GListPtr
 find_actions(GListPtr input, const char *key, node_t *on_node)
 {
 	GListPtr result = NULL;
 	CRM_CHECK(key != NULL, return NULL);
 	
 	slist_iter(
 		action, action_t, input, lpc,
 		crm_debug_5("Matching %s against %s", key, action->uuid);
 		if(safe_str_neq(key, action->uuid)) {
 			continue;
 			
 		} else if(on_node == NULL) {
 			result = g_list_append(result, action);
 			
 		} else if(action->node == NULL) {
 			/* skip */
 			crm_debug_2("While looking for %s action on %s, "
 				    "found an unallocated one.  Assigning"
 				    " it to the requested node...",
 				    key, on_node->details->uname);
 
 			action->node = on_node;
 			result = g_list_append(result, action);
 			
 		} else if(safe_str_eq(on_node->details->id,
 				      action->node->details->id)) {
 			result = g_list_append(result, action);
 		}
 		);
 
 	return result;
 }
 
 
 GListPtr
 find_actions_exact(GListPtr input, const char *key, node_t *on_node)
 {
 	GListPtr result = NULL;
 	CRM_CHECK(key != NULL, return NULL);
 	
 	slist_iter(
 		action, action_t, input, lpc,
 		crm_debug_5("Matching %s against %s", key, action->uuid);
 		if(safe_str_neq(key, action->uuid)) {
 			crm_debug_3("Key mismatch: %s vs. %s",
 				    key, action->uuid);
 			continue;
 			
 		} else if(on_node == NULL  || action->node == NULL) {
 			crm_debug_3("on_node=%p, action->node=%p",
 				    on_node, action->node);
 			continue;
 
 		} else if(safe_str_eq(on_node->details->id,
 				      action->node->details->id)) {
 			result = g_list_append(result, action);
 		}
 		crm_debug_2("Node mismatch: %s vs. %s",
 			    on_node->details->id, action->node->details->id);
 		);
 
 	return result;
 }
 
 void
 set_id(xmlNode * xml_obj, const char *prefix, int child) 
 {
 	int id_len = 0;
 	gboolean use_prefix = TRUE;
 	gboolean use_child = TRUE;
 
 	char *new_id   = NULL;
 	const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 	
 	id_len = 1 + strlen(id);
 
 	if(child > 999) {
 		pe_err("Are you insane?!?"
 			" The CRM does not support > 1000 children per resource");
 		return;
 		
 	} else if(child < 0) {
 		use_child = FALSE;
 		
 	} else {
 		id_len += 4; /* child */
 	}
 	
 	if(prefix == NULL || safe_str_eq(id, prefix)) {
 		use_prefix = FALSE;
 	} else {
 		id_len += (1 + strlen(prefix));
 	}
 	
 	crm_malloc0(new_id, id_len);
 
 	if(use_child) {
 		snprintf(new_id, id_len, "%s%s%s:%d",
 			 use_prefix?prefix:"", use_prefix?":":"", id, child);
 	} else {
 		snprintf(new_id, id_len, "%s%s%s",
 			 use_prefix?prefix:"", use_prefix?":":"", id);
 	}
 	
 	crm_xml_add(xml_obj, XML_ATTR_ID, new_id);
 	crm_free(new_id);
 }
 
 static void
 resource_node_score(resource_t *rsc, node_t *node, int score, const char *tag) 
 {
 	node_t *match = NULL;
 
 	if(rsc->children) {
 	    slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		resource_node_score(child_rsc, node, score, tag);
 		);
 	}
 	
 	crm_debug_2("Setting %s for %s on %s: %d",
 		    tag, rsc->id, node->details->uname, score);
 	match = pe_find_node_id(rsc->allowed_nodes, node->details->id);
 	if(match == NULL) {
 		match = node_copy(node);
 		match->weight = 0;
 		rsc->allowed_nodes = g_list_append(rsc->allowed_nodes, match);
 	}
 	match->weight = merge_weights(match->weight, score);
 }
 
 void
 resource_location(resource_t *rsc, node_t *node, int score, const char *tag,
 		  pe_working_set_t *data_set) 
 {
 	if(node != NULL) {
 		resource_node_score(rsc, node, score, tag);
 
 	} else if(data_set != NULL) {
 		slist_iter(
 			node, node_t, data_set->nodes, lpc,
 			resource_node_score(rsc, node, score, tag);
 			);
 	} else {
 		slist_iter(
 			node, node_t, rsc->allowed_nodes, lpc,
 			resource_node_score(rsc, node, score, tag);
 			);
 	}
 
 	if(node == NULL && score == -INFINITY) {
 		if(rsc->allocated_to) {
 			crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
 			crm_free(rsc->allocated_to);
 			rsc->allocated_to = NULL;
 		}
 	}
 }
 
 #define sort_return(an_int) crm_free(a_uuid); crm_free(b_uuid); return an_int
 
 gint
 sort_op_by_callid(gconstpointer a, gconstpointer b)
 {
 	char *a_uuid = NULL;
 	char *b_uuid = NULL;
 	const xmlNode *xml_a = a;
 	const xmlNode *xml_b = b;
 	
  	const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
  	const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
 
  	const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID);
  	const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID);
 
 	const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
  	const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
 
 	int dummy = -1;
 	
 	int a_id = -1;
 	int b_id = -1;
 
 	int a_rc = -1;
 	int b_rc = -1;
 
 	int a_status = -1;
 	int b_status = -1;
 	
 	int a_call_id = -1;
 	int b_call_id = -1;
 
 	if(safe_str_eq(a_xml_id, b_xml_id)) {
 		/* We have duplicate lrm_rsc_op entries in the status
 		 *    section which is unliklely to be a good thing
 		 *    - we can handle it easily enough, but we need to get
 		 *    to the bottom of why its happening.
 		 */
 		pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
 		sort_return(0);
 	}
 	
 	CRM_CHECK(a_task_id != NULL && b_task_id != NULL,
 		  crm_err("a: %s, b: %s", crm_str(a_xml_id), crm_str(b_xml_id));
 		  sort_return(0));	
 	a_call_id = crm_parse_int(a_task_id, NULL);
 	b_call_id = crm_parse_int(b_task_id, NULL);
 	
 	if(a_call_id == -1 && b_call_id == -1) {
 		/* both are pending ops so it doesnt matter since
 		 *   stops are never pending
 		 */
 		sort_return(0);
 
 	} else if(a_call_id >= 0 && a_call_id < b_call_id) {
 		crm_debug_4("%s (%d) < %s (%d) : call id",
 			    a_xml_id, a_call_id, b_xml_id, b_call_id);
 		sort_return(-1);
 
 	} else if(b_call_id >= 0 && a_call_id > b_call_id) {
 		crm_debug_4("%s (%d) > %s (%d) : call id",
 			    a_xml_id, a_call_id, b_xml_id, b_call_id);
 		sort_return(1);
 	}
 
 	crm_debug_5("%s (%d) == %s (%d) : continuing",
 		    a_xml_id, a_call_id, b_xml_id, b_call_id);
 	
 	/* now process pending ops */
 	CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0));
 	CRM_CHECK(decode_transition_magic(
 		      a_key, &a_uuid, &a_id, &dummy, &a_status, &a_rc, &dummy),
 		  sort_return(0));
 	CRM_CHECK(decode_transition_magic(
 		      b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy),
 		  sort_return(0));
 
 	/* try and determin the relative age of the operation...
 	 * some pending operations (ie. a start) may have been supuerceeded
 	 *   by a subsequent stop
 	 *
 	 * [a|b]_id == -1 means its a shutdown operation and _always_ comes last
 	 */
 	if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
 		/*
 		 * some of the logic in here may be redundant...
 		 *
 		 * if the UUID from the TE doesnt match then one better
 		 *   be a pending operation.
 		 * pending operations dont survive between elections and joins
 		 *   because we query the LRM directly
 		 */
 		
 		CRM_CHECK(a_call_id == -1 || b_call_id == -1,
 			  crm_err("a: %s=%d, b: %s=%d",
 				  crm_str(a_xml_id), a_call_id, crm_str(b_xml_id), b_call_id);
 			  sort_return(0));
 		CRM_CHECK(a_call_id >= 0  || b_call_id >= 0, sort_return(0));
 
 		if(b_call_id == -1) {
 			crm_debug_2("%s (%d) < %s (%d) : transition + call id",
 				    a_xml_id, a_call_id, b_xml_id, b_call_id);
 			sort_return(-1);
 		}
 
 		if(a_call_id == -1) {
 			crm_debug_2("%s (%d) > %s (%d) : transition + call id",
 				    a_xml_id, a_call_id, b_xml_id, b_call_id);
 			sort_return(1);
 		}
 		
 	} else if((a_id >= 0 && a_id < b_id) || b_id == -1) {
 		crm_debug_3("%s (%d) < %s (%d) : transition",
 			    a_xml_id, a_id, b_xml_id, b_id);
 		sort_return(-1);
 
 	} else if((b_id >= 0 && a_id > b_id) || a_id == -1) {
 		crm_debug_3("%s (%d) > %s (%d) : transition",
 			    a_xml_id, a_id, b_xml_id, b_id);
 		sort_return(1);
 	}
 
 	/* we should never end up here */
 	crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default",
 		a_xml_id, a_call_id, a_id, a_uuid, b_xml_id, b_call_id, b_id, b_uuid);
 	CRM_CHECK(FALSE, sort_return(0)); 
 }
 
 time_t get_timet_now(pe_working_set_t *data_set) 
 {
     time_t now = 0;
     if(data_set && data_set->now) {
 	now = data_set->now->tm_now;
     }
     
     if(now == 0) {
 	/* eventually we should convert data_set->now into time_tm
 	 * for now, its only triggered by PE regression tests
 	 */
 	now = time(NULL);
 	crm_crit("Defaulting to 'now'");
 	if(data_set && data_set->now) {
 	    data_set->now->tm_now = now;
 	}
     }
     return now;
 }
 
 
 int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set) 
 {
     int last = 0;
     int fail_count = 0;
     resource_t *failed = rsc;
     char *fail_attr = crm_concat("fail-count", rsc->id, '-');
     const char *value = g_hash_table_lookup(node->details->attrs, fail_attr);
 
     if(is_not_set(rsc->flags, pe_rsc_unique)) {
 	failed = uber_parent(rsc);
     }
     
     if(value != NULL) {
 	fail_count = char2score(value);
 	crm_info("%s has failed %d times on %s",
 		 rsc->id, fail_count, node->details->uname);
     }
     crm_free(fail_attr);
     
     fail_attr = crm_concat("last-failure", rsc->id, '-');
     value = g_hash_table_lookup(node->details->attrs, fail_attr);
     if(value != NULL && rsc->failure_timeout) {
 	last = crm_parse_int(value, NULL);
 	if(last_failure) {
 	    *last_failure = last;
 	}
 	if(last > 0) {
 	    time_t now = get_timet_now(data_set);		
 	    if(now > (last + rsc->failure_timeout)) {
 		crm_notice("Failcount for %s on %s has expired (limit was %ds)",
 			   failed->id, node->details->uname, rsc->failure_timeout);
 		fail_count = 0;
 	    }
 	}
     }
     
     crm_free(fail_attr);
     return fail_count;
 }
diff --git a/pengine/native.c b/pengine/native.c
index ce83be483f..fc4cf44083 100644
--- a/pengine/native.c
+++ b/pengine/native.c
@@ -1,2070 +1,2065 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program 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.1 of the License, or (at your option) any later version.
  * 
  * This software 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 this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <crm_internal.h>
 
 #include <pengine.h>
 #include <crm/pengine/rules.h>
 #include <lib/crm/pengine/utils.h>
 #include <crm/msg_xml.h>
 #include <allocate.h>
 #include <utils.h>
 
 #define DELETE_THEN_REFRESH 1 /* The crmd will remove the resource from the CIB itself, making this redundant */
 
 #define VARIANT_NATIVE 1
 #include <lib/crm/pengine/variant.h>
 
 gboolean at_stack_bottom(resource_t *rsc);
 
 void node_list_update(GListPtr list1, GListPtr list2, int factor);
 
 void native_rsc_colocation_rh_must(resource_t *rsc_lh, gboolean update_lh,
 				   resource_t *rsc_rh, gboolean update_rh);
 
 void native_rsc_colocation_rh_mustnot(resource_t *rsc_lh, gboolean update_lh,
 				      resource_t *rsc_rh, gboolean update_rh);
 
 void create_notifications(resource_t *rsc, pe_working_set_t *data_set);
 void Recurring(resource_t *rsc, action_t *start, node_t *node,
 			      pe_working_set_t *data_set);
 void RecurringOp(resource_t *rsc, action_t *start, node_t *node,
 		 xmlNode *operation, pe_working_set_t *data_set);
 void pe_pre_notify(
 	resource_t *rsc, node_t *node, action_t *op, 
 	notify_data_t *n_data, pe_working_set_t *data_set);
 void pe_post_notify(
 	resource_t *rsc, node_t *node, action_t *op, 
 	notify_data_t *n_data, pe_working_set_t *data_set);
 
 void NoRoleChange  (resource_t *rsc, node_t *current, node_t *next, pe_working_set_t *data_set);
 gboolean DeleteRsc (resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set);
 gboolean StopRsc   (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 gboolean StartRsc  (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 gboolean DemoteRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 gboolean PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 gboolean RoleError (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 gboolean NullOp    (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set);
 
 enum rsc_role_e rsc_state_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = {
 /* Current State */	
 /*    Next State:  Unknown 	    Stopped	      Started	        Slave	          Master */
 /* Unknown */	{ RSC_ROLE_UNKNOWN, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, },
 /* Stopped */	{ RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE,   RSC_ROLE_SLAVE, },
 /* Started */	{ RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE,   RSC_ROLE_MASTER, },
 /* Slave */	{ RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE,   RSC_ROLE_MASTER, },
 /* Master */	{ RSC_ROLE_STOPPED, RSC_ROLE_SLAVE,   RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE,   RSC_ROLE_MASTER, },
 };
 
 gboolean (*rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX])(resource_t*,node_t*,gboolean,pe_working_set_t*) = {
 /* Current State */	
 /*    Next State: Unknown	Stopped		Started		Slave		Master */
 /* Unknown */	{ RoleError,	StopRsc,	RoleError,	RoleError,	RoleError,  },
 /* Stopped */	{ RoleError,	NullOp,		StartRsc,	StartRsc,	RoleError,  },
 /* Started */	{ RoleError,	StopRsc,	NullOp,		NullOp,		PromoteRsc, },
 /* Slave */	{ RoleError,	StopRsc,	RoleError,	NullOp,		PromoteRsc, },
 /* Master */	{ RoleError,	RoleError,	RoleError,	DemoteRsc,	NullOp,     },
 };
 
 
 static gboolean
 native_choose_node(resource_t *rsc)
 {
 	/*
 	  1. Sort by weight
 	  2. color.chosen_node = the node (of those with the highest wieght)
 				   with the fewest resources
 	  3. remove color.chosen_node from all other colors
 	*/
 	GListPtr nodes = NULL;
 	node_t *chosen = NULL;
 
 	if(is_not_set(rsc->flags, pe_rsc_provisional)) {
 		return rsc->allocated_to?TRUE:FALSE;
 	}
 	
 	crm_debug_3("Choosing node for %s from %d candidates",
 		    rsc->id, g_list_length(rsc->allowed_nodes));
 
 	if(rsc->allowed_nodes) {
 		rsc->allowed_nodes = g_list_sort(
 			rsc->allowed_nodes, sort_node_weight);
 		nodes = rsc->allowed_nodes;
 		chosen = g_list_nth_data(nodes, 0);
 	}
 	
 	return native_assign_node(rsc, nodes, chosen);
 }
 
 GListPtr
 native_merge_weights(
     resource_t *rsc, const char *rhs, GListPtr nodes, int factor, gboolean allow_rollback) 
 {
     GListPtr archive = NULL;
 
     if(is_set(rsc->flags, pe_rsc_merging)) {
 	crm_info("%s: Breaking dependancy loop", rhs);
 	return nodes;
 
     } else if(is_not_set(rsc->flags, pe_rsc_provisional)) {
 	crm_debug_4("%s: not provisional", rsc->id);
 	return nodes;
     }
 
     set_bit(rsc->flags, pe_rsc_merging);
     crm_debug_2("%s: Combining scores from %s", rhs, rsc->id);
 
     if(allow_rollback) {
  	archive = node_list_dup(nodes, FALSE, FALSE);
     }
 
 #if 1
     node_list_update(nodes, rsc->allowed_nodes, factor);
 #else
     /* turn this off once we switch to migration-threshold */
     {
 	GListPtr tmp = node_list_dup(rsc->allowed_nodes, FALSE, FALSE);
 	
 	slist_iter(
 	    node, node_t, tmp, lpc,
 	    if(node->weight < 0 && node->weight > -INFINITY) {
 		/* Once a dependant's score goes below zero, force the node score to -INFINITY
 		 *
 		 * This prevents the colocation sets from being partially active in scenarios
 		 *  where it could be fully active elsewhere
 		 *
 		 * If we don't do this, then the next resource's stickiness might bring
 		 *  the combined score above 0 again - which confuses the PE into thinking
 		 *  the whole colocation set can run there but is pointless since the later children
 		 *  are not be able to run if the ones before them can't
 		 */
 		node->weight = -INFINITY;
 	    }
 	    );
 	
 	node_list_update(nodes, tmp, factor);
 	pe_free_shallow_adv(tmp, TRUE);
     }
 #endif
     
     if(archive && can_run_any(nodes) == FALSE) {
 	crm_debug("%s: Rolling back scores from %s", rhs, rsc->id);
   	pe_free_shallow_adv(nodes, TRUE);
 	nodes = archive;
 	goto bail;
     }
 
     pe_free_shallow_adv(archive, TRUE);
     
     slist_iter(
 	constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc,
 	
 	nodes = constraint->rsc_lh->cmds->merge_weights(
 	    constraint->rsc_lh, rhs, nodes,
 	    constraint->score/INFINITY, allow_rollback);
 	);
 
   bail:
     clear_bit(rsc->flags, pe_rsc_merging);
     return nodes;
 }
 
 node_t *
 native_color(resource_t *rsc, pe_working_set_t *data_set)
 {
         int alloc_details = scores_log_level;
 	if(rsc->parent && is_not_set(rsc->parent->flags, pe_rsc_allocating)) {
 		/* never allocate children on their own */
 		crm_debug("Escalating allocation of %s to its parent: %s",
 			  rsc->id, rsc->parent->id);
 		rsc->parent->cmds->color(rsc->parent, data_set);
 	}
 	
 	if(is_not_set(rsc->flags, pe_rsc_provisional)) {
 		return rsc->allocated_to;
 	}
 
 	if(is_set(rsc->flags, pe_rsc_allocating)) {
 		crm_debug("Dependancy loop detected involving %s", rsc->id);
 		return NULL;
 	}
 
 	set_bit(rsc->flags, pe_rsc_allocating);
 	print_resource(alloc_details+1, "Allocating: ", rsc, FALSE);
 	dump_node_scores(alloc_details+1, rsc, "Pre-allloc", rsc->allowed_nodes);
 
 	slist_iter(
 		constraint, rsc_colocation_t, rsc->rsc_cons, lpc,
 
 		resource_t *rsc_rh = constraint->rsc_rh;
 		crm_debug_2("%s: Pre-Processing %s (%s)",
 			    rsc->id, constraint->id, rsc_rh->id);
 		rsc_rh->cmds->color(rsc_rh, data_set);
 		rsc->cmds->rsc_colocation_lh(rsc, rsc_rh, constraint);	
 	    );	
 
 	dump_node_scores(alloc_details+1, rsc, "Post-coloc", rsc->allowed_nodes);
 
 	slist_iter(
 	    constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc,
 	    
 	    rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights(
 		constraint->rsc_lh, rsc->id, rsc->allowed_nodes,
 		constraint->score/INFINITY, TRUE);
 	    );
 	
 	dump_node_scores(alloc_details, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes);
 	
 	print_resource(LOG_DEBUG_2, "Allocating: ", rsc, FALSE);
 	if(rsc->next_role == RSC_ROLE_STOPPED) {
 		crm_debug_2("Making sure %s doesn't get allocated", rsc->id);
 		/* make sure it doesnt come up again */
 		resource_location(
 			rsc, NULL, -INFINITY, XML_RSC_ATTR_TARGET_ROLE, data_set);
 	}
 	
 	if(is_set(rsc->flags, pe_rsc_provisional)
 	   && native_choose_node(rsc) ) {
 		crm_debug_3("Allocated resource %s to %s",
 			    rsc->id, rsc->allocated_to->details->uname);
 
 	} else if(rsc->allocated_to == NULL) {
 		if(is_not_set(rsc->flags, pe_rsc_orphan)) {
 			pe_warn("Resource %s cannot run anywhere", rsc->id);
 		} else if(rsc->running_on != NULL) {
 			crm_info("Stopping orphan resource %s", rsc->id);
 		}
 		
 	} else {
 		crm_debug("Pre-Allocated resource %s to %s",
 			  rsc->id, rsc->allocated_to->details->uname);
 	}
 	
 	clear_bit(rsc->flags, pe_rsc_allocating);
 	print_resource(LOG_DEBUG_3, "Allocated ", rsc, TRUE);
 
 	return rsc->allocated_to;
 }
 
 static gboolean is_op_dup(
     resource_t *rsc, const char *name, const char *interval) 
 {
     gboolean dup = FALSE;
     const char *id = NULL;
     const char *value = NULL;
     xml_child_iter_filter(
 	rsc->ops_xml, operation, "op",
 	value = crm_element_value(operation, "name");
 	if(safe_str_neq(value, name)) {
 	    continue;
 	}
 	
 	value = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 	if(value == NULL) {
 	    value = "0";
 	}
 	
 	if(safe_str_neq(value, interval)) {
 	    continue;
 	}
 
 	if(id == NULL) {
 	    id = ID(operation);
 	    
 	} else {
 	    crm_config_err("Operation %s is a duplicate of %s", ID(operation), id);
 	    crm_config_err("Do not use the same (name, interval) combination more than once per resource");
 	    dup = TRUE;
 	}
 	);
     
     return dup;
 }
 
 void
 RecurringOp(resource_t *rsc, action_t *start, node_t *node,
 	    xmlNode *operation, pe_working_set_t *data_set) 
 {
 	char *key = NULL;
 	const char *name = NULL;
 	const char *value = NULL;
 	const char *interval = NULL;
 	const char *node_uname = NULL;
 
-	int interval_ms = 0;
+	unsigned long long interval_ms = 0;
 	action_t *mon = NULL;
 	gboolean is_optional = TRUE;
 	GListPtr possible_matches = NULL;
 	
 	crm_debug_2("Creating recurring action %s for %s",
 		    ID(operation), rsc->id);
 	
 	if(node != NULL) {
 		node_uname = node->details->uname;
 	}
 
 	interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
-	interval_ms = crm_get_msec(interval);
+	interval_ms = crm_get_interval(interval);
 	
 	if(interval_ms == 0) {
 	    return;
-		
-	} else if(interval_ms < 0) {
-	    crm_config_warn("%s contains an invalid interval: %s", ID(operation), interval);
-	    return;
 	}
 	
-	
 	value = crm_element_value(operation, "disabled");
 	if(crm_is_true(value)) {
 		return;
 	}
 	
 	name = crm_element_value(operation, "name");
 	if(is_op_dup(rsc, name, interval)) {
 	    return;
 	}
 
 	key = generate_op_key(rsc->id, name, interval_ms);
 	if(start != NULL) {
 		crm_debug_3("Marking %s %s due to %s",
 			    key, start->optional?"optional":"manditory",
 			    start->uuid);
 		is_optional = start->optional;
 	} else {
 		crm_debug_2("Marking %s optional", key);
 		is_optional = TRUE;
 	}
 
 	/* start a monitor for an already active resource */
 	possible_matches = find_actions_exact(rsc->actions, key, node);
 	if(possible_matches == NULL) {
 		is_optional = FALSE;
 		crm_debug_3("Marking %s manditory: not active", key);
 	} else {
 		g_list_free(possible_matches);
 	}
 	
 	value = crm_element_value(operation, "role");
 	if((rsc->next_role == RSC_ROLE_MASTER && value == NULL)
 	   || (value != NULL && text2role(value) != rsc->next_role)) {
 		int log_level = LOG_DEBUG_2;
 		const char *result = "Ignoring";
 		if(is_optional) {
 			char *local_key = crm_strdup(key);
 			log_level = LOG_INFO;
 			result = "Cancelling";
 			/* its running : cancel it */
 			
 			mon = custom_action(
 				rsc, local_key, RSC_CANCEL, node,
 				FALSE, TRUE, data_set);
 
 			crm_free(mon->task);
 			mon->task = crm_strdup(RSC_CANCEL);
 			add_hash_param(mon->meta, XML_LRM_ATTR_INTERVAL, interval);
 			add_hash_param(mon->meta, XML_LRM_ATTR_TASK, name);
 
 			local_key = NULL;
 
 			switch(rsc->role) {
 			    case RSC_ROLE_SLAVE:
 			    case RSC_ROLE_STARTED:
 				if(rsc->next_role == RSC_ROLE_MASTER) {
 				    local_key = promote_key(rsc);
 
 				} else if(rsc->next_role == RSC_ROLE_STOPPED) {
 				    local_key = stop_key(rsc);
 				}
 				
 				break;
 			    case RSC_ROLE_MASTER:
 				local_key = demote_key(rsc);
 				break;
 			    default:
 				break;
 			}
 
 			if(local_key) {
 			    custom_action_order(rsc, NULL, mon, rsc, local_key, NULL,
 						pe_order_runnable_left, data_set);
 			}
 			
 			mon = NULL;
 		}
 		
 		do_crm_log(log_level, "%s action %s (%s vs. %s)",
 			   result , key, value?value:role2text(RSC_ROLE_SLAVE),
 			   role2text(rsc->next_role));
 
 		crm_free(key);
 		key = NULL;
 		return;
 	}		
 		
 	mon = custom_action(rsc, key, name, node,
 			    is_optional, TRUE, data_set);
 	key = mon->uuid;
 	if(is_optional) {
 		crm_debug_2("%s\t   %s (optional)",
 			    crm_str(node_uname), mon->uuid);
 	}
 	
 	if(start == NULL || start->runnable == FALSE) {
 		crm_debug("%s\t   %s (cancelled : start un-runnable)",
 			  crm_str(node_uname), mon->uuid);
 		mon->runnable = FALSE;
 		
 	} else if(node == NULL
 		  || node->details->online == FALSE
 		  || node->details->unclean) {
 		crm_debug("%s\t   %s (cancelled : no node available)",
 			  crm_str(node_uname), mon->uuid);
 		mon->runnable = FALSE;
 		
 	} else if(mon->optional == FALSE) {
-	    crm_notice(" Start recurring %s (%ds) for %s on %s", mon->task, interval_ms/1000, rsc->id, crm_str(node_uname));
+	    crm_notice(" Start recurring %s (%llus) for %s on %s", mon->task, interval_ms/1000, rsc->id, crm_str(node_uname));
 	}
 	
 	custom_action_order(rsc, start_key(rsc), NULL,
 			    NULL, crm_strdup(key), mon,
 			    pe_order_implies_right|pe_order_runnable_left, data_set);
 	
 	if(rsc->next_role == RSC_ROLE_MASTER) {
 		char *running_master = crm_itoa(EXECRA_RUNNING_MASTER);
 		add_hash_param(mon->meta, XML_ATTR_TE_TARGET_RC, running_master);
 		custom_action_order(
 			rsc, promote_key(rsc), NULL,
 			rsc, NULL, mon,
 			pe_order_optional|pe_order_runnable_left, data_set);
 		crm_free(running_master);
 
 	} else if(rsc->role == RSC_ROLE_MASTER) {
 		custom_action_order(
 			rsc, demote_key(rsc), NULL,
 			rsc, NULL, mon,
 			pe_order_optional|pe_order_runnable_left, data_set);
 	}
 }
 
 void
 Recurring(resource_t *rsc, action_t *start, node_t *node,
 			 pe_working_set_t *data_set) 
 {
 	
 	xml_child_iter_filter(
 		rsc->ops_xml, operation, "op",
 		RecurringOp(rsc, start, node, operation, data_set);		
 		);	
 }
 
 void native_create_actions(resource_t *rsc, pe_working_set_t *data_set)
 {
 	action_t *start = NULL;
 	node_t *chosen = NULL;
 	enum rsc_role_e role = RSC_ROLE_UNKNOWN;
 	enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
 
 	crm_debug_2("Creating actions for %s", rsc->id);
 	
 	chosen = rsc->allocated_to;
 	if(chosen != NULL) {
 		CRM_CHECK(rsc->next_role != RSC_ROLE_UNKNOWN, rsc->next_role = RSC_ROLE_STARTED);
 	}
 
 	unpack_instance_attributes(
 		rsc->xml, XML_TAG_ATTR_SETS,
 		chosen?chosen->details->attrs:NULL,
 		rsc->parameters, NULL, FALSE, data_set->now);
 
 	crm_debug_2("%s: %s->%s", rsc->id,
 		    role2text(rsc->role), role2text(rsc->next_role));
 	
 	if(g_list_length(rsc->running_on) > 1) {
  		if(rsc->recovery_type == recovery_stop_start) {
 			pe_proc_err("Attempting recovery of resource %s", rsc->id);
 			if(rsc->role == RSC_ROLE_MASTER) {
 			    DemoteRsc(rsc, NULL, FALSE, data_set);
 			}
 			StopRsc(rsc, NULL, FALSE, data_set);
 			rsc->role = RSC_ROLE_STOPPED;
 		}
 		
 	} else if(rsc->running_on != NULL) {
 		node_t *current = rsc->running_on->data;
 		NoRoleChange(rsc, current, chosen, data_set);
 
 	} else if(rsc->role == RSC_ROLE_STOPPED && rsc->next_role == RSC_ROLE_STOPPED) {
 		char *key = start_key(rsc);
 		GListPtr possible_matches = find_actions(rsc->actions, key, NULL);
 		slist_iter(
 			action, action_t, possible_matches, lpc,
 			action->optional = TRUE;
 /*			action->pseudo = TRUE; */
 			);
 		g_list_free(possible_matches);
 		crm_debug_2("Stopping a stopped resource");
 		crm_free(key);
 		goto do_recurring;
 		
 	} else if(rsc->role != RSC_ROLE_STOPPED) {
 	    /* A cheap trick to account for the fact that Master/Slave groups may not be
 	     * completely running when we set their role to Slave
 	     */
 	    crm_debug_2("Resetting %s.role = %s (was %s)",
 			rsc->id, role2text(RSC_ROLE_STOPPED), role2text(rsc->role));
 	    rsc->role = RSC_ROLE_STOPPED;
 	}
 
 	role = rsc->role;
 
 	while(role != rsc->next_role) {
 		next_role = rsc_state_matrix[role][rsc->next_role];
 		crm_debug_2("Executing: %s->%s (%s)",
 			  role2text(role), role2text(next_role), rsc->id);
 		if(rsc_action_matrix[role][next_role](
 			   rsc, chosen, FALSE, data_set) == FALSE) {
 			break;
 		}
 		role = next_role;
 	}
 
   do_recurring:
 	if(rsc->next_role != RSC_ROLE_STOPPED || is_set(rsc->flags, pe_rsc_managed) == FALSE) {
 		start = start_action(rsc, chosen, TRUE);
 		Recurring(rsc, start, chosen, data_set);
 	}
 }
 
 void native_internal_constraints(resource_t *rsc, pe_working_set_t *data_set)
 {
 	int type = pe_order_optional;
 	const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 	action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
 
 	if(rsc->variant == pe_native) {
 		type |= pe_order_implies_right;
 	}
 
 	if(rsc->parent == NULL || rsc->parent->variant == pe_group) {
 		type |= pe_order_restart;
 	}
 	
 	new_rsc_order(rsc, RSC_STOP, rsc, RSC_START, type, data_set);
 
 	new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_STOP,
 		      pe_order_demote_stop, data_set);
 
 	new_rsc_order(rsc, RSC_START, rsc, RSC_PROMOTE,
 		      pe_order_runnable_left, data_set);
 
 	new_rsc_order(rsc, RSC_DELETE, rsc, RSC_START,
 		      pe_order_optional, data_set);	
 
 	if(is_set(rsc->flags, pe_rsc_notify)) {
 		new_rsc_order(rsc, "confirmed-post_notify_start", rsc, "pre_notify_promote", 
 			      pe_order_optional, data_set);	
 		new_rsc_order(rsc, "confirmed-post_notify_demote", rsc, "pre_notify_stop",
 			      pe_order_optional, data_set);	
 	}
 
 	if(is_not_set(rsc->flags, pe_rsc_managed)) {
 		crm_debug_3("Skipping fencing constraints for unmanaged resource: %s", rsc->id);
 		return;
 	} 
 
 	if(rsc->variant == pe_native && safe_str_neq(class, "stonith")) {
 	    custom_action_order(
 		rsc, stop_key(rsc), NULL,
 		NULL, crm_strdup(all_stopped->task), all_stopped,
 		pe_order_implies_right|pe_order_runnable_left, data_set);
 	}
 
 }
 
 void native_rsc_colocation_lh(
 	resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint)
 {
 	if(rsc_lh == NULL) {
 		pe_err("rsc_lh was NULL for %s", constraint->id);
 		return;
 
 	} else if(constraint->rsc_rh == NULL) {
 		pe_err("rsc_rh was NULL for %s", constraint->id);
 		return;
 	}
 	
 	crm_debug_2("Processing colocation constraint between %s and %s",
 		    rsc_lh->id, rsc_rh->id);
 	
 	rsc_rh->cmds->rsc_colocation_rh(rsc_lh, rsc_rh, constraint);		
 }
 
 static gboolean
 filter_colocation_constraint(
 	resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint)
 {
 	int level = LOG_DEBUG_4;
 	if(constraint->score == 0){
 		return FALSE;
 	}
 
 	if(constraint->score > 0
 	   && constraint->role_lh != RSC_ROLE_UNKNOWN
 	   && constraint->role_lh != rsc_lh->next_role) {
 		do_crm_log(level, "LH: Skipping constraint: \"%s\" state filter",
 			    role2text(constraint->role_rh));
 		return FALSE;
 	}
 	
 	if(constraint->score > 0
 	   && constraint->role_rh != RSC_ROLE_UNKNOWN
 	   && constraint->role_rh != rsc_rh->next_role) {
 		do_crm_log(level, "RH: Skipping constraint: \"%s\" state filter",
 			    role2text(constraint->role_rh));
 		return FALSE;
 	}
 
 	if(constraint->score < 0
 	   && constraint->role_lh != RSC_ROLE_UNKNOWN
 	   && constraint->role_lh == rsc_lh->next_role) {
 		do_crm_log(level, "LH: Skipping -ve constraint: \"%s\" state filter",
 			    role2text(constraint->role_rh));
 		return FALSE;
 	}
 	
 	if(constraint->score < 0
 	   && constraint->role_rh != RSC_ROLE_UNKNOWN
 	   && constraint->role_rh == rsc_rh->next_role) {
 		do_crm_log(level, "RH: Skipping -ve constraint: \"%s\" state filter",
 			    role2text(constraint->role_rh));
 		return FALSE;
 	}
 
 	return TRUE;
 }
 
 static void
 colocation_match(
 	resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) 
 {
 	const char *tmp = NULL;
 	const char *value = NULL;
 	gboolean do_check = FALSE;
 	const char *attribute = "#id";
 
 	if(constraint->node_attribute != NULL) {
 		attribute = constraint->node_attribute;
 	}
 
 	if(rsc_rh->allocated_to) {
 		value = g_hash_table_lookup(
 			rsc_rh->allocated_to->details->attrs, attribute);
 		do_check = TRUE;
 
 	} else if(constraint->score < 0) {
 		/* nothing to do:
 		 *   anti-colocation with something thats not running
 		 */
 		return;
 	}
 	
 	slist_iter(
 		node, node_t, rsc_lh->allowed_nodes, lpc,
 		tmp = g_hash_table_lookup(node->details->attrs, attribute);
 		if(do_check && safe_str_eq(tmp, value)) {
 		    if(constraint->score < INFINITY) {
 			crm_debug_2("%s: %s.%s += %d", constraint->id, rsc_lh->id,
 				  node->details->uname, constraint->score);
 			node->weight = merge_weights(
 				constraint->score, node->weight);
 		    }
 
 		} else if(do_check == FALSE || constraint->score >= INFINITY) {
 			crm_debug_2("%s: %s.%s = -INFINITY (%s)", constraint->id, rsc_lh->id,
 				  node->details->uname, do_check?"failed":"unallocated");
 			node->weight = -INFINITY;
 		}
 		
 		);
 }
 
 void native_rsc_colocation_rh(
 	resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint)
 {
 	crm_debug_2("%sColocating %s with %s (%s, weight=%d)",
 		    constraint->score >= 0?"":"Anti-",
 		    rsc_lh->id, rsc_rh->id, constraint->id, constraint->score);
 	
 	if(filter_colocation_constraint(rsc_lh, rsc_rh, constraint) == FALSE) {
 		return;
 	}
 	
 	if(is_set(rsc_rh->flags, pe_rsc_provisional)) {
 		return;
 
 	} else if(is_not_set(rsc_lh->flags, pe_rsc_provisional)) {
 		/* error check */
 		struct node_shared_s *details_lh;
 		struct node_shared_s *details_rh;
 		if((constraint->score > -INFINITY) && (constraint->score < INFINITY)) {
 			return;
 		}
 
 		details_rh = rsc_rh->allocated_to?rsc_rh->allocated_to->details:NULL;
 		details_lh = rsc_lh->allocated_to?rsc_lh->allocated_to->details:NULL;
 		
 		if(constraint->score == INFINITY && details_lh != details_rh) {
 			crm_err("%s and %s are both allocated"
 				" but to different nodes: %s vs. %s",
 				rsc_lh->id, rsc_rh->id,
 				details_lh?details_lh->uname:"n/a",
 				details_rh?details_rh->uname:"n/a");
 
 		} else if(constraint->score == -INFINITY && details_lh == details_rh) {
 			crm_err("%s and %s are both allocated"
 				" but to the SAME node: %s",
 				rsc_lh->id, rsc_rh->id,
 				details_rh?details_rh->uname:"n/a");
 		}
 		
 		return;
 		
 	} else {
 		colocation_match(rsc_lh, rsc_rh, constraint);
 	}
 }
 
 void
 node_list_update(GListPtr list1, GListPtr list2, int factor)
 {
 	node_t *other_node = NULL;
 
 	slist_iter(
 		node, node_t, list1, lpc,
 
 		if(node == NULL) {
 			continue;
 		}
 
 		other_node = (node_t*)pe_find_node_id(list2, node->details->id);
 
 		if(other_node != NULL) {
 			crm_debug_2("%s: %d + %d",
 				    node->details->uname, 
 				    node->weight, other_node->weight);
 			node->weight = merge_weights(
 				factor*other_node->weight, node->weight);
 		}
 		);	
 }
 
 static GListPtr find_actions_by_task(GListPtr actions, resource_t *rsc, const char *original_key)
 {
     GListPtr list = NULL;
 
     list = find_actions(actions, original_key, NULL);
     if(list == NULL) {
 	/* we're potentially searching a child of the original resource */
 	char *key = NULL;
 	char *tmp = NULL;
 	char *task = NULL;
 	int interval = 0;
 	CRM_CHECK(parse_op_key(original_key, &tmp, &task, &interval),
 		  crm_err("search key: %s", original_key); return NULL);
 	
 	key = generate_op_key(rsc->id, task, interval);
 	list = find_actions(actions, key, NULL);
 
 	crm_free(key);
 	crm_free(tmp);
 	crm_free(task);
     }
 
     return list;
 }
 
 void native_rsc_order_lh(resource_t *lh_rsc, order_constraint_t *order, pe_working_set_t *data_set)
 {
 	GListPtr lh_actions = NULL;
 	action_t *lh_action = order->lh_action;
 	resource_t *rh_rsc = order->rh_rsc;
 
 	crm_debug_3("Processing LH of ordering constraint %d", order->id);
 	CRM_ASSERT(lh_rsc != NULL);
 	
 	if(lh_action != NULL) {
 		lh_actions = g_list_append(NULL, lh_action);
 
 	} else if(lh_action == NULL) {
 		lh_actions = find_actions_by_task(
 		    lh_rsc->actions, lh_rsc, order->lh_action_task);
 	}
 
 	if(lh_actions == NULL && lh_rsc != rh_rsc) {
 		char *key = NULL;
 		char *rsc_id = NULL;
 		char *op_type = NULL;
 		int interval = 0;
 		
 		crm_debug_2("No LH-Side (%s/%s) found for constraint %d with %s - creating",
 			    lh_rsc->id, order->lh_action_task,
 			    order->id, order->rh_action_task);
 
 		parse_op_key(
 			order->lh_action_task, &rsc_id, &op_type, &interval);
 
 		key = generate_op_key(lh_rsc->id, op_type, interval);
 
 		lh_action = custom_action(lh_rsc, key, op_type,
 					  NULL, TRUE, TRUE, data_set);
 
 		if(lh_rsc->fns->state(lh_rsc, TRUE) == RSC_ROLE_STOPPED
 		   && safe_str_eq(op_type, RSC_STOP)) {
 			lh_action->pseudo = TRUE;
 			lh_action->runnable = TRUE;
 		}
 		
 		lh_actions = g_list_append(NULL, lh_action);
 
 		crm_free(op_type);
 		crm_free(rsc_id);
 	}
 
 	slist_iter(
 		lh_action_iter, action_t, lh_actions, lpc,
 
 		if(rh_rsc == NULL && order->rh_action) {
 			rh_rsc = order->rh_action->rsc;
 		}
 		if(rh_rsc) {
 			rh_rsc->cmds->rsc_order_rh(
 				lh_action_iter, rh_rsc, order);
 
 		} else if(order->rh_action) {
 			order_actions(
 				lh_action_iter, order->rh_action, order->type); 
 
 		}
 		);
 
 	pe_free_shallow_adv(lh_actions, FALSE);
 }
 
 void native_rsc_order_rh(
 	action_t *lh_action, resource_t *rsc, order_constraint_t *order)
 {
 	GListPtr rh_actions = NULL;
 	action_t *rh_action = NULL;
 
 	CRM_CHECK(rsc != NULL, return);
 	CRM_CHECK(order != NULL, return);
 
 	rh_action = order->rh_action;
 	crm_debug_3("Processing RH of ordering constraint %d", order->id);
 
 	if(rh_action != NULL) {
 		rh_actions = g_list_append(NULL, rh_action);
 
 	} else if(rsc != NULL) {
 		rh_actions = find_actions_by_task(
 		    rsc->actions, rsc, order->rh_action_task);
 	}
 
 	if(rh_actions == NULL) {
 		crm_debug_4("No RH-Side (%s/%s) found for constraint..."
 			    " ignoring", rsc->id,order->rh_action_task);
 		if(lh_action) {
 			crm_debug_4("LH-Side was: %s", lh_action->uuid);
 		}
 		return;
 	}
 	
 	slist_iter(
 		rh_action_iter, action_t, rh_actions, lpc,
 
 		if(lh_action) {
 			order_actions(lh_action, rh_action_iter, order->type); 
 			
 		} else if(order->type & pe_order_implies_right) {
 			rh_action_iter->runnable = FALSE;
 			crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, order->type);
 		} else {
 			crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, order->type);
 		}
 		
 		);
 
 	pe_free_shallow_adv(rh_actions, FALSE);
 }
 
 void native_rsc_location(resource_t *rsc, rsc_to_node_t *constraint)
 {
 	GListPtr or_list;
 
 	crm_debug_2("Applying %s (%s) to %s", constraint->id,
 		    role2text(constraint->role_filter), rsc->id);
 
 	/* take "lifetime" into account */
 	if(constraint == NULL) {
 		pe_err("Constraint is NULL");
 		return;
 
 	} else if(rsc == NULL) {
 		pe_err("LHS of rsc_to_node (%s) is NULL", constraint->id);
 		return;
 
 	} else if(constraint->role_filter > 0
 		  && constraint->role_filter != rsc->next_role) {
 		crm_debug("Constraint (%s) is not active (role : %s)",
 			  constraint->id, role2text(constraint->role_filter));
 		return;
 		
 	} else if(is_active(constraint) == FALSE) {
 		crm_debug_2("Constraint (%s) is not active", constraint->id);
 		return;
 	}
     
 	if(constraint->node_list_rh == NULL) {
 		crm_debug_2("RHS of constraint %s is NULL", constraint->id);
 		return;
 	}
 	or_list = node_list_or(
 		rsc->allowed_nodes, constraint->node_list_rh, FALSE);
 		
 	pe_free_shallow(rsc->allowed_nodes);
 	rsc->allowed_nodes = or_list;
 	slist_iter(node, node_t, or_list, lpc,
 		   crm_debug_3("%s + %s : %d", rsc->id, node->details->uname, node->weight);
 		);
 }
 
 void native_expand(resource_t *rsc, pe_working_set_t *data_set)
 {
 	crm_debug_3("Processing actions from %s", rsc->id);
 
 	slist_iter(
 		action, action_t, rsc->actions, lpc,
 		crm_debug_4("processing action %d for rsc=%s",
 			  action->id, rsc->id);
 		graph_element_from_action(action, data_set);
 		);
 
 	slist_iter(
 	    child_rsc, resource_t, rsc->children, lpc,
 	    
 	    child_rsc->cmds->expand(child_rsc, data_set);
 	    );
 }
 
 void
 create_notifications(resource_t *rsc, pe_working_set_t *data_set)
 {
 }
 
 static void
 register_activity(resource_t *rsc, enum action_tasks task, node_t *node, notify_data_t *n_data)
 {
 	notify_entry_t *entry = NULL;
 
 	if(node == NULL) {
 	    pe_proc_warn("%s has no node for required action %s", rsc->id, task2text(task));
 	    return;
 	}
 
 	crm_malloc0(entry, sizeof(notify_entry_t));
 	entry->rsc = rsc;
 	entry->node = node;
 
 	switch(task) {
 		case start_rsc:
 			n_data->start = g_list_append(n_data->start, entry);
 			break;
 		case stop_rsc:
 			n_data->stop = g_list_append(n_data->stop, entry);
 			break;
 		case action_promote:
 			n_data->promote = g_list_append(n_data->promote, entry);
 			break;
 		case action_demote:
 			n_data->demote = g_list_append(n_data->demote, entry);
 			break;
 		default:
 			crm_err("Unsupported notify action: %s", task2text(task));
 			crm_free(entry);
 			break;
 	}
 	
 }
 
 
 static void
 register_state(resource_t *rsc, node_t *on_node, notify_data_t *n_data)
 {
 	notify_entry_t *entry = NULL;
 	crm_malloc0(entry, sizeof(notify_entry_t));
 	entry->rsc = rsc;
 	entry->node = on_node;
 
 	crm_debug_2("%s state: %s", rsc->id, role2text(rsc->next_role));
 
 	switch(rsc->next_role) {
 		case RSC_ROLE_STOPPED:
 /* 			n_data->inactive = g_list_append(n_data->inactive, entry); */
 			crm_free(entry);
 			break;
 		case RSC_ROLE_STARTED:
 			n_data->active = g_list_append(n_data->active, entry);
 			break;
 		case RSC_ROLE_SLAVE:
  			n_data->slave = g_list_append(n_data->slave, entry); 
 			break;
 		case RSC_ROLE_MASTER:
 			n_data->master = g_list_append(n_data->master, entry);
 			break;
 		default:
 			crm_err("Unsupported notify role");
 			crm_free(entry);
 			break;
 	}
 }
 
 void
 complex_create_notify_element(resource_t *rsc, action_t *op,
 			     notify_data_t *n_data, pe_working_set_t *data_set)
 {
 	node_t *next_node = NULL;
 	gboolean registered = FALSE;
 	char *op_key = NULL;
 	GListPtr possible_matches = NULL;
 	enum action_tasks task = text2task(op->task);
 
 	if(rsc->children) {
 	    slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		
 		child_rsc->cmds->create_notify_element(
 		    child_rsc, op, n_data, data_set);
 		);
 	    return;
 	}
 	
 	if(op->pre_notify == NULL || op->post_notify == NULL) {
 		/* no notifications required */
 		crm_debug_4("No notificaitons required for %s", op->task);
 		return;
 	}
 	next_node = rsc->allocated_to;
 	op_key = generate_op_key(rsc->id, op->task, 0);
 	possible_matches = find_actions(rsc->actions, op_key, NULL);
 	
 	crm_debug_2("Creating notificaitons for: %s (%s->%s)",
 		    op->uuid, role2text(rsc->role), role2text(rsc->next_role));
 
 	if(rsc->role == rsc->next_role) {
 		register_state(rsc, next_node, n_data);
 	}
 	
 	slist_iter(
 		local_op, action_t, possible_matches, lpc,
 
 		local_op->notify_keys = n_data->keys;
 		if(local_op->optional == FALSE) {
 			registered = TRUE;
 			register_activity(rsc, task, local_op->node, n_data);
 		}		
 		);
 
 	/* stop / demote */
 	if(rsc->role != RSC_ROLE_STOPPED) {
 		if(task == stop_rsc || task == action_demote) {
 			slist_iter(
 				current_node, node_t, rsc->running_on, lpc,
 				pe_pre_notify(rsc, current_node, op, n_data, data_set);
 				if(task == action_demote || registered == FALSE) {
 					pe_post_notify(rsc, current_node, op, n_data, data_set);
 				}
 				);
 		}
 	}
 	
 	/* start / promote */
 	if(rsc->next_role != RSC_ROLE_STOPPED) {	
 		CRM_CHECK(next_node != NULL,;);
 
 		if(next_node == NULL) {
 			pe_proc_err("next role: %s", role2text(rsc->next_role));
 			
 		} else if(task == start_rsc || task == action_promote) {
 			if(task != start_rsc || registered == FALSE) {
 				pe_pre_notify(rsc, next_node, op, n_data, data_set);
 			}
 			pe_post_notify(rsc, next_node, op, n_data, data_set);
 		}
 	}
 	
 	crm_free(op_key);
 	g_list_free(possible_matches);
 }
 
 
 static void dup_attr(gpointer key, gpointer value, gpointer user_data)
 {
 	char *meta_key = crm_concat(CRM_META, key, '_');
 	g_hash_table_replace(user_data, meta_key, crm_strdup(value));
 }
 
 static action_t *
 pe_notify(resource_t *rsc, node_t *node, action_t *op, action_t *confirm,
 	  notify_data_t *n_data, pe_working_set_t *data_set)
 {
 	char *key = NULL;
 	action_t *trigger = NULL;
 	const char *value = NULL;
 	const char *task = NULL;
 	
 	if(op == NULL || confirm == NULL) {
 		crm_debug_2("Op=%p confirm=%p", op, confirm);
 		return NULL;
 	}
 
 	CRM_CHECK(node != NULL, return NULL);
 
 	if(node->details->online == FALSE) {
 		crm_debug_2("Skipping notification for %s: node offline", rsc->id);
 		return NULL;
 	} else if(op->runnable == FALSE) {
 		crm_debug_2("Skipping notification for %s: not runnable", op->uuid);
 		return NULL;
 	}
 	
 	value = g_hash_table_lookup(op->meta, "notify_type");
 	task = g_hash_table_lookup(op->meta, "notify_operation");
 
 	crm_debug_2("Creating notify actions for %s: %s (%s-%s)",
 		    op->uuid, rsc->id, value, task);
 	
 	key = generate_notify_key(rsc->id, value, task);
 	trigger = custom_action(rsc, key, op->task, node,
 				op->optional, TRUE, data_set);
 	g_hash_table_foreach(op->meta, dup_attr, trigger->extra);
 	trigger->notify_keys = n_data->keys;
 
 	/* pseudo_notify before notify */
 	crm_debug_3("Ordering %s before %s (%d->%d)",
 		op->uuid, trigger->uuid, trigger->id, op->id);
 
 	order_actions(op, trigger, pe_order_implies_left);
 	
 	value = g_hash_table_lookup(op->meta, "notify_confirm");
 	if(crm_is_true(value)) {
 		/* notify before pseudo_notified */
 		crm_debug_3("Ordering %s before %s (%d->%d)",
 			    trigger->uuid, confirm->uuid,
 			    confirm->id, trigger->id);
 
 		order_actions(trigger, confirm, pe_order_implies_left);
 	}	
 	return trigger;
 }
 
 void
 pe_pre_notify(resource_t *rsc, node_t *node, action_t *op,
 	      notify_data_t *n_data, pe_working_set_t *data_set)
 {
 	crm_debug_2("%s: %s", rsc->id, op->uuid);
 	pe_notify(rsc, node, op->pre_notify, op->pre_notified,
 		  n_data, data_set);
 }
 
 void
 pe_post_notify(resource_t *rsc, node_t *node, action_t *op, 
 	       notify_data_t *n_data, pe_working_set_t *data_set)
 {
 	action_t *notify = NULL;
 
 	CRM_CHECK(op != NULL, return);
 	CRM_CHECK(rsc != NULL, return);
 	
 	crm_debug_2("%s: %s", rsc->id, op->uuid);
 	notify = pe_notify(rsc, node, op->post_notify, op->post_notified,
 			   n_data, data_set);
 
 	if(notify != NULL) {
 /* 		crm_err("Upgrading priority for %s to INFINITY", notify->uuid); */
 		notify->priority = INFINITY;
 	}
 
 	notify = op->post_notified;
 	if(notify != NULL) {
 		slist_iter(
 			mon, action_t, rsc->actions, lpc,
 
 			const char *interval = g_hash_table_lookup(mon->meta, "interval");
 			if(interval == NULL || safe_str_eq(interval, "0")) {
 				crm_debug_3("Skipping %s: interval", mon->uuid); 
 				continue;
 			} else if(safe_str_eq(mon->task, "cancel")) {
 				crm_debug_3("Skipping %s: cancel", mon->uuid); 
 				continue;
 			}
 
 			order_actions(notify, mon, pe_order_optional);
 			);
 	}
 }
 
 
 void
 NoRoleChange(resource_t *rsc, node_t *current, node_t *next,
 	     pe_working_set_t *data_set)
 {
 	action_t *stop = NULL;
 	action_t *start = NULL;		
 	GListPtr possible_matches = NULL;
 
 	crm_debug_2("Executing: %s (role=%s)", rsc->id, role2text(rsc->next_role));
 
 	if(current == NULL && next == NULL) {
 	    crm_notice("Leave resource %s\t(%s)",
 		       rsc->id, role2text(rsc->role));
 	    return;
 
 	} else if(next == NULL) {
 	    crm_notice("Stop resource %s\t(%s %s)",
 		       rsc->id, role2text(rsc->role), current->details->uname);
 	    return;
 
 	} else if(current == NULL && rsc->next_role > RSC_ROLE_SLAVE) {
 	    crm_notice("Promote %s\t(%s -> %s %s)", rsc->id,
 		       role2text(rsc->role), role2text(rsc->next_role),
 		       next->details->uname);
 	    return;
 
 	} else if(current == NULL) {
 	    crm_notice("Start %s\t(%s -> %s %s)", rsc->id,
 		       role2text(rsc->role), role2text(rsc->next_role),
 		       next->details->uname);
 	    return;
 	}
 
 	if(rsc->role == rsc->next_role) {
 	    start = start_action(rsc, next, TRUE);
 	    if(start->optional) {
 		crm_notice("Leave resource %s\t(%s %s)",
 			   rsc->id, role2text(rsc->role), next->details->uname);
 
 	    } else if(safe_str_eq(current->details->id, next->details->id)) {
 		if(is_set(rsc->flags, pe_rsc_failed)) {
 		    crm_notice("Recover resource %s\t(%s %s)",
 			       rsc->id, role2text(rsc->role), next->details->uname);
 		} else {
 		    crm_notice("Restart resource %s\t(%s %s)",
 			       rsc->id, role2text(rsc->role), next->details->uname);
 		}
 		
 	    } else {
 		crm_notice("Move resource %s\t(%s %s -> %s)",
 			   rsc->id, role2text(rsc->role), current->details->uname, next->details->uname);
 	    }
 
 	} else if(rsc->role < rsc->next_role) {
 	    crm_notice("Promote %s\t(%s -> %s %s)", rsc->id,
 		       role2text(rsc->role), role2text(rsc->next_role),
 		       next->details->uname);
 	    
 	} else if(rsc->role > rsc->next_role) {
 	    crm_notice("Demote %s\t(%s -> %s %s)", rsc->id,
 		       role2text(rsc->role), role2text(rsc->next_role),
 		       next->details->uname);
 	}
 	
 	if(is_set(rsc->flags, pe_rsc_failed)
 	   || safe_str_neq(current->details->id, next->details->id)) {
 
 		if(rsc->next_role > RSC_ROLE_STARTED) {
 		    gboolean optional = TRUE;
 		    if(rsc->role == RSC_ROLE_MASTER) {
 			optional = FALSE;
 		    }
 		    DemoteRsc(rsc, current, optional, data_set);
 		}
 		if(rsc->role == RSC_ROLE_MASTER) {
 			DemoteRsc(rsc, current, FALSE, data_set);
 		}
 		StopRsc(rsc, current, FALSE, data_set);
 		StartRsc(rsc, next, FALSE, data_set);
 		if(rsc->next_role == RSC_ROLE_MASTER) {
 		    PromoteRsc(rsc, next, FALSE, data_set);
 		}
 
 		possible_matches = find_recurring_actions(rsc->actions, next);
 		slist_iter(match, action_t, possible_matches, lpc,
 			   if(match->optional == FALSE) {
 				   crm_debug("Fixing recurring action: %s",
 					     match->uuid);
 				   match->optional = TRUE;
 			   }
 			);
 		g_list_free(possible_matches);
 		
 	} else if(is_set(rsc->flags, pe_rsc_start_pending)) {
 		start = start_action(rsc, next, TRUE);
 		if(start->runnable) {
 			/* wait for StartRsc() to be called */
 			rsc->role = RSC_ROLE_STOPPED;
 		} else {
 			/* wait for StopRsc() to be called */
 			rsc->next_role = RSC_ROLE_STOPPED;
 		}
 		
 	} else {
 	    
 		stop = stop_action(rsc, current, TRUE);
 		start = start_action(rsc, next, TRUE);
 		stop->optional = start->optional;
 		if(rsc->next_role > RSC_ROLE_STARTED) {
 		    DemoteRsc(rsc, current, start->optional, data_set);
 		}
 		StopRsc(rsc, current, start->optional, data_set);
 		StartRsc(rsc, current, start->optional, data_set);
 		if(rsc->next_role == RSC_ROLE_MASTER) {
 			PromoteRsc(rsc, next, start->optional, data_set);
 		}
 		
 		if(start->runnable == FALSE) {
 			rsc->next_role = RSC_ROLE_STOPPED;
 		}
 	}
 }
 
 
 gboolean
 StopRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	action_t *stop = NULL;
 	const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 
 	crm_debug_2("Executing: %s", rsc->id);
 
 	if(rsc->next_role == RSC_ROLE_STOPPED
 	   && rsc->variant == pe_native
 	   && safe_str_eq(class, "stonith")) {
 	    action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
 	    custom_action_order(
 		NULL, crm_strdup(all_stopped->task), all_stopped,
 		rsc, stop_key(rsc), NULL,
 		pe_order_implies_left|pe_order_stonith_stop, data_set);
 	}
 	
 	slist_iter(
 		current, node_t, rsc->running_on, lpc,
 		stop = stop_action(rsc, current, optional);
 
 		if(stop->runnable && stop->optional == FALSE) {
 		    crm_notice("  %s\tStop %s",current->details->uname,rsc->id);
 		}
 		
 		if(data_set->remove_after_stop) {
 			DeleteRsc(rsc, current, optional, data_set);
 		}
 		);
 	
 	return TRUE;
 }
 
 
 gboolean
 StartRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	action_t *start = NULL;
 	
 	crm_debug_2("Executing: %s", rsc->id);
 	start = start_action(rsc, next, TRUE);
 	if(start->runnable && optional == FALSE) {
 		crm_notice(" %s\tStart %s", next->details->uname, rsc->id);
 		start->optional = FALSE;
 	}		
 	return TRUE;
 }
 
 gboolean
 PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	char *key = NULL;
 	gboolean runnable = TRUE;
 	GListPtr action_list = NULL;
 	crm_debug_2("Executing: %s", rsc->id);
 
 	CRM_CHECK(rsc->next_role == RSC_ROLE_MASTER,
 		  crm_err("Next role: %s", role2text(rsc->next_role));
 		  return FALSE);
 
 	CRM_CHECK(next != NULL, return FALSE);
 
 	key = start_key(rsc);
 	action_list = find_actions_exact(rsc->actions, key, next);
 	crm_free(key);
 
 	slist_iter(start, action_t, action_list, lpc,
 		   if(start->runnable == FALSE) {
 			   runnable = FALSE;
 		   }
 		);
 
 	g_list_free(action_list);
 
 	if(runnable) {
 		promote_action(rsc, next, optional);
 		if(optional == FALSE) {
 			crm_debug("%s\tPromote %s", next->details->uname, rsc->id);
 		}
 		return TRUE;
 	} 
 
 	crm_debug("%s\tPromote %s (canceled)", next->details->uname, rsc->id);
 
 	key = promote_key(rsc);
 	action_list = find_actions_exact(rsc->actions, key, next);
 	crm_free(key);
 
 	slist_iter(promote, action_t, action_list, lpc,
 		   promote->runnable = FALSE;
 		);
 	
 	g_list_free(action_list);
 	return TRUE;
 }
 
 gboolean
 DemoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	crm_debug_2("Executing: %s", rsc->id);
 
 /* 	CRM_CHECK(rsc->next_role == RSC_ROLE_SLAVE, return FALSE); */
 	slist_iter(
 		current, node_t, rsc->running_on, lpc,
 		do_crm_log(optional?LOG_DEBUG:LOG_NOTICE, "%s\tDemote %s", current->details->uname, rsc->id);
 		demote_action(rsc, current, optional);
 		);
 	return TRUE;
 }
 
 gboolean
 RoleError(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	crm_debug("Executing: %s", rsc->id);
 	CRM_CHECK(FALSE, return FALSE);
 	return FALSE;
 }
 
 gboolean
 NullOp(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set)
 {
 	crm_debug_2("Executing: %s", rsc->id);
 	return FALSE;
 }
 
 gboolean
 DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set)
 {
 	action_t *delete = NULL;
 #if DELETE_THEN_REFRESH
  	action_t *refresh = NULL;
 #endif
 	if(is_set(rsc->flags, pe_rsc_failed)) {
 		crm_debug_2("Resource %s not deleted from %s: failed",
 			    rsc->id, node->details->uname);
 		return FALSE;
 		
 	} else if(node == NULL) {
 		crm_debug_2("Resource %s not deleted: NULL node", rsc->id);
 		return FALSE;
 		
 	} else if(node->details->unclean || node->details->online == FALSE) {
 		crm_debug_2("Resource %s not deleted from %s: unrunnable",
 			    rsc->id, node->details->uname);
 		return FALSE;
 	}
 	
 	crm_notice("Removing %s from %s",
 		 rsc->id, node->details->uname);
 	
 	delete = delete_action(rsc, node, optional);
 	
 	new_rsc_order(rsc, RSC_STOP, rsc, RSC_DELETE, 
 		      optional?pe_order_implies_right:pe_order_implies_left, data_set);
 	
 #if DELETE_THEN_REFRESH
 	refresh = custom_action(
 		NULL, crm_strdup(CRM_OP_LRM_REFRESH), CRM_OP_LRM_REFRESH,
 		node, FALSE, TRUE, data_set);
 
 	add_hash_param(refresh->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
 
 	order_actions(delete, refresh, pe_order_optional);
 #endif
 	
 	return TRUE;
 }
 
 gboolean
 native_create_probe(resource_t *rsc, node_t *node, action_t *complete,
 		    gboolean force, pe_working_set_t *data_set) 
 {
 	char *key = NULL;
 	char *target_rc = NULL;
 	action_t *probe = NULL;
 	node_t *running = NULL;
 
 	CRM_CHECK(node != NULL, return FALSE);
 
 	if(rsc->children) {
 	    gboolean any_created = FALSE;
 	    
 	    slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		
 		any_created = child_rsc->cmds->create_probe(
 		    child_rsc, node, complete, force, data_set) || any_created;
 		);
 
 	    return any_created;
 	}
 
 	if(is_set(rsc->flags, pe_rsc_orphan)) {
 		crm_debug_2("Skipping orphan: %s", rsc->id);
 		return FALSE;
 	}
 	
 	running = pe_find_node_id(rsc->known_on, node->details->id);
 	if(force == FALSE && running != NULL) {
 		/* we already know the status of the resource on this node */
 		crm_debug_3("Skipping active: %s", rsc->id);
 		return FALSE;
 	}
 
 	key = generate_op_key(rsc->id, RSC_STATUS, 0);
 	probe = custom_action(rsc, key, RSC_STATUS, node,
 			      FALSE, TRUE, data_set);
 	probe->optional = FALSE;
 	
 	running = pe_find_node_id(rsc->running_on, node->details->id);
 	if(running == NULL) {
 		target_rc = crm_itoa(EXECRA_NOT_RUNNING);
 
 	} else if(rsc->role == RSC_ROLE_MASTER) {
 		target_rc = crm_itoa(EXECRA_RUNNING_MASTER);
 	}
 
 	if(target_rc != NULL) {
 		add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, target_rc);
 		crm_free(target_rc);
 	}
 	
 	crm_debug_2("Probing %s on %s (%s)", rsc->id, node->details->uname, role2text(rsc->role));
 	order_actions(probe, complete, pe_order_implies_right);
 
 	return TRUE;
 }
 
 static void
 native_start_constraints(
 	resource_t *rsc,  action_t *stonith_op, gboolean is_stonith,
 	pe_working_set_t *data_set)
 {
 	node_t *target = stonith_op?stonith_op->node:NULL;
 
 	if(is_stonith) {
 		char *key = start_key(rsc);
 		action_t *ready = get_pseudo_op(STONITH_UP, data_set);
 		crm_debug_2("Ordering %s action before stonith events", key);
 		custom_action_order(
 			rsc, key, NULL,
 			NULL, crm_strdup(ready->task), ready,
 			pe_order_implies_right, data_set);
 
 	} else {
 		action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
 		slist_iter(action, action_t, rsc->actions, lpc2,
 			   if(action->needs == rsc_req_stonith) {
 			       order_actions(all_stopped, action, pe_order_implies_left);
 
 			   } else if(target != NULL
 			      && safe_str_eq(action->task, RSC_START)
 			      && NULL == pe_find_node_id(
 				      rsc->known_on, target->details->id)) {
 				   /* if known == NULL, then we dont know if
 				    *   the resource is active on the node
 				    *   we're about to shoot
 				    *
 				    * in this case, regardless of action->needs,
 				    *   the only safe option is to wait until
 				    *   the node is shot before doing anything
 				    *   to with the resource
 				    *
 				    * its analogous to waiting for all the probes
 				    *   for rscX to complete before starting rscX
 				    *
 				    * the most likely explaination is that the
 				    *   DC died and took its status with it
 				    */
 				   
 				   crm_info("Ordering %s after %s recovery",
 					    action->uuid, target->details->uname);
 				   order_actions(all_stopped, action,
 						 pe_order_implies_left|pe_order_runnable_left);
 			   }
 			   
 			);
 	}
 }
 
 static void
 native_stop_constraints(
 	resource_t *rsc,  action_t *stonith_op, gboolean is_stonith,
 	pe_working_set_t *data_set)
 {
 	char *key = NULL;
 	GListPtr action_list = NULL;
 	node_t *node = stonith_op->node;
 	
 	key = stop_key(rsc);
 	action_list = find_actions(rsc->actions, key, node);
 	crm_free(key);
 
 	/* add the stonith OP as a stop pre-req and the mark the stop
 	 * as a pseudo op - since its now redundant
 	 */
 	
 	slist_iter(
 	    action, action_t, action_list, lpc2,
 
 	    resource_t *parent = NULL;
 	    if(node->details->online
 	       && node->details->unclean == FALSE
 	       && is_set(rsc->flags, pe_rsc_failed)) {
 		continue;
 	    }
 
 	    if(is_set(rsc->flags, pe_rsc_failed)) {
 		crm_warn("Stop of failed resource %s is"
 			 " implicit after %s is fenced",
 			 rsc->id, node->details->uname);
 	    } else {
 		crm_info("%s is implicit after %s is fenced",
 			 action->uuid, node->details->uname);
 	    }
 
 	    /* the stop would never complete and is
 	     * now implied by the stonith operation
 	     */
 	    action->pseudo = TRUE;
 	    action->runnable = TRUE;
 	    action->implied_by_stonith = TRUE;
 	    
 	    if(is_stonith == FALSE) {
 		order_actions(stonith_op, action, pe_order_optional);
 	    }
 	    
 	    /* find the top-most resource */
 	    parent = rsc->parent;
 	    while(parent != NULL && parent->parent != NULL) {
 		parent = parent->parent;
 	    }
 	    
 	    if(parent) {
 		crm_debug_2("Re-creating actions for %s", parent->id);
 		parent->cmds->create_actions(parent, data_set);
 		
 		/* make sure we dont mess anything up in create_actions */
 		CRM_CHECK(action->pseudo, action->pseudo = TRUE);
 		CRM_CHECK(action->runnable, action->runnable = TRUE);
 	    }
 /* From Bug #1601, successful fencing must be an input to a failed resources stop action.
 
    However given group(A, B) running on nodeX and B.stop has failed, 
    A := stop healthy resource (A.stop)
    B := stop failed resource (pseudo operation B.stop)
    C := stonith nodeX
    A requires B, B requires C, C requires A
    This loop would prevent the cluster from making progress.
 
    This block creates the "C requires A" dependancy and therefore must (at least
    for now) be disabled.
 
    Instead, run the block above and treat all resources on nodeX as B would be
    (marked as a pseudo op depending on the STONITH).
    
 		} else if(is_stonith == FALSE) {
 			crm_info("Moving healthy resource %s"
 				 " off %s before fencing",
 				 rsc->id, node->details->uname);
 			
 			 * stop healthy resources before the
 			 * stonith op
 			 *
 			custom_action_order(
 				rsc, stop_key(rsc), NULL,
 				NULL,crm_strdup(CRM_OP_FENCE),stonith_op,
 				pe_order_optional, data_set);
 */
 	    );
 	
 	g_list_free(action_list);
 
 	key = demote_key(rsc);
 	action_list = find_actions(rsc->actions, key, node);
 	crm_free(key);
 	
 	slist_iter(
 		action, action_t, action_list, lpc2,
 		if(node->details->online == FALSE || is_set(rsc->flags, pe_rsc_failed)) {
 			crm_info("Demote of failed resource %s is"
 				 " implict after %s is fenced",
 				 rsc->id, node->details->uname);
 			/* the stop would never complete and is
 			 * now implied by the stonith operation
 			 */
 			action->pseudo = TRUE;
 			action->runnable = TRUE;
 			if(is_stonith == FALSE) {
 			    order_actions(stonith_op, action, pe_order_optional);
 			}
 		}
 		);	
 	
 	g_list_free(action_list);
 }
 
 void
 complex_stonith_ordering(
 	resource_t *rsc,  action_t *stonith_op, pe_working_set_t *data_set)
 {
 	gboolean is_stonith = FALSE;
 	const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 
 	if(rsc->children) {
 	    slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 
 		child_rsc->cmds->stonith_ordering(
 		    child_rsc, stonith_op, data_set);
 		);
 	    return;
 	}
 	
 	if(is_not_set(rsc->flags, pe_rsc_managed)) {
 		crm_debug_3("Skipping fencing constraints for unmanaged resource: %s", rsc->id);
 		return;
 	} 
 
 	if(stonith_op != NULL && safe_str_eq(class, "stonith")) {
 		is_stonith = TRUE;
 	}
 	
 	/* Start constraints */
 	native_start_constraints(rsc,  stonith_op, is_stonith, data_set);
  
 	/* Stop constraints */
 	native_stop_constraints(rsc,  stonith_op, is_stonith, data_set);
 }
 
 #define ALLOW_WEAK_MIGRATION 0
 
 static gboolean
 check_stack_element(resource_t *rsc, resource_t *other_rsc, const char *type) 
 {
     char *key = NULL;
     int level = LOG_DEBUG;
     GListPtr action_list = NULL;
 
     if(other_rsc == NULL || other_rsc == rsc) {
 	return TRUE;
     }
 
     do_crm_log(level+1, "%s: processing %s (%s)", rsc->id, other_rsc->id, type);
     
     if(other_rsc->variant == pe_native) {
 	do_crm_log(level, "%s: depends on %s (mid-stack) %s",
 		   rsc->id, other_rsc->id, type);
 	return FALSE;
 	
     } else if(other_rsc->variant == pe_group) {
 	if(at_stack_bottom(other_rsc) == FALSE) {
 	    do_crm_log(level, "%s: depends on group %s (mid-stack) %s",
 		       rsc->id, other_rsc->id, type);
 	    return FALSE;
 	}
 	return TRUE;
     }
     
     /* is the clone also moving moved around?
      *
      * if so, then we can't yet be completely sure the
      *   resource can safely migrate since the node we're
      *   moving too may not have the clone instance started
      *   yet
      *
      * in theory we can figure out if the clone instance we
      *   will run on is already there, but there that would
      *   involve too much knowledge of internal clone code.
      *   maybe later...
      */
     
     do_crm_log(level+1,"%s: start depends on clone %s",
 	       rsc->id, other_rsc->id);
     key = stop_key(other_rsc);
     action_list = find_actions(other_rsc->actions, key, NULL);
     crm_free(key);
     
     slist_iter(
 	other_stop, action_t, action_list,lpc,
 	if(other_stop && other_stop->optional == FALSE) {
 	    do_crm_log(level, "%s: start depends on %s",
 		       rsc->id, other_stop->uuid);
 	    
 	    g_list_free(action_list);
 	    return FALSE;
 	}
 	);
     g_list_free(action_list);
     return TRUE;
 }
 
 gboolean
 at_stack_bottom(resource_t *rsc) 
 {
     char *key = NULL;
     action_t *start = NULL;
     action_t *other = NULL;
     GListPtr action_list = NULL;
     
     key = start_key(rsc);
     action_list = find_actions(rsc->actions, key, NULL);
     crm_free(key);
     
     crm_debug_3("%s: processing", rsc->id);
     CRM_CHECK(action_list != NULL, return FALSE);
     
     start = action_list->data;
     g_list_free(action_list);
 
     slist_iter(
 	constraint, rsc_colocation_t, rsc->rsc_cons, lpc,
 
 	resource_t *target = constraint->rsc_rh;
 	crm_debug_4("%s == %s (%d)", rsc->id, target->id, constraint->score);
 	if(constraint->score > 0
 	   && check_stack_element(rsc, target, "coloc") == FALSE) {
 	    return FALSE;
 	}
 	);
 
     slist_iter(
 	other_w, action_wrapper_t, start->actions_before, lpc,
 	other = other_w->action;
 
 #if ALLOW_WEAK_MIGRATION
 	if((other_w->type & pe_order_implies_right) == 0) {
 	    crm_debug_3("%s: depends on %s (optional ordering)",
 			rsc->id, other->uuid);
 	    continue;
 	}	
 #endif 
 
 	if(other->optional == FALSE
 	   && check_stack_element(rsc, other->rsc, "order") == FALSE) {
 	    return FALSE;
 	}
 	
 	);
 
     return TRUE;
 }
 
 void
 complex_migrate_reload(resource_t *rsc, pe_working_set_t *data_set)
 {
 	char *key = NULL;
 	int level = LOG_DEBUG;
 	GListPtr action_list = NULL;
 	
 	action_t *stop = NULL;
 	action_t *start = NULL;
 	action_t *other = NULL;
 	action_t *action = NULL;
 	const char *value = NULL;
 
 	if(rsc->children) {
 	    slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		
 		child_rsc->cmds->migrate_reload(child_rsc, data_set);
 		);
 	    other = NULL;
 	    return;
 	}
 
 	do_crm_log(level+1, "Processing %s", rsc->id);
 	CRM_CHECK(rsc->variant == pe_native, return);
 	
 	if(is_not_set(rsc->flags, pe_rsc_managed)
 	   || is_set(rsc->flags, pe_rsc_failed)
 	   || is_set(rsc->flags, pe_rsc_start_pending)
 	   || rsc->next_role != RSC_ROLE_STARTED
 	   || g_list_length(rsc->running_on) != 1) {
 		do_crm_log(level+1, "%s: general resource state", rsc->id);
 		return;
 	}
 	
 	key = start_key(rsc);
 	action_list = find_actions(rsc->actions, key, NULL);
 	crm_free(key);
 	
 	if(action_list == NULL) {
 		do_crm_log(level, "%s: no start action", rsc->id);
 		return;
 	}
 	
 	start = action_list->data;
 	g_list_free(action_list);
 
 	value = g_hash_table_lookup(rsc->meta, XML_OP_ATTR_ALLOW_MIGRATE);
 	if(crm_is_true(value)) {
 	    set_bit(rsc->flags, pe_rsc_can_migrate);	
 	}	
 
 	if(is_not_set(rsc->flags, pe_rsc_can_migrate)
 	   && start->allow_reload_conversion == FALSE) {
 		do_crm_log(level+1, "%s: no need to continue", rsc->id);
 		return;
 	}
 	
 	key = stop_key(rsc);
 	action_list = find_actions(rsc->actions, key, NULL);
 	crm_free(key);
 	
 	if(action_list == NULL) {
 		do_crm_log(level, "%s: no stop action", rsc->id);
 		return;
 	}
 	
 	stop = action_list->data;
 	g_list_free(action_list);
 	
 	action = start;
 	if(action->pseudo
 	   || action->optional
 	   || action->node == NULL
 	   || action->runnable == FALSE) {
 		do_crm_log(level, "%s: %s", rsc->id, action->task);
 		return;
 	}
 	
 	action = stop;
 	if(action->pseudo
 	   || action->optional
 	   || action->node == NULL
 	   || action->runnable == FALSE) {
 		do_crm_log(level, "%s: %s", rsc->id, action->task);
 		return;
 	}
 	
 	if(is_set(rsc->flags, pe_rsc_can_migrate)) {
 	    if(start->node == NULL
 	       || stop->node == NULL
 	       || stop->node->details == start->node->details) {
 		clear_bit(rsc->flags, pe_rsc_can_migrate);
 
 	    } else if(at_stack_bottom(rsc) == FALSE) {
 		crm_notice("Cannot migrate %s from %s to %s"
 			   " - %s is not at the bottom of the resource stack",
 			   rsc->id, stop->node->details->uname,
 			   start->node->details->uname, rsc->id);
 		clear_bit(rsc->flags, pe_rsc_can_migrate);
 	    }
 	}
 
 	if(is_set(rsc->flags, pe_rsc_can_migrate)) {
 		crm_notice("Migrating %s from %s to %s", rsc->id,
 			 stop->node->details->uname,
 			 start->node->details->uname);
 		
 		crm_free(stop->uuid);
 		crm_free(stop->task);
 		stop->task = crm_strdup(RSC_MIGRATE);
 		stop->uuid = generate_op_key(rsc->id, stop->task, 0);
 		add_hash_param(stop->meta, "migrate_source",
 			       stop->node->details->uname);
 		add_hash_param(stop->meta, "migrate_target",
 			       start->node->details->uname);
 
 		/* Hook up to the all_stopped and shutdown actions */
 		slist_iter(
 			other_w, action_wrapper_t, stop->actions_after, lpc,
 			other = other_w->action;
 			if(other->optional == FALSE
 			   && other->rsc == NULL) {
 				order_actions(start, other, other_w->type);
 			}
 			);
 
 		slist_iter(
 			other_w, action_wrapper_t, start->actions_before, lpc,
 			other = other_w->action;
 			if(other->optional == FALSE
 #if ALLOW_WEAK_MIGRATION
 			   && (other_w->type & pe_order_implies_right)
 #endif
 			   && other->rsc != NULL
 			   && other->rsc != rsc->parent
 			   && other->rsc != rsc) {
 				do_crm_log(level, "Ordering %s before %s",
 					   other->uuid, stop->uuid);
 			    
 				order_actions(other, stop, other_w->type);
 			}
 			);
 
 		crm_free(start->uuid);
 		crm_free(start->task);
 		start->task = crm_strdup(RSC_MIGRATED);
 		start->uuid = generate_op_key(rsc->id, start->task, 0);
 		add_hash_param(start->meta, "migrate_source_uuid",
 			       stop->node->details->id);
 		add_hash_param(start->meta, "migrate_source",
 			       stop->node->details->uname);
 		add_hash_param(start->meta, "migrate_target",
 			       start->node->details->uname);
 		
 	} else if(start->allow_reload_conversion
 		  && stop->node->details == start->node->details) {
 		crm_info("Rewriting restart of %s on %s as a reload",
 			 rsc->id, start->node->details->uname);
 		crm_free(start->uuid);
 		crm_free(start->task);
 		start->task = crm_strdup("reload");
 		start->uuid = generate_op_key(rsc->id, start->task, 0);
 		
 		stop->pseudo = TRUE; /* easier than trying to delete it from the graph */
 		
 	} else {
 		do_crm_log(level+1, "%s nothing to do", rsc->id);
 	}
 }
diff --git a/xml/resources.rng.in b/xml/resources.rng.in
index c5c1649b1e..66438356ba 100644
--- a/xml/resources.rng.in
+++ b/xml/resources.rng.in
@@ -1,162 +1,165 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0" 
          datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
   <start>
       <ref name="element-resources"/>
   </start>
 
   <define name="element-resources">
       <zeroOrMore>
 	<choice>
 	  <ref name="element-primitive"/>
 	  <ref name="element-group"/>
 	  <ref name="element-clone"/>
 	  <ref name="element-master"/>
 	</choice>
       </zeroOrMore>
   </define>
 
   <define name="element-primitive">
     <element name="primitive">
       <interleave>
 	<attribute name="id"><data type="ID"/></attribute>
 	<choice>
 	  <group>
 	    <attribute name="class"><value>ocf</value></attribute>
 	    <attribute name="provider"><text/></attribute>
 	  </group>
 	  <attribute name="class">
 	    <choice>
 	      <value>lsb</value>
 	      <value>heartbeat</value>
 	      <value>stonith</value>
 	    </choice>
 	  </attribute>
 	</choice>
 	<attribute name="type"><text/></attribute>
 	<optional>
 	  <attribute name="description"><text/></attribute>
 	</optional>
 	<ref name="element-resource-extra"/>
 	<ref name="element-operations"/>
       </interleave>
     </element>
   </define>
 
   <define name="element-group">
     <element name="group">
       <attribute name="id"><data type="ID"/></attribute>
       <optional>
 	<attribute name="description"><text/></attribute>
       </optional>
       <interleave>
 	<ref name="element-resource-extra"/>
 	<oneOrMore>
 	  <ref name="element-primitive"/>
 	</oneOrMore>
       </interleave>
     </element>
   </define>
  
   <define name="element-clone">
     <element name="clone">
       <attribute name="id"><data type="ID"/></attribute>
       <optional>
 	<attribute name="description"><text/></attribute>
       </optional>
       <interleave>
 	<ref name="element-resource-extra"/>
 	<choice>
 	  <ref name="element-primitive"/>
 	  <ref name="element-group"/>
 	</choice>
       </interleave>
     </element>
   </define>
 
   <define name="element-master">
     <element name="master">
       <attribute name="id"><data type="ID"/></attribute>
       <optional>
 	<attribute name="description"><text/></attribute>
       </optional>
       <interleave>
 	<ref name="element-resource-extra"/>
 	<choice>
 	  <ref name="element-primitive"/>
 	  <ref name="element-group"/>
 	</choice>
       </interleave>
     </element>
   </define>
 
   <define name="element-resource-extra">
       <zeroOrMore>
 	<choice>
 	  <element name="meta_attributes">
 	    <externalRef href="nvset-@DTD_VERSION@.rng"/>
 	  </element>
 	  <element name="instance_attributes">
 	    <externalRef href="nvset-@DTD_VERSION@.rng"/>
 	  </element>
 	</choice>
       </zeroOrMore>
   </define>
  
   <define name="element-operations">
     <optional>
       <element name="operations">
 	<zeroOrMore>
 	  <element name="op">
 	    <attribute name="id"><data type="ID"/></attribute>
 	    <attribute name="name"><text/></attribute>
 	    <attribute name="interval"><text/></attribute>
 	    <optional>
 	      <attribute name="description"><text/></attribute>
 	    </optional>
 	    <optional>
-	      <attribute name="start-delay"><text/></attribute>
+	      <choice>
+		<attribute name="start-delay"><text/></attribute>
+		<attribute name="interval-origin"><text/></attribute>
+	      </choice>
 	    </optional>
 	    <optional>
 	      <attribute name="timeout"><text/></attribute>
 	    </optional>
 	    <optional>
 	      <attribute name="enabled"><data type="boolean"/></attribute>
 	    </optional>
 	    <optional>
 	      <attribute name="role">
 		<choice>
 		  <value>Started</value>
 		  <value>Slave</value>
 		  <value>Master</value>
 		</choice>
 	      </attribute>
 	    </optional>
 	    <optional>
 	      <attribute name="requires">
 		<choice>
 		  <value>nothing</value>
 		  <value>quorum</value>
 		  <value>fencing</value>
 		</choice>
 	      </attribute>
 	    </optional>
 	    <optional>
 	      <attribute name="on-fail">
 		<choice>
 		  <value>ignore</value>
 		  <value>block</value>
 		  <value>stop</value>
 		  <value>restart</value>
 		  <value>fence</value>
 		</choice>
 	      </attribute>
 	    </optional>
 	    <ref name="element-resource-extra"/>
 	  </element>
 	</zeroOrMore>
       </element>
     </optional>
   </define>
 
 </grammar>