[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Cluster-devel] Cluster Project branch, RHEL4, updated. gfs-kernel_2_6_9_76-12-gdb6a74a



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Cluster Project".

http://sources.redhat.com/git/gitweb.cgi?p=cluster.git;a=commitdiff;h=db6a74a9900257848aa4c07256b77b2d10a7db13

The branch, RHEL4 has been updated
       via  db6a74a9900257848aa4c07256b77b2d10a7db13 (commit)
       via  5aeaf6345cd4672e0ef52a753b4f6cf56a64d5b5 (commit)
       via  43571d7af75bdb6a0b19d40048da1491b6499efb (commit)
       via  df101a4507641607e5869f2f15c05cb1be6e0639 (commit)
      from  1befab3b0e83584f08b06b816adbc0f8b9dc11ab (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit db6a74a9900257848aa4c07256b77b2d10a7db13
Author: Lon Hohberger <lhh redhat com>
Date:   Tue Mar 4 17:31:32 2008 -0500

    [CMAN] make qdisk master abdication work in all cases
    
    Red Hat bugzilla 430264

commit 5aeaf6345cd4672e0ef52a753b4f6cf56a64d5b5
Author: Lon Hohberger <lhh redhat com>
Date:   Tue Mar 11 14:54:49 2008 -0400

    [rgmanager] Make ip.sh check link states of non-ethernet devices
    
    Resolves: Red Hat Bugzilla #331661

commit 43571d7af75bdb6a0b19d40048da1491b6499efb
Author: Lon Hohberger <lhh redhat com>
Date:   Mon Mar 10 09:58:08 2008 -0400

    Merge, part 2

commit df101a4507641607e5869f2f15c05cb1be6e0639
Author: Lon Hohberger <lhh redhat com>
Date:   Mon Mar 10 09:56:40 2008 -0400

    Commit phase 1 of update from rhel5 branch

-----------------------------------------------------------------------

Summary of changes:
 cman/qdisk/main.c                                  |    3 +-
 rgmanager/event-script.txt                         |  311 +++++
 rgmanager/include/clulog.h                         |    2 +-
 rgmanager/include/event.h                          |  145 +++
 rgmanager/include/members.h                        |   35 +
 rgmanager/include/reslist.h                        |   65 +-
 .../include/{rg_locks.h => restart_counter.h}      |   38 +-
 rgmanager/include/rg_queue.h                       |   26 +-
 rgmanager/include/{rg_locks.h => sets.h}           |   51 +-
 rgmanager/man/clusvcadm.8                          |   13 +-
 rgmanager/rgmanager/event-script.txt               |  311 +++++
 rgmanager/src/clulib/rg_strings.c                  |  208 +++-
 rgmanager/src/clulib/sets.c                        |  370 ++++++
 rgmanager/src/clulib/signals.c                     |   18 +
 rgmanager/src/clulib/tmgr.c                        |  128 ++
 rgmanager/src/daemons/event_config.c               |  541 +++++++++
 rgmanager/src/daemons/fo_domain.c                  |  113 ++-
 rgmanager/src/daemons/groups.c                     |  366 +++++-
 rgmanager/src/daemons/main.c                       |    2 +-
 rgmanager/src/daemons/reslist.c                    |   92 ++-
 rgmanager/src/daemons/resrules.c                   |  101 ++-
 rgmanager/src/daemons/restart_counter.c            |  205 ++++
 rgmanager/src/daemons/restree.c                    |  532 ++++-----
 rgmanager/src/daemons/rg_event.c                   |  573 +++++++++
 rgmanager/src/daemons/service_op.c                 |  204 ++++
 rgmanager/src/daemons/slang_event.c                | 1264 ++++++++++++++++++++
 rgmanager/src/daemons/test.c                       |  116 ++-
 rgmanager/src/resources/Makefile                   |    9 +-
 rgmanager/src/resources/clusterfs.sh               |   59 +-
 rgmanager/src/resources/default_event_script.sl    |  314 +++++
 rgmanager/src/resources/ip.sh                      |   18 +-
 rgmanager/src/resources/ocf-shellfuncs             |    4 +
 rgmanager/src/resources/script.sh                  |    2 +-
 rgmanager/src/resources/service.sh                 |  104 ++-
 rgmanager/src/resources/svclib_nfslock             |   28 +
 .../src/resources/utils/named-parse-config.pl      |   26 +
 rgmanager/src/resources/utils/ra-skelet.sh         |    2 +-
 37 files changed, 5822 insertions(+), 577 deletions(-)
 create mode 100644 rgmanager/event-script.txt
 create mode 100644 rgmanager/include/event.h
 create mode 100644 rgmanager/include/members.h
 copy rgmanager/include/{rg_locks.h => restart_counter.h} (53%)
 copy rgmanager/include/{rg_locks.h => sets.h} (53%)
 create mode 100644 rgmanager/rgmanager/event-script.txt
 create mode 100644 rgmanager/src/clulib/sets.c
 create mode 100644 rgmanager/src/clulib/tmgr.c
 create mode 100644 rgmanager/src/daemons/event_config.c
 create mode 100644 rgmanager/src/daemons/restart_counter.c
 create mode 100644 rgmanager/src/daemons/rg_event.c
 create mode 100644 rgmanager/src/daemons/service_op.c
 create mode 100644 rgmanager/src/daemons/slang_event.c
 create mode 100644 rgmanager/src/resources/default_event_script.sl
 create mode 100644 rgmanager/src/resources/utils/named-parse-config.pl

diff --git a/cman/qdisk/main.c b/cman/qdisk/main.c
index 9eb2bbd..8e63c88 100644
--- a/cman/qdisk/main.c
+++ b/cman/qdisk/main.c
@@ -935,7 +935,8 @@ quorum_loop(qd_ctx *ctx, node_info_t *ni, int max)
 		ctx->qc_master = master_exists(ctx, ni, max, &low_id, &count);
 
 		/* Resolve master conflict, if one exists */
-		if (count > 1 && ctx->qc_status == S_MASTER) {
+		if (count >= 1 && ctx->qc_status == S_MASTER &&
+		    ctx->qc_master != ctx->qc_my_id) {
 			clulog(LOG_WARNING, "Master conflict: abdicating\n");
 
 			/* Handle just like a recent upgrade */
diff --git a/rgmanager/event-script.txt b/rgmanager/event-script.txt
new file mode 100644
index 0000000..00a8b4c
--- /dev/null
+++ b/rgmanager/event-script.txt
@@ -0,0 +1,311 @@
+TODO:
+* Return correct error codes to clusvcadm (currently it always returns
+  "Unknown")
+* Write glue for 'migrate' operations and migrate-enabled services
+
+Basic configuration specification:
+
+  <rm>
+    <events>
+      <event class="node"/>        <!-- all node events -->
+      <event class="node"
+             node="bar"/>     <!-- events concerning 'bar' -->
+      <event class="node"
+             node="foo"
+             node_state="up"/>     <!-- 'up' events for 'foo' -->
+      <event class="node"
+             node_id="3"
+             node_state="down"/>   <!-- 'down' events for node ID 3 -->
+
+          (note, all service ops and such deal with node ID, not
+           with node names)
+
+      <event class="service"/>     <!-- all service events-->
+      <event class="service"
+             service_name="A"/>    <!-- events concerning 'A' -->
+      <event class="service"
+             service_name="B"
+	     service_state="started"/> <!-- when 'B' is started... -->
+      <event class="service"
+             service_name="B"
+	     service_state="started"/>
+	     service_owner="3"/> <!-- when 'B' is started on node 3... -->
+
+      <event class="service"
+             priority="1"
+	     service_state="started"/>
+	     service_owner="3"/> <!-- when 'B' is started on node 3, do this
+				      before the other event handlers ... -->
+
+
+    </events>
+    ...
+  </rm>
+
+General globals available from all scripts:
+
+   node_self - local node ID
+   event_type - event class, either:
+       EVENT_NONE - unspecified / unknown
+       EVENT_NODE - node transition
+       EVENT_SERVICE - service transition
+       EVENT_USER - a user-generated request
+       EVENT_CONFIG - [NOT CONFIGURABLE]
+
+Node event globals (i.e. when event_type == EVENT_NODE):
+  
+   node_id - node ID which is transitioning
+   node_name - name of node which is transitioning
+   node_state - new node state (NODE_ONLINE or NODE_OFFLINE, or if you prefer,
+                1 or 0, respectively)
+   node_clean - 0 if the node has not been fenced, 1 if the node has been
+                fenced
+
+Service event globals (i.e. when event_type == EVENT_SERVICE):
+
+   service_name - Name of service which transitioned
+   service_state - new state of service
+   service_owner - new owner of service (or <0 if service is no longer
+		   running)
+   service_last_owner - Last owner of service if known.  Used for when
+                   service_state = "recovering" generally, in order to
+                   apply restart/relocate/disable policy.
+
+User event globals (i.e. when event_type == EVENT_USER):
+
+   service_name - service to perform request upon
+   user_request - request to perform (USER_ENABLE, USER_DISABLE,
+                   USER_STOP, USER_RELOCATE, [TODO] USER_MIGRATE)
+   user_target - target node ID if applicable
+
+
+Scripting functions - Informational:
+
+  node_list = nodes_online();
+
+	Returns a list of all online nodes.
+
+  service_list = service_list();
+
+	Returns a list of all configured services.
+
+  (restarts, last_owner, owner, state) = service_status(service_name);
+
+	Returns the state, owner, last_owner, and restarts.  Note that
+	all return values are optional, but are right-justified per S-Lang
+	specification.  This means if you only want the 'state', you can use:
+	
+	(state) = service_status(service_name);
+
+	However, if you need the restart count, you must provide all four 
+	return values as above.
+
+  (nofailback, restricted, ordered, node_list) =
+		service_domain_info(service_name);
+
+	Returns the failover domain specification, if it exists, for the
+	specified service name.  The node list returned is an ordered list
+	according to priority levels.  In the case of unordered domains, 
+	the ordering of the returned list is pseudo-random.
+
+Scripting functions - Operational:
+
+  err = service_start(service_name, node_list, [avoid_list]);
+
+	Start a non-running, (but runnable, i.e. not failed)
+	service on the first node in node_list.  Failing that, start it on
+	the second node in node_list and so forth.  One may also specify
+	an avoid list, but it's better to just use the subtract() function
+	below.  If the start is successful, the node ID running the service
+	is returned.  If the start is unsuccessful, a value < 0 is returned.
+
+  err = service_stop(service_name, [0 = stop, 1 = disable]);
+
+	Stop a running service.  The second parameter is optional, and if
+	non-zero is specified, the service will enter the disabled state.
+
+  ... stuff that's not done but needs to be:
+
+  err = service_relocate(service_name, node_list);
+
+	Move a running service to the specified node_list in order of
+	preference.  In the case of VMs, this is actually a migrate-or-
+	relocate operation.
+
+Utility functions - Node list manipulation
+
+  node_list = union(left_node_list, right_node_list);
+
+	Calculates the union between the two node list, removing duplicates
+	and preserving ordering according to left_node_list.  Any added
+	values from right_node_list will appear in their order, but
+	after left_node_list in the returned list.
+
+  node_list = intersection(left_node_list, right_node_list);
+
+	Calculates the intersection (items in both lists) between the two
+	node lists, removing duplicates and preserving ordering according
+	to left_node_list.  Any added values from right_node_list will
+	appear in their order, but after left_node_list in the returned list.
+
+  node_list = delta(left_node_list, right_node_list);
+
+	Calculates the delta (items not in both lists) between the two
+	node lists, removing duplicates and preserving ordering according
+	to left_node_list.  Any added values from right_node_list will
+	appear in their order, but after left_node_list in the returned list.
+
+  node_list = subtract(left_node_list, right_node_list);
+
+	Removes any duplicates as well as items specified in right_node_list
+	from left_node_list.  Example:
+
+	all_nodes = nodes_online();
+	allowed_nodes = subtract(nodes_online, node_to_avoid);
+
+  node_list = shuffle(node_list_old);
+
+	Rearranges the contents of node_list_old randomly and returns a
+	new node list.
+
+Utility functions - Logging:
+
+  debug(item1, item2, ...);	LOG_DEBUG level
+  info(...);			LOG_INFO level
+  notice(...);			LOG_NOTICE level
+  warning(...);			LOG_WARNING level
+  err(...);			LOG_ERR level
+  crit(...);			LOG_CRIT level
+  alert(...);			LOG_ALERT level
+  emerg(...);			LOG_EMERG level
+
+	items - These can be strings, integer lists, or integers.  Logging
+		string lists is not supported.
+
+	level - the level is consistent with syslog(8)
+
+  stop_processing();
+
+	Calling this function will prevent further event scripts from being
+	executed on a particular event.  Call this script if, for example,
+	you do not wish for the default event handler to process the event.
+
+	Note: This does NOT terminate the caller script; that is, the
+	script being executed will run to completion.
+
+Event scripts are written in a language called S-Lang; documentation specifics
+about the language are available at http://www.s-lang.org
+
+Example script (creating a follows-but-avoid-after-start behavior):
+%
+% If the main queue server and replication queue server are on the same
+% node, relocate the replication server somewhere else if possible.
+%
+define my_sap_event_trigger()
+{
+	variable state, owner_rep, owner_main;
+	variable nodes, allowed;
+
+	%
+	% If this was a service event, don't execute the default event
+	% script trigger after this script completes.
+	%
+	if (event_type == EVENT_SERVICE) {
+		stop_processing();
+	}
+
+	(owner_main, state) = service_status("service:main_queue";);
+	(owner_rep, state) = service_status("service:replication_server";);
+
+	if ((event_type == EVENT_NODE) and (owner_main == node_id) and
+	    (node_state == NODE_OFFLINE) and (owner_rep >= 0)) {
+		%
+		% uh oh, the owner of the main server died.  Restart it
+		% on the node running the replication server
+		%
+		notice("Starting Main Queue Server on node ", owner_rep);
+		()=service_start("service:main_queue";, owner_rep);
+		return;
+	}
+
+	%
+	% S-Lang doesn't short-circuit prior to 2.1.0
+	%
+	if ((owner_main >= 0) and
+	    ((owner_main == owner_rep) or (owner_rep < 0))) {
+
+		%
+		% Get all online nodes
+		%
+		nodes = nodes_online();
+
+		%
+		% Drop out the owner of the main server
+		%
+		allowed = subtract(nodes, owner_main);
+		if ((owner_rep >= 0) and (length(allowed) == 0)) {
+			%
+			% Only one node is online and the rep server is
+			% already running.  Don't do anything else.
+			%
+			return;
+		}
+
+		if ((length(allowed) == 0) and (owner_rep < 0)) {
+			%
+			% Only node online is the owner ... go ahead
+			% and start it, even though it doesn't increase
+			% availability to do so.
+			%
+			allowed = owner_main;
+		}
+
+		%
+		% Move the replication server off the node that is
+		% running the main server if a node's available.
+		%
+		if (owner_rep >= 0) {
+			()=service_stop("service:replication_server";);
+		}
+		()=service_start("service:replication_server";, allowed);
+	}
+
+	return;
+}
+
+my_sap_event_trigger();
+
+
+Relevant <rm> section from cluster.conf:
+
+        <rm central_processing="1">
+                <events>
+                        <event name="main-start" class="service"
+				service="service:main_queue";
+				service_state="started"
+				file="/tmp/sap.sl"/>
+                        <event name="rep-start" class="service"
+				service="service:replication_server";
+				service_state="started"
+				file="/tmp/sap.sl"/>
+                        <event name="node-up" node_state="up"
+				class="node"
+				file="/tmp/sap.sl"/>
+
+                </events>
+                <failoverdomains>
+                        <failoverdomain name="all" ordered="1" restricted="1">
+                                <failoverdomainnode name="molly"
+priority="2"/>
+                                <failoverdomainnode name="frederick"
+priority="1"/>
+                        </failoverdomain>
+                </failoverdomains>
+                <resources/>
+                <service name="main_queue"/>
+                <service name="replication_server" autostart="0"/>
+		<!-- replication server is started when main-server start
+		     event completes -->
+        </rm>
+
+
diff --git a/rgmanager/include/clulog.h b/rgmanager/include/clulog.h
index 856d83c..e6b7ff8 100644
--- a/rgmanager/include/clulog.h
+++ b/rgmanager/include/clulog.h
@@ -38,7 +38,7 @@ extern "C" {
 #include <syslog.h>
 #include <sys/types.h>
 
-#define LOGLEVEL_DFLT         LOG_INFO
+#define LOGLEVEL_DFLT         LOG_NOTICE
 #define MAX_LOGMSG_LEN        512
 
 /*
diff --git a/rgmanager/include/event.h b/rgmanager/include/event.h
new file mode 100644
index 0000000..58edcd5
--- /dev/null
+++ b/rgmanager/include/event.h
@@ -0,0 +1,145 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+#ifndef _EVENT_H
+#define _EVENT_H
+
+/* 128 is a bit big, but it should be okay */
+typedef struct __rge_q {
+	char rg_name[128];
+	uint32_t rg_state;
+	uint32_t pad1;
+	int rg_owner;
+	int rg_last_owner;
+} group_event_t;
+
+typedef struct __ne_q {
+	int ne_local;
+	int ne_nodeid;
+	int ne_state;
+	int ne_clean;
+} node_event_t;
+
+typedef struct __cfg_q {
+	int cfg_version;
+	int cfg_oldversion;
+} config_event_t;
+
+typedef struct __user_q {
+	char u_name[128];
+	msgctx_t *u_ctx;
+	int u_request;
+	int u_arg1;
+	int u_arg2;
+	int u_target;		/* Node ID */
+} user_event_t;
+
+typedef enum {
+	EVENT_NONE=0,
+	EVENT_CONFIG,
+	EVENT_NODE,
+	EVENT_RG,
+	EVENT_USER
+} event_type_t;
+
+/* Data that's distributed which indicates which
+   node is the event master */
+typedef struct __rgm {
+	uint32_t m_magic;
+	uint32_t m_nodeid;
+	uint64_t m_master_time;
+	uint8_t  m_reserved[112];
+} event_master_t;
+
+#define swab_event_master_t(ptr) \
+{\
+	swab32((ptr)->m_nodeid);\
+	swab32((ptr)->m_magic);\
+	swab64((ptr)->m_master_time);\
+}
+
+/* Just a magic # to help us ensure we've got good
+   date from VF */
+#define EVENT_MASTER_MAGIC 0xfabab0de
+
+/* Event structure - internal to the event subsystem; use
+   the queueing functions below which allocate this struct
+   and pass it to the event handler */
+typedef struct _event {
+	/* Not used dynamically - part of config info */
+	list_head();
+	char *ev_name;
+	char *ev_script;
+	char *ev_script_file;
+	int ev_prio; 
+	int ev_pad;
+	/* --- end config part */
+	int ev_type;		/* config & generated by rgmanager*/
+	int ev_transaction;
+	union {
+		group_event_t group;
+		node_event_t node;
+		config_event_t config;
+		user_event_t user;
+	} ev;
+} event_t;
+
+#define EVENT_PRIO_COUNT 100
+
+typedef struct _event_table {
+	int max_prio;
+	int pad;
+	event_t *entries[0];
+} event_table_t;
+
+
+int construct_events(int ccsfd, event_table_t **);
+void deconstruct_events(event_table_t **);
+void print_events(event_table_t *);
+
+/* Does the event match a configured event? */
+int event_match(event_t *pattern, event_t *actual);
+
+/* Event queueing functions. */
+void node_event_q(int local, int nodeID, int state, int clean);
+void rg_event_q(char *name, uint32_t state, int owner, int last);
+void user_event_q(char *svc, int request, int arg1, int arg2,
+		  int target, msgctx_t *ctx);
+void config_event_q(int old_version, int new_version);
+
+/* Call this to see if there's a master. */
+int event_master_info_cached(event_master_t *);
+
+/* Call this to get the node ID of the current 
+   master *or* become the master if none exists */
+int event_master(void);
+
+/* Setup */
+int central_events_enabled(void);
+void set_central_events(int flag);
+int slang_process_event(event_table_t *event_table, event_t *ev);
+
+/* For distributed events. */
+void set_transition_throttling(int nsecs);
+
+/* Simplified service start. */
+int service_op_start(char *svcName, int *target_list, int target_list_len,
+		     int *new_owner);
+int service_op_stop(char *svcName, int do_disable, int event_type);
+
+
+#endif
diff --git a/rgmanager/include/members.h b/rgmanager/include/members.h
new file mode 100644
index 0000000..08feee0
--- /dev/null
+++ b/rgmanager/include/members.h
@@ -0,0 +1,35 @@
+#ifndef _MEMBERS_H
+#define _MEMBERS_H
+
+#include <rg_types.h>
+
+typedef enum {
+	NODE_STATE_DOWN = 0,
+	NODE_STATE_UP = 1,
+	NODE_STATE_CLEAN = 2
+} node_state_t;
+
+
+int get_my_nodeid(cman_handle_t h);
+int my_id(void);
+cluster_member_list_t * get_member_list(cman_handle_t h);
+void free_member_list(cluster_member_list_t *ml);
+void member_set_state(int nodeid, int state);
+int memb_count(cluster_member_list_t *ml);
+int member_online(int nodeid);
+int memb_online(cluster_member_list_t *ml, int nodeid);
+int memb_online_name(cluster_member_list_t *ml, char *name);
+int memb_name_to_id(cluster_member_list_t *ml, char *name);
+int memb_mark_down(cluster_member_list_t *ml, int nodeid);
+char * memb_id_to_name(cluster_member_list_t *ml, int nodeid);
+cman_node_t * memb_id_to_p(cluster_member_list_t *ml, int nodeid);
+cman_node_t * memb_name_to_p(cluster_member_list_t *ml, char *name);
+void free_member_list(cluster_member_list_t *ml);
+cluster_member_list_t *memb_gained(cluster_member_list_t *old,
+		 		   cluster_member_list_t *new);
+cluster_member_list_t *memb_lost(cluster_member_list_t *old,
+	 			 cluster_member_list_t *new);
+
+cluster_member_list_t *member_list_dup(cluster_member_list_t *old);
+
+#endif
diff --git a/rgmanager/include/reslist.h b/rgmanager/include/reslist.h
index 7b98f23..c4dce68 100644
--- a/rgmanager/include/reslist.h
+++ b/rgmanager/include/reslist.h
@@ -25,31 +25,33 @@
 #include <libxml/xpath.h>
 
 
+#define RA_PRIMARY	(1<<0)	/** Primary key */
+#define RA_UNIQUE	(1<<1)	/** Unique for given type */
+#define RA_REQUIRED	(1<<2)	/** Required (or an error if not present */
+#define RA_INHERIT	(1<<3)	/** Inherit a parent resource's attr */
+#define RA_RECONFIG	(1<<4)	/** Allow inline reconfiguration */
+
 #define RF_INLINE	(1<<0)
 #define RF_DEFINED	(1<<1)
 #define RF_NEEDSTART	(1<<2)	/** Used when adding/changing resources */
 #define RF_NEEDSTOP	(1<<3)  /** Used when deleting/changing resources */
 #define RF_COMMON	(1<<4)	/** " */
+#define RF_INDEPENDENT	(1<<5)  /** Define this for a resource if it is
+				  otherwise an independent subtree */
+#define RF_RECONFIG	(1<<6)
+
+#define RF_INIT		(1<<7)	/** Resource rule: Initialize this resource
+				  class on startup */
+#define RF_DESTROY	(1<<8)	/** Resource rule flag: Destroy this
+				  resource class if you delete it from
+				  the configuration */
+
+
 
 #define RES_STOPPED	(0)
 #define RES_STARTED	(1)
 #define RES_FAILED	(2)
 
-/*
-   Resource operations
- */
-#define RS_START	(0)
-#define RS_STOP		(1)
-#define RS_STATUS	(2)
-#define RS_RESINFO	(3)
-#define RS_RESTART	(4)
-#define RS_RELOAD	(5)
-#define RS_CONDRESTART  (6)
-#define	RS_RECOVER	(7)
-#define RS_CONDSTART	(8)	/** Start if flagged with RF_NEEDSTART */
-#define RS_CONDSTOP	(9)	/** STOP if flagged with RF_NEEDSTOP */
-
-
 #ifndef SHAREDIR
 #define SHAREDIR		"/usr/share/rgmanager"
 #endif
@@ -65,33 +67,20 @@
 #include <res-ocf.h>
 
 
-typedef enum {
-/*
-#define RA_PRIMARY	(1<<0)
-#define RA_UNIQUE	(1<<1)
-#define RA_REQUIRED	(1<<2)
-#define RA_INHERIT	(1<<3)
- */
-	RA_PRIMARY = (1<<0),
-	RA_UNIQUE  = (1<<1),
-	RA_REQUIRED= (1<<2),
-	RA_INHERIT = (1<<3),
-	RA_SPEC    = (1<<4)
-} ra_flag_t;
-
 typedef struct _resource_attribute {
 	char	*ra_name;
 	char	*ra_value;
-	ra_flag_t ra_flags;
+	int	ra_flags;
+	int	_pad_;
 } resource_attr_t;
 
 
 typedef struct _resource_child {
+	char	*rc_name;
 	int	rc_startlevel;
 	int	rc_stoplevel;
 	int	rc_forbid;
 	int	rc_flags;
-	char	*rc_name;
 } resource_child_t;
 
 
@@ -110,7 +99,7 @@ typedef struct _resource_rule {
 	char *	rr_type;
 	char *	rr_agent;
 	char *	rr_version;	/** agent XML spec version; OCF-ism */
-	int	rr_root;
+	int	rr_flags;
 	int	rr_maxrefs;
 	resource_attr_t *	rr_attrs;
 	resource_child_t *	rr_childtypes;
@@ -137,6 +126,7 @@ typedef struct _rg_node {
 	struct _rg_node	*rn_child, *rn_parent;
 	resource_t	*rn_resource;
 	resource_act_t	*rn_actions;
+	restart_counter_t rn_restart_counter;
 	int	rn_state; /* State of this instance of rn_resource */
 	int	rn_flags;
 	int	rn_last_status;
@@ -149,7 +139,7 @@ typedef struct _fod_node {
 	list_head();
 	char	*fdn_name;
 	int	fdn_prio;
-	int	_pad_; /* align */
+	uint64_t fdn_nodeid; /* on rhel4 this will be 64-bit int */
 } fod_node_t;
 
 typedef struct _fod {
@@ -169,7 +159,11 @@ int res_stop(resource_node_t **tree, resource_t *res, void *ret);
 int res_status(resource_node_t **tree, resource_t *res, void *ret);
 int res_condstart(resource_node_t **tree, resource_t *res, void *ret);
 int res_condstop(resource_node_t **tree, resource_t *res, void *ret);
+int res_exec(resource_node_t *node, int op, const char *arg, int depth);
 /*int res_resinfo(resource_node_t **tree, resource_t *res, void *ret);*/
+int expand_time(char *val);
+int store_action(resource_act_t **actsp, char *name, int depth, int timeout, int interval);
+
 
 /*
    Calculate differences
@@ -206,8 +200,10 @@ void destroy_resource_tree(resource_node_t **tree);
 int construct_domains(int ccsfd, fod_t **domains);
 void deconstruct_domains(fod_t **domains);
 void print_domains(fod_t **domains);
-int node_should_start(uint64_t nodeid, cluster_member_list_t *membership,
+int node_should_start(int nodeid, cluster_member_list_t *membership,
 		      char *rg_name, fod_t **domains);
+int node_domain_set(fod_t *domain, int **ret, int *retlen);
+int node_domain_set_safe(char *domainname, int **ret, int *retlen, int *flags);
 
 
 /*
@@ -216,6 +212,7 @@ int node_should_start(uint64_t nodeid, cluster_member_list_t *membership,
 resource_t *find_resource_by_ref(resource_t **reslist, char *type, char *ref);
 resource_t *find_root_by_ref(resource_t **reslist, char *ref);
 resource_rule_t *find_rule_by_type(resource_rule_t **rulelist, char *type);
+void res_build_name(char *, size_t, resource_t *);
 
 /*
    Internal functions; shouldn't be needed.
diff --git a/rgmanager/include/rg_locks.h b/rgmanager/include/restart_counter.h
similarity index 53%
copy from rgmanager/include/rg_locks.h
copy to rgmanager/include/restart_counter.h
index 78d6096..2f158ad 100644
--- a/rgmanager/include/rg_locks.h
+++ b/rgmanager/include/restart_counter.h
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2004-2007
+  Copyright Red Hat, Inc. 2007
 
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License version 2 as published
@@ -15,34 +15,18 @@
   Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
   MA 02139, USA.
 */
-#ifndef __RG_LOCKS_H
-#define __RG_LOCKS_H
+/* Time-based restart counters for rgmanager */
 
-int rg_running(void);
+#ifndef _RESTART_COUNTER_H
+#define _RESTART_COUNTER_H
 
-int rg_locked(void);
-int rg_lockall(int flag);
-int rg_unlockall(int flag);
+typedef void *restart_counter_t;
 
-int rg_quorate(void);
-int rg_set_quorate(void);
-int rg_set_inquorate(void);
-
-int rg_inc_threads(void);
-int rg_dec_threads(void);
-int rg_wait_threads(void);
-
-int rg_initialized(void);
-int rg_set_initialized(void);
-int rg_set_uninitialized(void);
-int rg_wait_initialized(void);
-
-int rg_inc_status(void);
-int rg_dec_status(void);
-int rg_set_statusmax(int max);
-
-int ccs_lock(void);
-int ccs_unlock(int fd);
+int restart_add(restart_counter_t arg);
+int restart_clear(restart_counter_t arg);
+int restart_count(restart_counter_t arg);
+int restart_treshold_exceeded(restart_counter_t arg);
+restart_counter_t restart_init(time_t expire_timeout, int max_restarts);
+int restart_cleanup(restart_counter_t arg);
 
 #endif
-
diff --git a/rgmanager/include/rg_queue.h b/rgmanager/include/rg_queue.h
index ac26ce8..87e9e40 100644
--- a/rgmanager/include/rg_queue.h
+++ b/rgmanager/include/rg_queue.h
@@ -1,9 +1,27 @@
+/*
+  Copyright Red Hat, Inc. 2004-2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
 #ifndef _RG_QUEUE_H
 #define _RG_QUEUE_H
 #include <list.h>
 #include <stdint.h>
 #include <sys/time.h>
 #include <unistd.h>
+#include <message.h>
 
 
 /** 
@@ -15,12 +33,12 @@ typedef struct _request {
 	uint32_t	rr_request;		/** Request */
 	uint32_t	rr_errorcode;		/** Error condition */
 	uint32_t	rr_orig_request;	/** Original request */
-	uint32_t	rr_resp_fd;		/** FD to send response */
 	uint64_t	rr_target;		/** Target node */
 	uint32_t	rr_arg0;		/** Integer argument */
 	uint32_t	rr_arg1;		/** Integer argument */
+	uint32_t	rr_arg2;		/** Integer argument */
 	uint32_t	rr_line;		/** Line no */
-	uint32_t	_pad_;			/** pad */
+	uint32_t	rr_resp_fd;		/** FD to send response */
 	char 		*rr_file;		/** Who made req */
 	time_t		rr_when;		/** time to execute */
 } request_t;
@@ -28,7 +46,7 @@ typedef struct _request {
 
 int _rq_queue_request(request_t **queue, char *name, uint32_t request,
     		     uint32_t err, uint32_t oldreq, uint32_t fd, time_t when,
-    		     uint64_t target, uint32_t arg0, uint32_t arg1, char *file,
+    		     uint36_t target, uint32_t arg0, uint32_t arg1, char *file,
 		     int line);
 
 #define rq_queue_request(queue, name, request, err, oldreq,\
@@ -41,5 +59,7 @@ int rq_queue_empty(request_t **q);
 void rq_free(request_t *foo);
 
 void forward_request(request_t *req);
+void forward_message(int fd, void *msg, int nodeid);
+
 
 #endif
diff --git a/rgmanager/include/rg_locks.h b/rgmanager/include/sets.h
similarity index 53%
copy from rgmanager/include/rg_locks.h
copy to rgmanager/include/sets.h
index 78d6096..8cc271b 100644
--- a/rgmanager/include/rg_locks.h
+++ b/rgmanager/include/sets.h
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2004-2007
+  Copyright Red Hat, Inc. 2007
 
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License version 2 as published
@@ -15,34 +15,25 @@
   Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
   MA 02139, USA.
 */
-#ifndef __RG_LOCKS_H
-#define __RG_LOCKS_H
-
-int rg_running(void);
-
-int rg_locked(void);
-int rg_lockall(int flag);
-int rg_unlockall(int flag);
-
-int rg_quorate(void);
-int rg_set_quorate(void);
-int rg_set_inquorate(void);
-
-int rg_inc_threads(void);
-int rg_dec_threads(void);
-int rg_wait_threads(void);
-
-int rg_initialized(void);
-int rg_set_initialized(void);
-int rg_set_uninitialized(void);
-int rg_wait_initialized(void);
-
-int rg_inc_status(void);
-int rg_dec_status(void);
-int rg_set_statusmax(int max);
-
-int ccs_lock(void);
-int ccs_unlock(int fd);
+/**
+ @file sets.h - Header file for sets.c
+ @author Lon Hohberger <lhh at redhat.com>
+ */
+#ifndef _SETS_H
+#define _SETS_H
+
+#include <stdint.h>
+typedef uint64_t set_type_t;
+
+int s_add(set_type_t *, int *, set_type_t);
+int s_union(set_type_t *, int, set_type_t *,
+	    int, set_type_t **, int *);
+
+int s_intersection(set_type_t *, int, set_type_t *,
+		   int, set_type_t **, int *);
+int s_delta(set_type_t *, int, set_type_t *,
+	    int, set_type_t **, int *);
+int s_subtract(set_type_t *, int, set_type_t *, int, set_type_t **, int *);
+int s_shuffle(set_type_t *, int);
 
 #endif
-
diff --git a/rgmanager/man/clusvcadm.8 b/rgmanager/man/clusvcadm.8
index dcc5691..20ae823 100644
--- a/rgmanager/man/clusvcadm.8
+++ b/rgmanager/man/clusvcadm.8
@@ -49,12 +49,8 @@ service
 Lock the local resource group manager.  This should only be used if the 
 administrator intends to perform a global, cluster-wide shutdown.  This
 prevents starting resource groups on the local node, allowing 
-services will not fail over during the shutdown of the cluster.  Generally,
-administrators should use the
-.B
-clushutdown(8)
-command to accomplish this.  Once the cluster quorum is dissolved, this
-state is reset.
+services will not fail over during the shutdown of the cluster.
+Once the cluster quorum is dissolved, this state is reset.
 .IP "\-m <member>"
 When used in conjunction with either the
 .B
@@ -88,11 +84,10 @@ service
 until a member transition or until it is enabled again.
 .IP \-u
 Unlock the cluster's service managers.  This allows services to transition
-again.  It will be necessary to re-enable all services in the stopped state
-if this is run after \fB clushutdown(8)\fR.
+again. 
 
 .IP \-v
 Display version information and exit.
 
 .SH "SEE ALSO"
-clustat(8), clushutdown(8)
+clustat(8)
diff --git a/rgmanager/rgmanager/event-script.txt b/rgmanager/rgmanager/event-script.txt
new file mode 100644
index 0000000..00a8b4c
--- /dev/null
+++ b/rgmanager/rgmanager/event-script.txt
@@ -0,0 +1,311 @@
+TODO:
+* Return correct error codes to clusvcadm (currently it always returns
+  "Unknown")
+* Write glue for 'migrate' operations and migrate-enabled services
+
+Basic configuration specification:
+
+  <rm>
+    <events>
+      <event class="node"/>        <!-- all node events -->
+      <event class="node"
+             node="bar"/>     <!-- events concerning 'bar' -->
+      <event class="node"
+             node="foo"
+             node_state="up"/>     <!-- 'up' events for 'foo' -->
+      <event class="node"
+             node_id="3"
+             node_state="down"/>   <!-- 'down' events for node ID 3 -->
+
+          (note, all service ops and such deal with node ID, not
+           with node names)
+
+      <event class="service"/>     <!-- all service events-->
+      <event class="service"
+             service_name="A"/>    <!-- events concerning 'A' -->
+      <event class="service"
+             service_name="B"
+	     service_state="started"/> <!-- when 'B' is started... -->
+      <event class="service"
+             service_name="B"
+	     service_state="started"/>
+	     service_owner="3"/> <!-- when 'B' is started on node 3... -->
+
+      <event class="service"
+             priority="1"
+	     service_state="started"/>
+	     service_owner="3"/> <!-- when 'B' is started on node 3, do this
+				      before the other event handlers ... -->
+
+
+    </events>
+    ...
+  </rm>
+
+General globals available from all scripts:
+
+   node_self - local node ID
+   event_type - event class, either:
+       EVENT_NONE - unspecified / unknown
+       EVENT_NODE - node transition
+       EVENT_SERVICE - service transition
+       EVENT_USER - a user-generated request
+       EVENT_CONFIG - [NOT CONFIGURABLE]
+
+Node event globals (i.e. when event_type == EVENT_NODE):
+  
+   node_id - node ID which is transitioning
+   node_name - name of node which is transitioning
+   node_state - new node state (NODE_ONLINE or NODE_OFFLINE, or if you prefer,
+                1 or 0, respectively)
+   node_clean - 0 if the node has not been fenced, 1 if the node has been
+                fenced
+
+Service event globals (i.e. when event_type == EVENT_SERVICE):
+
+   service_name - Name of service which transitioned
+   service_state - new state of service
+   service_owner - new owner of service (or <0 if service is no longer
+		   running)
+   service_last_owner - Last owner of service if known.  Used for when
+                   service_state = "recovering" generally, in order to
+                   apply restart/relocate/disable policy.
+
+User event globals (i.e. when event_type == EVENT_USER):
+
+   service_name - service to perform request upon
+   user_request - request to perform (USER_ENABLE, USER_DISABLE,
+                   USER_STOP, USER_RELOCATE, [TODO] USER_MIGRATE)
+   user_target - target node ID if applicable
+
+
+Scripting functions - Informational:
+
+  node_list = nodes_online();
+
+	Returns a list of all online nodes.
+
+  service_list = service_list();
+
+	Returns a list of all configured services.
+
+  (restarts, last_owner, owner, state) = service_status(service_name);
+
+	Returns the state, owner, last_owner, and restarts.  Note that
+	all return values are optional, but are right-justified per S-Lang
+	specification.  This means if you only want the 'state', you can use:
+	
+	(state) = service_status(service_name);
+
+	However, if you need the restart count, you must provide all four 
+	return values as above.
+
+  (nofailback, restricted, ordered, node_list) =
+		service_domain_info(service_name);
+
+	Returns the failover domain specification, if it exists, for the
+	specified service name.  The node list returned is an ordered list
+	according to priority levels.  In the case of unordered domains, 
+	the ordering of the returned list is pseudo-random.
+
+Scripting functions - Operational:
+
+  err = service_start(service_name, node_list, [avoid_list]);
+
+	Start a non-running, (but runnable, i.e. not failed)
+	service on the first node in node_list.  Failing that, start it on
+	the second node in node_list and so forth.  One may also specify
+	an avoid list, but it's better to just use the subtract() function
+	below.  If the start is successful, the node ID running the service
+	is returned.  If the start is unsuccessful, a value < 0 is returned.
+
+  err = service_stop(service_name, [0 = stop, 1 = disable]);
+
+	Stop a running service.  The second parameter is optional, and if
+	non-zero is specified, the service will enter the disabled state.
+
+  ... stuff that's not done but needs to be:
+
+  err = service_relocate(service_name, node_list);
+
+	Move a running service to the specified node_list in order of
+	preference.  In the case of VMs, this is actually a migrate-or-
+	relocate operation.
+
+Utility functions - Node list manipulation
+
+  node_list = union(left_node_list, right_node_list);
+
+	Calculates the union between the two node list, removing duplicates
+	and preserving ordering according to left_node_list.  Any added
+	values from right_node_list will appear in their order, but
+	after left_node_list in the returned list.
+
+  node_list = intersection(left_node_list, right_node_list);
+
+	Calculates the intersection (items in both lists) between the two
+	node lists, removing duplicates and preserving ordering according
+	to left_node_list.  Any added values from right_node_list will
+	appear in their order, but after left_node_list in the returned list.
+
+  node_list = delta(left_node_list, right_node_list);
+
+	Calculates the delta (items not in both lists) between the two
+	node lists, removing duplicates and preserving ordering according
+	to left_node_list.  Any added values from right_node_list will
+	appear in their order, but after left_node_list in the returned list.
+
+  node_list = subtract(left_node_list, right_node_list);
+
+	Removes any duplicates as well as items specified in right_node_list
+	from left_node_list.  Example:
+
+	all_nodes = nodes_online();
+	allowed_nodes = subtract(nodes_online, node_to_avoid);
+
+  node_list = shuffle(node_list_old);
+
+	Rearranges the contents of node_list_old randomly and returns a
+	new node list.
+
+Utility functions - Logging:
+
+  debug(item1, item2, ...);	LOG_DEBUG level
+  info(...);			LOG_INFO level
+  notice(...);			LOG_NOTICE level
+  warning(...);			LOG_WARNING level
+  err(...);			LOG_ERR level
+  crit(...);			LOG_CRIT level
+  alert(...);			LOG_ALERT level
+  emerg(...);			LOG_EMERG level
+
+	items - These can be strings, integer lists, or integers.  Logging
+		string lists is not supported.
+
+	level - the level is consistent with syslog(8)
+
+  stop_processing();
+
+	Calling this function will prevent further event scripts from being
+	executed on a particular event.  Call this script if, for example,
+	you do not wish for the default event handler to process the event.
+
+	Note: This does NOT terminate the caller script; that is, the
+	script being executed will run to completion.
+
+Event scripts are written in a language called S-Lang; documentation specifics
+about the language are available at http://www.s-lang.org
+
+Example script (creating a follows-but-avoid-after-start behavior):
+%
+% If the main queue server and replication queue server are on the same
+% node, relocate the replication server somewhere else if possible.
+%
+define my_sap_event_trigger()
+{
+	variable state, owner_rep, owner_main;
+	variable nodes, allowed;
+
+	%
+	% If this was a service event, don't execute the default event
+	% script trigger after this script completes.
+	%
+	if (event_type == EVENT_SERVICE) {
+		stop_processing();
+	}
+
+	(owner_main, state) = service_status("service:main_queue";);
+	(owner_rep, state) = service_status("service:replication_server";);
+
+	if ((event_type == EVENT_NODE) and (owner_main == node_id) and
+	    (node_state == NODE_OFFLINE) and (owner_rep >= 0)) {
+		%
+		% uh oh, the owner of the main server died.  Restart it
+		% on the node running the replication server
+		%
+		notice("Starting Main Queue Server on node ", owner_rep);
+		()=service_start("service:main_queue";, owner_rep);
+		return;
+	}
+
+	%
+	% S-Lang doesn't short-circuit prior to 2.1.0
+	%
+	if ((owner_main >= 0) and
+	    ((owner_main == owner_rep) or (owner_rep < 0))) {
+
+		%
+		% Get all online nodes
+		%
+		nodes = nodes_online();
+
+		%
+		% Drop out the owner of the main server
+		%
+		allowed = subtract(nodes, owner_main);
+		if ((owner_rep >= 0) and (length(allowed) == 0)) {
+			%
+			% Only one node is online and the rep server is
+			% already running.  Don't do anything else.
+			%
+			return;
+		}
+
+		if ((length(allowed) == 0) and (owner_rep < 0)) {
+			%
+			% Only node online is the owner ... go ahead
+			% and start it, even though it doesn't increase
+			% availability to do so.
+			%
+			allowed = owner_main;
+		}
+
+		%
+		% Move the replication server off the node that is
+		% running the main server if a node's available.
+		%
+		if (owner_rep >= 0) {
+			()=service_stop("service:replication_server";);
+		}
+		()=service_start("service:replication_server";, allowed);
+	}
+
+	return;
+}
+
+my_sap_event_trigger();
+
+
+Relevant <rm> section from cluster.conf:
+
+        <rm central_processing="1">
+                <events>
+                        <event name="main-start" class="service"
+				service="service:main_queue";
+				service_state="started"
+				file="/tmp/sap.sl"/>
+                        <event name="rep-start" class="service"
+				service="service:replication_server";
+				service_state="started"
+				file="/tmp/sap.sl"/>
+                        <event name="node-up" node_state="up"
+				class="node"
+				file="/tmp/sap.sl"/>
+
+                </events>
+                <failoverdomains>
+                        <failoverdomain name="all" ordered="1" restricted="1">
+                                <failoverdomainnode name="molly"
+priority="2"/>
+                                <failoverdomainnode name="frederick"
+priority="1"/>
+                        </failoverdomain>
+                </failoverdomains>
+                <resources/>
+                <service name="main_queue"/>
+                <service name="replication_server" autostart="0"/>
+		<!-- replication server is started when main-server start
+		     event completes -->
+        </rm>
+
+
diff --git a/rgmanager/src/clulib/rg_strings.c b/rgmanager/src/clulib/rg_strings.c
index 4728789..fb7598f 100644
--- a/rgmanager/src/clulib/rg_strings.c
+++ b/rgmanager/src/clulib/rg_strings.c
@@ -1,35 +1,179 @@
-const char *rg_state_strings[] = {
-	"stopped",
-	"starting",
-	"started",
-	"stopping",
-	"failed",
-	"uninitialized",
-	"checking",
-	"recoverable",
-	"recovering",
-	"disabled",
-	""
+/*
+  Copyright Red Hat, Inc. 2004-2006
+
+  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, or (at your option) any
+  later version.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+#include <res-ocf.h>
+#include <resgroup.h>
+
+struct string_val {
+	int val;
+	char *str;
+};
+
+
+const struct string_val rg_error_strings[] = {
+	{ RG_EEXCL,	"Service not runnable: cannot run exclusive" },
+	{ RG_EDOMAIN,   "Service not runnable: restricted failover domain offline" },
+	{ RG_ESCRIPT,   "S/Lang Script Error" },
+	{ RG_EFENCE,    "Fencing operation pending; try again later" },
+	{ RG_ENODE,     "Target node dead / nonexistent" },
+	{ RG_ERUN,      "Service is already running" },
+	{ RG_EQUORUM,	"Operation requires quorum" },
+	{ RG_EINVAL,	"Invalid operation for resource" },
+	{ RG_EDEPEND,	"Operation violates dependency rule" },
+	{ RG_EAGAIN,	"Temporary failure; try again" },
+	{ RG_EDEADLCK,	"Operation would cause a deadlock" },
+	{ RG_ENOSERVICE,"Service does not exist" },
+	{ RG_EFORWARD,	"Service not mastered locally" },
+	{ RG_EABORT,	"Aborted; service failed" },
+	{ RG_EFAIL,	"Failure" },
+	{ RG_ESUCCESS,	"Success" },
+	{ RG_YES,	"Yes" },
+	{ RG_NO, 	"No" },
+	{ 0,		NULL }
+};
+
+
+const struct string_val rg_req_strings[] = {
+	{RG_SUCCESS, "success" },
+	{RG_FAIL, "fail"},
+	{RG_START, "start"},
+	{RG_STOP, "stop"},
+	{RG_STATUS, "status"},
+	{RG_DISABLE, "disable"},
+	{RG_STOP_RECOVER, "stop (recovery)"},
+	{RG_START_RECOVER, "start (recovery)"},
+	{RG_RESTART, "restart"},
+	{RG_EXITING, "exiting"},
+	{RG_INIT, "initialize"},
+	{RG_ENABLE, "enable"},
+	{RG_STATUS_NODE, "status inquiry"},
+	{RG_RELOCATE, "relocate"},
+	{RG_CONDSTOP, "conditional stop"},
+	{RG_CONDSTART, "conditional start"},
+	{RG_START_REMOTE,"remote start"},
+	{RG_STOP_USER, "user stop"},
+	{RG_STOP_EXITING, "stop (shutdown)"},
+	{RG_LOCK, "locking"},
+	{RG_UNLOCK, "unlocking"},
+	{RG_QUERY_LOCK, "lock status inquiry"},
+	{RG_MIGRATE, "migrate"},
+	{RG_STATUS_INQUIRY, "out of band service status inquiry"},
+	{RG_NONE, "none"},
+	{0, NULL}
+};
+
+
+const struct string_val rg_state_strings[] = {
+	{RG_STATE_STOPPED, "stopped"},
+	{RG_STATE_STARTING, "starting"},
+	{RG_STATE_STARTED, "started"},
+	{RG_STATE_STOPPING, "stopping"},
+	{RG_STATE_FAILED, "failed"},
+	{RG_STATE_UNINITIALIZED, "uninitialized"},
+	{RG_STATE_CHECK, "checking"},
+	{RG_STATE_ERROR, "recoverable"},
+	{RG_STATE_RECOVER, "recovering"},
+	{RG_STATE_DISABLED, "disabled"},
+	{RG_STATE_MIGRATE, "migrating"},
+	{0, NULL}
 };
 
-const char *rg_req_strings[] = {
-	"success",
-	"fail",
-	"start",
-	"stop",
-	"status",
-	"disable",
-	"stop (recovery)",
-	"start (recovery)",
-	"restart",
-	"exiting",
-	"initialize",
-	"enable",
-	"status inquiry",
-	"relocate",
-	"conditional stop",
-	"conditional start",
-	"remote start",
-	"user stop",
-	""
+
+const struct string_val agent_ops[] = {
+	{RS_START, "start"},
+	{RS_STOP, "stop"},
+	{RS_STATUS, "status"},
+	{RS_RESINFO, "resinfo"},
+	{RS_RESTART, "restart"},
+	{RS_RELOAD, "reload"},
+	{RS_CONDRESTART, "condrestart"},		/* Unused */
+	{RS_RECOVER, "recover"},		
+	{RS_CONDSTART, "condstart"},
+	{RS_CONDSTOP, "condstop"},
+	{RS_MONITOR, "monitor"},
+	{RS_META_DATA, "meta-data"},		/* printenv */
+	{RS_VALIDATE, "validate-all"},
+	{RS_MIGRATE, "migrate"},
+	{RS_RECONFIG, "reconfig"},
+	{0 , NULL}
 };
+
+
+static inline const char *
+rg_search_table(const struct string_val *table, int val)
+{
+	int x;
+
+	for (x = 0; table[x].str != NULL; x++) {
+		if (table[x].val == val) {
+			return table[x].str;
+		}
+	}
+
+	return "Unknown";
+}
+
+
+static inline int
+rg_search_table_by_str(const struct string_val *table, const char *val)
+{
+	int x;
+
+	for (x = 0; table[x].str != NULL; x++) {
+		if (!strcasecmp(table[x].str, val))
+			return table[x].val;
+	}
+
+	return -1;
+}
+
+
+
+const char *
+rg_strerror(int val)
+{
+	return rg_search_table(rg_error_strings, val);
+}
+	
+const char *
+rg_state_str(int val)
+{
+	return rg_search_table(rg_state_strings, val);
+}
+
+
+int
+rg_state_str_to_id(const char *val)
+{
+	return rg_search_table_by_str(rg_state_strings, val);
+}
+
+
+
+const char *
+rg_req_str(int val)
+{
+	return rg_search_table(rg_req_strings, val);
+}
+
+
+const char *
+agent_op_str(int val)
+{
+	return rg_search_table(agent_ops, val);
+}
diff --git a/rgmanager/src/clulib/sets.c b/rgmanager/src/clulib/sets.c
new file mode 100644
index 0000000..f734064
--- /dev/null
+++ b/rgmanager/src/clulib/sets.c
@@ -0,0 +1,370 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/**
+ @file sets.c - Order-preserving set functions (union / intersection / delta)
+                (designed for integer types; a la int, uint64_t, etc...)
+ @author Lon Hohberger <lhh at redhat.com>
+ */
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sets.h>
+#include <sys/time.h>
+
+
+/**
+ Add a value to a set.  This function disregards an add if the value is already
+ in the set.  Note that the maximum length of set s must be preallocated; this
+ function doesn't do error or bounds checking. 
+
+ @param s		Set to modify
+ @param curlen		Current length (modified if added)
+ @param val		Value to add
+ @return		0 if not added, 1 if added
+ */
+int
+s_add(set_type_t *s, int *curlen, set_type_t val)
+{
+	int idx=0;
+
+	for (; idx < *curlen; idx++)
+		if (s[idx] == val)
+			return 0;
+	s[*curlen] = val;
+	++(*curlen);
+	return 1;
+}
+
+
+/**
+ Union-set function.  Allocates and returns a new set which is the union of
+ the two given sets 'left' and 'right'.  Also returns the new set length.
+
+ @param left		Left set - order is preserved on this set; that is,
+			this is the set where the caller cares about ordering.
+ @param ll		Length of left set.
+ @param right		Right set - order is not preserved on this set during
+			the union operation
+ @param rl		Length of right set
+ @param ret		Return set.  Should * not * be preallocated.
+ @param retl		Return set length.  Should be ready to accept 1 integer
+			upon calling this function
+ @return 		0 on success, -1 on error
+ */
+int
+s_union(set_type_t *left, int ll, set_type_t *right, int rl,
+	set_type_t **ret, int *retl)
+{
+	int l, r, cnt = 0, total;
+
+	total = ll + rl; /* Union will never exceed both sets */
+
+	*ret = malloc(sizeof(set_type_t)*total);
+	if (!*ret) {
+		return -1;
+	}
+	memset((void *)(*ret), 0, sizeof(set_type_t)*total);
+
+	cnt = 0;
+
+	/* Add all the ones on the left */
+	for (l = 0; l < ll; l++)
+		s_add(*ret, &cnt, left[l]);
+
+	/* Add the ones on the left */
+	for (r = 0; r < rl; r++)
+		s_add(*ret, &cnt, right[r]);
+
+	*retl = cnt;
+
+	return 0;
+}
+
+
+/**
+ Intersection-set function.  Allocates and returns a new set which is the 
+ intersection of the two given sets 'left' and 'right'.  Also returns the new
+ set length.
+
+ @param left		Left set - order is preserved on this set; that is,
+			this is the set where the caller cares about ordering.
+ @param ll		Length of left set.
+ @param right		Right set - order is not preserved on this set during
+			the union operation
+ @param rl		Length of right set
+ @param ret		Return set.  Should * not * be preallocated.
+ @param retl		Return set length.  Should be ready to accept 1 integer
+			upon calling this function
+ @return 		0 on success, -1 on error
+ */
+int
+s_intersection(set_type_t *left, int ll, set_type_t *right, int rl,
+	       set_type_t **ret, int *retl)
+{
+	int l, r, cnt = 0, total;
+
+	total = ll; /* Intersection will never exceed one of the two set
+		       sizes */
+
+	*ret = malloc(sizeof(set_type_t)*total);
+	if (!*ret) {
+		return -1;
+	}
+	memset((void *)(*ret), 0, sizeof(set_type_t)*total);
+
+	cnt = 0;
+	/* Find duplicates */
+	for (l = 0; l < ll; l++) {
+		for (r = 0; r < rl; r++) {
+			if (left[l] != right[r])
+				continue;
+			if (s_add(*ret, &cnt, right[r]))
+				break;
+		}
+	}
+
+	*retl = cnt;
+	return 0;
+}
+
+
+/**
+ Delta-set function.  Allocates and returns a new set which is the delta (i.e.
+ numbers not in both sets) of the two given sets 'left' and 'right'.  Also
+ returns the new set length.
+
+ @param left		Left set - order is preserved on this set; that is,
+			this is the set where the caller cares about ordering.
+ @param ll		Length of left set.
+ @param right		Right set - order is not preserved on this set during
+			the union operation
+ @param rl		Length of right set
+ @param ret		Return set.  Should * not * be preallocated.
+ @param retl		Return set length.  Should be ready to accept 1 integer
+			upon calling this function
+ @return 		0 on success, -1 on error
+ */
+int
+s_delta(set_type_t *left, int ll, set_type_t *right, int rl,
+	set_type_t **ret, int *retl)
+{
+	int l, r, cnt = 0, total, found;
+
+	total = ll + rl; /* Union will never exceed both sets */
+
+	*ret = malloc(sizeof(set_type_t)*total);
+	if (!*ret) {
+		return -1;
+	}
+	memset((void *)(*ret), 0, sizeof(set_type_t)*total);
+
+	cnt = 0;
+
+	/* not efficient, but it works */
+	/* Add all the ones on the left */
+	for (l = 0; l < ll; l++) {
+		found = 0;
+		for (r = 0; r < rl; r++) {
+			if (right[r] == left[l]) {
+				found = 1;
+				break;
+			}
+		}
+		
+		if (found)
+			continue;
+		s_add(*ret, &cnt, left[l]);
+	}
+
+
+	/* Add all the ones on the right*/
+	for (r = 0; r < rl; r++) {
+		found = 0;
+		for (l = 0; l < ll; l++) {
+			if (right[r] == left[l]) {
+				found = 1;
+				break;
+			}
+		}
+		
+		if (found)
+			continue;
+		s_add(*ret, &cnt, right[r]);
+	}
+
+	*retl = cnt;
+
+	return 0;
+}
+
+
+/**
+ Subtract-set function.  Allocates and returns a new set which is the
+ subtraction of the right set from the left set.
+ Also returns the new set length.
+
+ @param left		Left set - order is preserved on this set; that is,
+			this is the set where the caller cares about ordering.
+ @param ll		Length of left set.
+ @param right		Right set - order is not preserved on this set during
+			the union operation
+ @param rl		Length of right set
+ @param ret		Return set.  Should * not * be preallocated.
+ @param retl		Return set length.  Should be ready to accept 1 integer
+			upon calling this function
+ @return 		0 on success, -1 on error
+ */
+int
+s_subtract(set_type_t *left, int ll, set_type_t *right, int rl,
+	   set_type_t **ret, int *retl)
+{
+	int l, r, cnt = 0, total, found;
+
+	total = ll; /* Union will never exceed left set length*/
+
+	*ret = malloc(sizeof(set_type_t)*total);
+	if (!*ret) {
+		return -1;
+	}
+	memset((void *)(*ret), 0, sizeof(set_type_t)*total);
+
+	cnt = 0;
+
+	/* not efficient, but it works */
+	for (l = 0; l < ll; l++) {
+		found = 0;
+		for (r = 0; r < rl; r++) {
+			if (right[r] == left[l]) {
+				found = 1;
+				break;
+			}
+		}
+		
+		if (found)
+			continue;
+		s_add(*ret, &cnt, left[l]);
+	}
+
+	*retl = cnt;
+
+	return 0;
+}
+
+
+/**
+ Shuffle-set function.  Weakly randomizes ordering of a set in-place.
+
+ @param set		Set to randomize
+ @param sl		Length of set
+ @return		0
+ */
+int
+s_shuffle(set_type_t *set, int sl)
+{
+	int x, newidx;
+	unsigned r_state = 0;
+	set_type_t t;
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+	r_state = (int)(tv.tv_usec);
+
+	for (x = 0; x < sl; x++) {
+		newidx = (rand_r(&r_state) % sl);
+		if (newidx == x)
+			continue;
+		t = set[x];
+		set[x] = set[newidx];
+		set[newidx] = t;
+	}
+
+	return 0;
+}
+
+
+#ifdef STANDALONE
+/* Testbed */
+/*
+  gcc -o sets sets.c -DSTANDALONE -ggdb -I../../include \
+       -Wall -Werror -Wstrict-prototypes -Wextra
+ */
+int
+main(int __attribute__ ((unused)) argc, char __attribute__ ((unused)) **argv)
+{
+	set_type_t a[] = { 1, 2, 3, 3, 3, 2, 2, 3 };
+	set_type_t b[] = { 2, 3, 4 };
+	set_type_t *i;
+	int ilen = 0, x;
+
+	s_union(a, 8, b, 3, &i, &ilen);
+
+	/* Should return length of 4 - { 1 2 3 4 } */
+	printf("set_union [%d] = ", ilen);
+	for ( x = 0; x < ilen; x++) {
+		printf("%d ", (int)i[x]);
+	}
+	printf("\n");
+
+	s_shuffle(i, ilen);
+	printf("shuffled [%d] = ", ilen);
+	for ( x = 0; x < ilen; x++) {
+		printf("%d ", (int)i[x]);
+	}
+	printf("\n");
+
+
+	free(i);
+
+	/* Should return length of 2 - { 2 3 } */
+	s_intersection(a, 8, b, 3, &i, &ilen);
+
+	printf("set_intersection [%d] = ", ilen);
+	for ( x = 0; x < ilen; x++) {
+		printf("%d ", (int)i[x]);
+	}
+	printf("\n");
+
+	free(i);
+
+	/* Should return length of 2 - { 1 4 } */
+	s_delta(a, 8, b, 3, &i, &ilen);
+
+	printf("set_delta [%d] = ", ilen);
+	for ( x = 0; x < ilen; x++) {
+		printf("%d ", (int)i[x]);
+	}
+	printf("\n");
+
+	free(i);
+
+	/* Should return length of 1 - { 1 } */
+	s_subtract(a, 8, b, 3, &i, &ilen);
+
+	printf("set_subtract [%d] = ", ilen);
+	for ( x = 0; x < ilen; x++) {
+		printf("%d ", (int)i[x]);
+	}
+	printf("\n");
+
+	free(i);
+
+
+	return 0;
+}
+#endif
diff --git a/rgmanager/src/clulib/signals.c b/rgmanager/src/clulib/signals.c
index 1d49ee5..fa9f4a6 100644
--- a/rgmanager/src/clulib/signals.c
+++ b/rgmanager/src/clulib/signals.c
@@ -1,3 +1,21 @@
+/*
+  Copyright Red Hat, Inc. 2003-2006
+
+  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, or (at your option) any
+  later version.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/rgmanager/src/clulib/tmgr.c b/rgmanager/src/clulib/tmgr.c
new file mode 100644
index 0000000..2565f26
--- /dev/null
+++ b/rgmanager/src/clulib/tmgr.c
@@ -0,0 +1,128 @@
+/*
+  Copyright Red Hat, Inc. 2007
+  Copyright Crosswalk 2006-2007
+
+  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, or (at your option) any
+  later version.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+#ifdef WRAP_THREADS
+#include <stdio.h>
+#include <sys/types.h>
+#include <gettid.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <list.h>
+#include <execinfo.h>
+
+typedef struct _thr {
+	list_head();
+	void *(*fn)(void *arg);
+	char **name;
+	pthread_t th;
+} mthread_t;
+
+static mthread_t *_tlist = NULL;
+static int _tcount = 0;
+static pthread_rwlock_t _tlock = PTHREAD_RWLOCK_INITIALIZER;
+
+void
+dump_thread_states(FILE *fp)
+{
+	int x;
+	mthread_t *curr;
+	fprintf(fp, "Thread Information\n");
+	pthread_rwlock_rdlock(&_tlock);
+	list_for(&_tlist, curr, x) {
+		fprintf(fp, "  Thread #%d   id: %d   function: %s\n",
+			x, (unsigned)curr->th, curr->name[0]);
+	}
+	pthread_rwlock_unlock(&_tlock);
+	fprintf(fp, "\n\n");
+}
+
+
+int __real_pthread_create(pthread_t *, const pthread_attr_t *,
+			  void *(*)(void*), void *);
+int
+__wrap_pthread_create(pthread_t *th, const pthread_attr_t *attr,
+	 	      void *(*start_routine)(void*),
+	 	      void *arg)
+{
+	void *fn = start_routine;
+	mthread_t *new;
+	int ret;
+
+	new = malloc(sizeof (*new));
+
+	ret = __real_pthread_create(th, attr, start_routine, arg);
+	if (ret) {
+		if (new)
+			free(new);
+		return ret;
+	}
+
+	if (new) {
+		new->th = *th;
+		new->fn = start_routine;
+		new->name = backtrace_symbols(&fn, 1);
+		pthread_rwlock_wrlock(&_tlock);
+		list_insert(&_tlist, new);
+		++_tcount;
+		pthread_rwlock_unlock(&_tlock);
+	}
+
+	return ret;
+}
+
+
+void __real_pthread_exit(void *);
+void
+__wrap_pthread_exit(void *exitval)
+{
+	mthread_t *old;
+	int ret = 0, found = 0;
+	pthread_t me = pthread_self();
+
+	pthread_rwlock_rdlock(&_tlock);
+	list_for(&_tlist, old, ret) {
+		if (old->th == me) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		old = NULL;
+	pthread_rwlock_unlock(&_tlock);
+
+	if (!old)
+		__real_pthread_exit(exitval);
+
+	pthread_rwlock_wrlock(&_tlock);
+	list_remove(&_tlist, old);
+	--_tcount;
+	pthread_rwlock_unlock(&_tlock);
+
+	if (old->name)
+		free(old->name);
+	free(old);
+	__real_pthread_exit(exitval);
+}
+#endif
diff --git a/rgmanager/src/daemons/event_config.c b/rgmanager/src/daemons/event_config.c
new file mode 100644
index 0000000..c52e8e1
--- /dev/null
+++ b/rgmanager/src/daemons/event_config.c
@@ -0,0 +1,541 @@
+/**
+  Copyright Red Hat, Inc. 2002-2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge,
+  MA 02139, USA.
+*/
+/** @file
+ * CCS event parsing, based on failover domain parsing
+ */
+#include <string.h>
+#include <list.h>
+#include <clulog.h>
+#include <resgroup.h>
+#include <restart_counter.h>
+#include <reslist.h>
+#include <ccs.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <members.h>
+#include <reslist.h>
+#include <ctype.h>
+#include <event.h>
+
+#define CONFIG_NODE_ID_TO_NAME \
+   "/cluster/clusternodes/clusternode[ nodeid=\"%d\"]/@name"
+#define CONFIG_NODE_NAME_TO_ID \
+   "/cluster/clusternodes/clusternode[ name=\"%s\"]/@nodeid"
+
+void deconstruct_events(event_table_t **);
+void print_event(event_t *ev);
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define ENTER() clulog(LOG_DEBUG, "ENTER: %s\n", __FUNCTION__)
+#define RETURN(val) {\
+	clulog(LOG_DEBUG, "RETURN: %s line=%d value=%d\n", __FUNCTION__, \
+	       __LINE__, (val));\
+	return(val);\
+}
+#else
+#define ENTER()
+#define RETURN(val) return(val)
+#endif
+
+#ifdef NO_CCS
+#define ccs_get(fd, query, ret) conf_get(query, ret)
+#endif
+
+/*
+   <events>
+     <event name="helpful_name_here" class="node"
+            node="nodeid|nodename" nodestate="up|down">
+	    slang_script_stuff();
+	    start_service();
+     </event>
+   </events>
+ */
+int
+event_match(event_t *pattern, event_t *actual)
+{
+	if (pattern->ev_type != EVENT_NONE &&
+	    actual->ev_type != pattern->ev_type)
+		return 0;
+
+	/* If there's no event class specified, the rest is
+	   irrelevant */
+	if (pattern->ev_type == EVENT_NONE)
+		return 1;
+
+	switch(pattern->ev_type) {
+	case EVENT_NODE:
+		if (pattern->ev.node.ne_nodeid >= 0 &&
+		    actual->ev.node.ne_nodeid !=
+				pattern->ev.node.ne_nodeid) {
+			return 0;
+		}
+		if (pattern->ev.node.ne_local >= 0 && 
+		    actual->ev.node.ne_local !=
+				pattern->ev.node.ne_local) {
+			return 0;
+		}
+		if (pattern->ev.node.ne_state >= 0 && 
+		    actual->ev.node.ne_state !=
+				pattern->ev.node.ne_state) {
+			return 0;
+		}
+		if (pattern->ev.node.ne_clean >= 0 && 
+		    actual->ev.node.ne_clean !=
+				pattern->ev.node.ne_clean) {
+			return 0;
+		}
+		return 1; /* All specified params match */
+	case EVENT_RG:
+		if (pattern->ev.group.rg_name[0] &&
+		    strcasecmp(actual->ev.group.rg_name, 
+			       pattern->ev.group.rg_name)) {
+			return 0;
+		}
+		if (pattern->ev.group.rg_state != (uint32_t)-1 && 
+		    actual->ev.group.rg_state !=
+				pattern->ev.group.rg_state) {
+			return 0;
+		}
+		if (pattern->ev.group.rg_owner >= 0 && 
+		    actual->ev.group.rg_owner !=
+				pattern->ev.group.rg_owner) {
+			return 0;
+		}
+		return 1;
+	case EVENT_CONFIG:
+		if (pattern->ev.config.cfg_version >= 0 && 
+		    actual->ev.config.cfg_version !=
+				pattern->ev.config.cfg_version) {
+			return 0;
+		}
+		if (pattern->ev.config.cfg_oldversion >= 0 && 
+		    actual->ev.config.cfg_oldversion !=
+				pattern->ev.config.cfg_oldversion) {
+			return 0;
+		}
+		return 1;
+	case EVENT_USER:
+		if (pattern->ev.user.u_name[0] &&
+		    strcasecmp(actual->ev.user.u_name, 
+			       pattern->ev.user.u_name)) {
+			return 0;
+		}
+		if (pattern->ev.user.u_request != 0 && 
+		    actual->ev.user.u_request !=
+				pattern->ev.user.u_request) {
+			return 0;
+		}
+		if (pattern->ev.user.u_target != 0 && 
+		    actual->ev.user.u_target !=
+				pattern->ev.user.u_target) {
+			return 0;
+		}
+		return 1;
+	default:
+		break;
+	}
+			
+	return 0;
+}
+
+
+char *
+ccs_node_id_to_name(int ccsfd, int nodeid)
+{
+	char xpath[256], *ret = 0;
+
+	snprintf(xpath, sizeof(xpath), CONFIG_NODE_ID_TO_NAME,
+		 nodeid);
+	if (ccs_get(ccsfd, xpath, &ret) == 0)
+		return ret;
+	return NULL;
+}
+
+
+int
+ccs_node_name_to_id(int ccsfd, char *name)
+{
+	char xpath[256], *ret = 0;
+	int rv = 0;
+
+	snprintf(xpath, sizeof(xpath), CONFIG_NODE_NAME_TO_ID,
+		 name);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		rv = atoi(ret);
+		free(ret);
+		return rv;
+	}
+	return 0;
+}
+
+
+static void 
+deconstruct_event(event_t *ev)
+{
+	if (ev->ev_script)
+		free(ev->ev_script);
+	if (ev->ev_name)
+		free(ev->ev_name);
+	free(ev);
+}
+
+
+static int
+get_node_event(int ccsfd, char *base, event_t *ev)
+{
+	char xpath[256], *ret = NULL;
+
+	/* Clear out the possibilitiies */
+	ev->ev.node.ne_nodeid = -1;
+	ev->ev.node.ne_local = -1;
+	ev->ev.node.ne_state = -1;
+	ev->ev.node.ne_clean = -1;
+
+	snprintf(xpath, sizeof(xpath), "%s/@node_id", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		ev->ev.node.ne_nodeid = atoi(ret);
+		free(ret);
+		if (ev->ev.node.ne_nodeid <= 0)
+			return -1;
+	} else {
+		/* See if there's a node name */
+		snprintf(xpath, sizeof(xpath), "%s/@node", base);
+		if (ccs_get(ccsfd, xpath, &ret) == 0) {
+			ev->ev.node.ne_nodeid =
+				ccs_node_name_to_id(ccsfd, ret);
+			free(ret);
+			if (ev->ev.node.ne_nodeid <= 0)
+				return -1;
+		}
+	}
+
+	snprintf(xpath, sizeof(xpath), "%s/@node_state", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		if (!strcasecmp(ret, "up")) {
+			ev->ev.node.ne_state = 1;
+		} else if (!strcasecmp(ret, "down")) {
+			ev->ev.node.ne_state = 0;
+		} else {
+			ev->ev.node.ne_state = !!atoi(ret);
+		}
+		free(ret);
+	}
+
+	snprintf(xpath, sizeof(xpath), "%s/@node_clean", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		ev->ev.node.ne_clean = !!atoi(ret);
+		free(ret);
+	}
+
+	snprintf(xpath, sizeof(xpath), "%s/@node_local", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		ev->ev.node.ne_local = !!atoi(ret);
+		free(ret);
+	}
+
+	return 0;
+}
+
+
+static int
+get_rg_event(int ccsfd, char *base, event_t *ev)
+{
+	char xpath[256], *ret = NULL;
+
+	/* Clear out the possibilitiies */
+	ev->ev.group.rg_name[0] = 0;
+	ev->ev.group.rg_state = (uint32_t)-1;
+	ev->ev.group.rg_owner = -1;
+
+	snprintf(xpath, sizeof(xpath), "%s/@service", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		strncpy(ev->ev.group.rg_name, ret,
+			sizeof(ev->ev.group.rg_name));
+		free(ret);
+		if (!strlen(ev->ev.group.rg_name)) {
+			return -1;
+		}
+	}
+
+	snprintf(xpath, sizeof(xpath), "%s/@service_state", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		if (!isdigit(ret[0])) {
+			ev->ev.group.rg_state =
+			       	rg_state_str_to_id(ret);
+		} else {
+			ev->ev.group.rg_state = atoi(ret);
+		}	
+		free(ret);
+	}
+
+	snprintf(xpath, sizeof(xpath), "%s/@service_owner", base);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		if (!isdigit(ret[0])) {
+			ev->ev.group.rg_owner =
+			       	ccs_node_name_to_id(ccsfd, ret);
+		} else {
+			ev->ev.group.rg_owner = !!atoi(ret);
+		}	
+		free(ret);
+	}
+
+	return 0;
+}
+
+
+static int
+get_config_event(int __attribute__((unused)) ccsfd,
+		 char __attribute__((unused)) *base,
+		 event_t __attribute__((unused)) *ev)
+{
+	errno = ENOSYS;
+	return -1;
+}
+
+
+static event_t *
+get_event(int ccsfd, char *base, int idx, int *_done)
+{
+	event_t *ev;
+	char xpath[256];
+	char *ret = NULL;
+
+	*_done = 0;
+	snprintf(xpath, sizeof(xpath), "%s/event[%d]/@name",
+		 base, idx);
+	if (ccs_get(ccsfd, xpath, &ret) != 0) {
+		*_done = 1;
+		return NULL;
+	}
+
+	ev = malloc(sizeof(*ev));
+	if (!ev)
+		return NULL;
+	memset(ev, 0, sizeof(*ev));
+	ev->ev_name = ret;
+
+	/* Get the script file / inline from config */
+	ret = NULL;
+	snprintf(xpath, sizeof(xpath), "%s/event[%d]/@file",
+		 base, idx);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		ev->ev_script_file = ret;
+	} else {
+		snprintf(xpath, sizeof(xpath), "%s/event[%d]",
+		         base, idx);
+		if (ccs_get(ccsfd, xpath, &ret) == 0) {
+			ev->ev_script = ret;
+		} else {
+			goto out_fail;
+		}
+	}
+
+	/* Get the priority ordering (must be nonzero) */
+	ev->ev_prio = 99;
+	ret = NULL;
+	snprintf(xpath, sizeof(xpath), "%s/event[%d]/@priority",
+		 base, idx);
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		ev->ev_prio = atoi(ret);
+		if (ev->ev_prio <= 0 || ev->ev_prio > EVENT_PRIO_COUNT) {
+			clulog(LOG_ERR,
+			       "event %s: priority %s invalid\n",
+			       ev->ev_name, ret);
+			goto out_fail;
+		}
+		free(ret);
+	}
+
+	/* Get the event class */
+	snprintf(xpath, sizeof(xpath), "%s/event[%d]/@class",
+		 base, idx);
+	ret = NULL;
+	if (ccs_get(ccsfd, xpath, &ret) == 0) {
+		snprintf(xpath, sizeof(xpath), "%s/event[%d]",
+		 	 base, idx);
+		if (!strcasecmp(ret, "node")) {
+			ev->ev_type = EVENT_NODE;
+			if (get_node_event(ccsfd, xpath, ev) < 0)
+				goto out_fail;
+		} else if (!strcasecmp(ret, "service") ||
+			   !strcasecmp(ret, "resource") ||
+			   !strcasecmp(ret, "rg") ) {
+			ev->ev_type = EVENT_RG;
+			if (get_rg_event(ccsfd, xpath, ev) < 0)
+				goto out_fail;
+		} else if (!strcasecmp(ret, "config") ||
+			   !strcasecmp(ret, "reconfig")) {
+			ev->ev_type = EVENT_CONFIG;
+			if (get_config_event(ccsfd, xpath, ev) < 0)
+				goto out_fail;
+		} else {
+			clulog(LOG_ERR,
+			       "event %s: class %s unrecognized\n",
+			       ev->ev_name, ret);
+			goto out_fail;
+		}
+
+		free(ret);
+		ret = NULL;
+	}
+
+	return ev;
+out_fail:
+	if (ret)
+		free(ret);
+	deconstruct_event(ev);
+	return NULL;
+}
+
+
+static event_t *
+get_default_event(void)
+{
+	event_t *ev;
+	char xpath[1024];
+
+	ev = malloc(sizeof(*ev));
+	if (!ev)
+		return NULL;
+	memset(ev, 0, sizeof(*ev));
+	ev->ev_name = strdup("Default");
+
+	/* Get the script file / inline from config */
+	snprintf(xpath, sizeof(xpath), "%s/default_event_script.sl",
+		 RESOURCE_ROOTDIR);
+
+	ev->ev_prio = 100;
+	ev->ev_type = EVENT_NONE;
+	ev->ev_script_file = strdup(xpath);
+	if (!ev->ev_script_file || ! ev->ev_name) {
+		deconstruct_event(ev);
+		return NULL;
+	}
+
+	return ev;
+}
+
+
+/**
+ * similar API to failover domain
+ */
+int
+construct_events(int ccsfd, event_table_t **events)
+{
+	char xpath[256];
+	event_t *ev;
+	int x = 1, done = 0;
+
+	/* Allocate the event list table */
+	*events = malloc(sizeof(event_table_t) +
+			 sizeof(event_t) * (EVENT_PRIO_COUNT+1));
+	if (!*events)
+		return -1;
+	memset(*events, 0, sizeof(event_table_t) +
+	       		   sizeof(event_t) * (EVENT_PRIO_COUNT+1));
+	(*events)->max_prio = EVENT_PRIO_COUNT;
+
+	snprintf(xpath, sizeof(xpath),
+		 RESOURCE_TREE_ROOT "/events");
+
+	do {
+		ev = get_event(ccsfd, xpath, x++, &done);
+		if (ev)
+			list_insert(&((*events)->entries[ev->ev_prio]), ev);
+	} while (!done);
+
+	ev = get_default_event();
+	if (ev)
+		list_insert(&((*events)->entries[ev->ev_prio]), ev);
+	
+	return 0;
+}
+
+
+void
+print_event(event_t *ev)
+{
+	printf("  Name: %s\n", ev->ev_name);
+
+	switch(ev->ev_type) {
+	case EVENT_NODE:
+		printf("    Node %d State %d\n", ev->ev.node.ne_nodeid,
+		       ev->ev.node.ne_state);
+		break;
+	case EVENT_RG:
+		printf("    RG %s State %s\n", ev->ev.group.rg_name,
+		       rg_state_str(ev->ev.group.rg_state));
+		break;
+	case EVENT_CONFIG:
+		printf("    Config change - unsupported\n");
+		break;
+	default:
+		printf("    (Any event)\n");
+		break;
+	}
+	
+	if (ev->ev_script) {
+		printf("    Inline script.\n");
+	} else {
+		printf("    File: %s\n", ev->ev_script_file);
+	}
+}
+
+
+void
+print_events(event_table_t *events)
+{
+	int x, y;
+	event_t *ev;
+
+	for (x = 0; x <= events->max_prio; x++) {
+		if (!events->entries[x])
+			continue;
+		printf("Event Priority Level %d:\n", x);
+		list_for(&(events->entries[x]), ev, y) {
+			print_event(ev);
+		}
+	}
+}
+
+
+void
+deconstruct_events(event_table_t **eventsp)
+{
+	int x;
+	event_table_t *events = *eventsp;
+	event_t *ev = NULL;
+
+	if (!events)
+		return;
+
+	for (x = 0; x <= events->max_prio; x++) {
+		while ((ev = (events->entries[x]))) {
+			list_remove(&(events->entries[x]), ev);
+			deconstruct_event(ev);
+		}
+	}
+
+	free(events);
+	*eventsp = NULL;
+}
+
+
diff --git a/rgmanager/src/daemons/fo_domain.c b/rgmanager/src/daemons/fo_domain.c
index 5dedbd5..a434fe0 100644
--- a/rgmanager/src/daemons/fo_domain.c
+++ b/rgmanager/src/daemons/fo_domain.c
@@ -27,11 +27,14 @@
 #include <list.h>
 #include <clulog.h>
 #include <resgroup.h>
+#include <restart_counter.h>
 #include <reslist.h>
 #include <ccs.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <magma.h>
+#include <sets.h>
 
 
 //#define DEBUG
@@ -94,6 +97,23 @@ get_node(int ccsfd, char *base, int idx, fod_t *domain)
 	fodn->fdn_name = ret;
 	fodn->fdn_prio = 0;
 
+	snprintf(xpath, sizeof(xpath),
+		 "/cluster/clusternodes/clusternode[ name=\"%s\"]/@nodeid",
+		 ret);
+	if (ccs_get(ccsfd, xpath, &ret) != 0) {
+		clulog(LOG_WARNING, "Node %s has no nodeid attribute\n",
+		       fodn->fdn_name);
+		fodn->fdn_nodeid = -1;
+	} else {
+		/* 64-bit-ism on rhel4? */
+		fodn->fdn_nodeid = atoi(ret);
+	}
+
+	/* Don't even bother getting priority if we're not ordered (it's set
+	   to 0 above */
+	if (!(domain->fd_flags & FOD_ORDERED))
+		return fodn;
+
 	snprintf(xpath, sizeof(xpath), "%s/failoverdomainnode[%d]/@priority",
 		 base, idx);
 	if (ccs_get(ccsfd, xpath, &ret) != 0)
@@ -226,6 +246,11 @@ print_domains(fod_t **domains)
 {
 	fod_t *fod;
 	fod_node_t *fodn = NULL;
+	/*
+	int x;
+	int *node_set = NULL;
+	int node_set_len = 0;
+	 */
 
 	list_do(domains, fod) {
 		printf("Failover domain: %s\n", fod->fd_name);
@@ -243,9 +268,21 @@ print_domains(fod_t **domains)
 		}
 
 		list_do(&fod->fd_nodes, fodn) {
-			printf("  Node %s (priority %d)\n",
-			       fodn->fdn_name, fodn->fdn_prio);
+			printf("  Node %s (id %d, priority %d)\n",
+			       fodn->fdn_name, fodn->fdn_nodeid,
+			       fodn->fdn_prio);
 		} while (!list_done(&fod->fd_nodes, fodn));
+
+		/*
+		node_domain_set(fod, &node_set, &node_set_len);
+		printf("  Failover Order = {");
+		for (x = 0; x < node_set_len; x++) {
+			printf(" %d ", node_set[x]);
+		}
+		free(node_set);
+		printf("}\n");
+		*/
+		
 	} while (!list_done(domains, fod));
 }
 
@@ -265,7 +302,7 @@ int
 node_in_domain(char *nodename, fod_t *domain,
 	       cluster_member_list_t *membership)
 {
-	int member_online = 0, member_match = 0, preferred = 100, myprio = -1;
+	int online = 0, member_match = 0, preferred = 100, myprio = -1;
 	fod_node_t *fodn;
 
 	list_do(&domain->fd_nodes, fodn) {
@@ -282,7 +319,7 @@ node_in_domain(char *nodename, fod_t *domain,
 		 * If we get here, we know:
 		 * A member of the domain is online somewhere
 		 */
-		member_online = 1;
+		online = 1;
 		if (!strcmp(nodename, fodn->fdn_name)) {
 			/*
 			 * If we get here, we know:
@@ -296,7 +333,7 @@ node_in_domain(char *nodename, fod_t *domain,
 			preferred = fodn->fdn_prio;
 	} while (!list_done(&domain->fd_nodes, fodn));
 
-	if (!member_online)
+	if (!online)
 		return 0;
 
 	if (!member_match)
@@ -311,6 +348,70 @@ node_in_domain(char *nodename, fod_t *domain,
 }
 
 
+int
+node_domain_set(fod_t *domain, uint64_t **ret, int *retlen)
+{
+	int x, i, j;
+	int *tmpset;
+	int ts_count;
+
+	fod_node_t *fodn;
+
+	/* Count domain length */
+	list_for(&domain->fd_nodes, fodn, x) { }
+	
+	*retlen = 0;
+	*ret = malloc(sizeof(int) * x);
+	if (!(*ret))
+		return -1;
+	tmpset = malloc(sizeof(int) * x);
+	if (!(*tmpset))
+		return -1;
+
+	if (domain->fd_flags & FOD_ORDERED) {
+		for (i = 1; i <= 100; i++) {
+			
+			ts_count = 0;
+			list_for(&domain->fd_nodes, fodn, x) {
+				if (fodn->fdn_prio == i) {
+					s_add(tmpset, &ts_count,
+					      fodn->fdn_nodeid);
+				}
+			}
+
+			if (!ts_count)
+				continue;
+
+			/* Shuffle stuff at this prio level */
+			if (ts_count > 1)
+				s_shuffle(tmpset, ts_count);
+			for (j = 0; j < ts_count; j++)
+				s_add(*ret, retlen, tmpset[j]);
+		}
+	}
+
+	/* Add unprioritized nodes */
+	ts_count = 0;
+	list_for(&domain->fd_nodes, fodn, x) {
+		if (!fodn->fdn_prio) {
+			s_add(tmpset, &ts_count,
+			      fodn->fdn_nodeid);
+		}
+	}
+
+	if (!ts_count)
+		return 0;
+
+	/* Shuffle stuff at this prio level */
+	if (ts_count > 1)
+		s_shuffle(tmpset, ts_count);
+	for (j = 0; j < ts_count; j++)
+		s_add(*ret, retlen, tmpset[j]);
+
+	return 0;
+}
+
+
 /**
  * See if a given nodeid should start a specified service svcid.
  *
@@ -411,7 +512,7 @@ node_should_start(uint64_t nodeid, cluster_member_list_t *membership,
 			RETURN(FOD_BEST);
 		}
                 
-		if (get_rg_state(rg_name, &svc_state) == FAIL) {
+		if (get_rg_state(rg_name, &svc_state) == RG_EFAIL) {
                 	/*
 			 * Couldn't get the service state, thats odd
 			 */
diff --git a/rgmanager/src/daemons/groups.c b/rgmanager/src/daemons/groups.c
index ebd37dc..45dc912 100644
--- a/rgmanager/src/daemons/groups.c
+++ b/rgmanager/src/daemons/groups.c
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2002-2003
+  Copyright Red Hat, Inc. 2002-2006
   Copyright Mission Critical Linux, Inc. 2000
 
   This program is free software; you can redistribute it and/or modify it
@@ -19,21 +19,27 @@
 */
 //#define DEBUG
 #include <platform.h>
-#include <magma.h>
-#include <magmamsg.h>
 #include <resgroup.h>
+#include <restart_counter.h>
 #include <reslist.h>
 #include <vf.h>
 #include <magma.h>
 #include <ccs.h>
 #include <clulog.h>
+#include <magmamsg.h>
 #include <list.h>
 #include <reslist.h>
 #include <assert.h>
+#include <event.h>
 
-#define cm_svccount cm_pad[0] /* Theses are uint8_t size */
-#define cm_svcexcl  cm_pad[1]
+/* Use address field in this because we never use it internally,
+   and there is no extra space in the cman_node_t type.
+   */
 
+#define cn_svccount cm_pad[0] /* Theses are uint8_t size */
+#define cn_svcexcl  cm_pad[1]
+
+extern event_table_t *master_event_table;
 
 static int config_version = 0;
 static resource_t *_resources = NULL;
@@ -41,9 +47,17 @@ static resource_rule_t *_rules = NULL;
 static resource_node_t *_tree = NULL;
 static fod_t *_domains = NULL;
 
+#ifdef WRAP_LOCKS
+pthread_mutex_t config_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+pthread_mutex_t status_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+#else
 pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_rwlock_t resource_lock = PTHREAD_RWLOCK_INITIALIZER;
 pthread_mutex_t status_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+pthread_rwlock_t resource_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+void res_build_name(char *, size_t, resource_t *);
+int group_migratory(char *groupname, int lock);
 
 
 struct status_arg {
@@ -72,14 +86,41 @@ node_should_start_safe(uint64_t nodeid, cluster_member_list_t *membership,
 
 
 int
+node_domain_set_safe(char *domainname, uint64_t **ret, int *retlen, int *flags)
+{
+	fod_t *fod;
+	int rv = -1, found = 0, x = 0;
+
+	pthread_rwlock_rdlock(&resource_lock);
+
+	list_for(&_domains, fod, x) {
+		if (!strcasecmp(fod->fd_name, domainname)) {
+			found = 1;
+			break;
+		}
+	} // while (!list_done(&_domains, fod));
+
+	if (found) {
+		rv = node_domain_set(fod, ret, retlen);
+		*flags = fod->fd_flags;
+	}
+
+	pthread_rwlock_unlock(&resource_lock);
+
+	return rv;
+}
+
+
+int
 count_resource_groups(cluster_member_list_t *ml)
 {
 	resource_t *res;
-	char *rgname, *val;
+	resource_node_t *node;
+	char rgname[64], *val;
 	int x;
 	rg_state_t st;
 	void *lockp;
-	cluster_member_t *mp;
+	cluster_member_list_t *mp;
 
 	for (x = 0; x < ml->cml_count; x++) {
 		ml->cml_members[x].cm_svccount = 0;
@@ -88,11 +129,11 @@ count_resource_groups(cluster_member_list_t *ml)
 
 	pthread_rwlock_rdlock(&resource_lock);
 
-	list_do(&_resources, res) {
-		if (res->r_rule->rr_root == 0)
-			continue;
+	list_do(&_tree, node) {
+
+		res = node->rn_resource;
 
-		rgname = res->r_attrs[0].ra_value;
+		res_build_name(rgname, sizeof(rgname), res);
 
 		if (rg_lock(rgname, &lockp) < 0) {
 			clulog(LOG_ERR, "#XX: Unable to obtain cluster "
@@ -126,10 +167,9 @@ count_resource_groups(cluster_member_list_t *ml)
 			++mp->cm_svcexcl;
 		}
 
-	} while (!list_done(&_resources, res));
+	} while (!list_done(&_tree, node));
 
 	pthread_rwlock_unlock(&resource_lock);
-
 	return 0;
 }
 
@@ -168,12 +208,35 @@ is_exclusive(char *svcName)
 }
 
 
-int get_rg_state_local(char *, rg_state_t *);
+resource_node_t *
+node_by_ref(resource_node_t **tree, char *name)
+{
+	resource_t *res;
+	resource_node_t *node, *ret = NULL;
+	char rgname[64];
+	int x;
+
+	list_for(tree, node, x) {
+
+		res = node->rn_resource;
+		res_build_name(rgname, sizeof(rgname), res);
+
+		if (!strcasecmp(name, rgname)) {
+			ret = node;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+
 int
 count_resource_groups_local(cluster_member_t *mp)
 {
 	resource_t *res;
-	char *rgname, *val;
+	resource_node_t *node;
+	char rgname[64];
 	rg_state_t st;
 
 	mp->cm_svccount = 0;
@@ -181,20 +244,18 @@ count_resource_groups_local(cluster_member_t *mp)
 
 	pthread_rwlock_rdlock(&resource_lock);
 
-	list_do(&_resources, res) {
+	list_do(&_tree, node) {
 
-		if (res->r_rule->rr_root == 0)
-			continue;
+		res = node->rn_resource;
 
-		rgname = res->r_attrs[0].ra_value;
+		res_build_name(rgname, sizeof(rgname), res);
 
 		if (get_rg_state_local(rgname, &st) < 0) {
 			continue;
 		}
 
 		if (st.rs_state != RG_STATE_STARTED &&
-		     st.rs_state != RG_STATE_STARTING &&
-		     st.rs_state != RG_STATE_STOPPING)
+		     st.rs_state != RG_STATE_STARTING)
 			continue;
 
 		if (mp->cm_id != st.rs_owner)
@@ -202,13 +263,12 @@ count_resource_groups_local(cluster_member_t *mp)
 
 		++mp->cm_svccount;
 
-		if (is_exclusive_res(res)) 
+		if (is_exclusive_res(res))
 			++mp->cm_svcexcl;
 
-	} while (!list_done(&_resources, res));
+	} while (!list_done(&_tree, node));
 
 	pthread_rwlock_unlock(&resource_lock);
-
 	return 0;
 }
 
@@ -225,6 +285,7 @@ have_exclusive_resources(void)
 			pthread_rwlock_unlock(&resource_lock);
 			return 1;
 		}
+
 	} while (!list_done(&_resources, res));
 
 	pthread_rwlock_unlock(&resource_lock);
@@ -237,9 +298,8 @@ int
 check_exclusive_resources(cluster_member_list_t *membership, char *svcName)
 {
 	cluster_member_t *mp;
-	int exclusive, count; 
+	int exclusive, count, excl; 
 	resource_t *res;
-	char *val;
 
 	mp = memb_id_to_p(membership, my_id());
 	assert(mp);
@@ -250,14 +310,13 @@ check_exclusive_resources(cluster_member_list_t *membership, char *svcName)
 	res = find_root_by_ref(&_resources, svcName);
 	if (!res) {
 		pthread_rwlock_unlock(&resource_lock);
-		return FAIL;
+		return RG_ENOSERVICE;
 	}
-	val = res_attr_value(res, "exclusive");
+
+	excl = is_exclusive_res(res);
 	pthread_rwlock_unlock(&resource_lock);
-	if (exclusive || (count && val && 
-			(!strcmp(val, "yes") || (atoi(val)>0)))) {
-		return 1;
-	}
+	if (exclusive || (count && excl))
+		return RG_YES;
 
 	return 0;
 }
@@ -343,6 +402,42 @@ best_target_node(cluster_member_list_t *allowed, uint64_t owner,
 }
 
 
+int
+check_depend(resource_t *res)
+{
+	char *val;
+	rg_state_t rs;
+
+	val = res_attr_value(res, "depend");
+	if (!val)
+		/* No dependency */
+		return -1;
+
+	if (get_rg_state_local(val, &rs) == 0)
+		return (rs.rs_state == RG_STATE_STARTED);
+
+	return 1;
+}
+
+
+int
+check_depend_safe(char *rg_name)
+{
+	resource_t *res;
+	int ret;
+
+	pthread_rwlock_rdlock(&resource_lock);
+	res = find_root_by_ref(&_resources, rg_name);
+	if (!res)
+		return -1;
+
+	ret = check_depend(res);
+	pthread_rwlock_unlock(&resource_lock);
+
+	return ret;
+}
+
+
 /**
   Start or failback a resource group: if it's not running, start it.
   If it is running and we're a better member to run it, then ask for
@@ -353,9 +448,9 @@ consider_start(resource_node_t *node, char *svcName, rg_state_t *svcStatus,
 	       cluster_member_list_t *membership)
 {
 	char *val;
-	cluster_member_t *mp;
+	cman_node_t *mp;
 	int autostart, exclusive;
-	void *lockp;
+	struct dlm_lksb lockp;
 
 	mp = memb_id_to_p(membership, my_id());
 	assert(mp);
@@ -414,6 +509,14 @@ consider_start(resource_node_t *node, char *svcName, rg_state_t *svcStatus,
 		}
 	}
 
+	/* See if service this one depends on is running.  If not,
+           don't start it */
+	if (check_depend(node->rn_resource) == 0) {
+		clulog(LOG_DEBUG,
+		       "Skipping RG %s: Dependency missing\n", svcName);
+		return;
+	}
+
 	val = res_attr_value(node->rn_resource, "exclusive");
 	exclusive = val && ((!strcmp(val, "yes") || (atoi(val)>0)));
 
@@ -450,13 +553,14 @@ void
 consider_relocate(char *svcName, rg_state_t *svcStatus, uint64_t nodeid,
 		  cluster_member_list_t *membership)
 {
-	int a, b;
+	int a, b, req = RG_RELOCATE;
 
 	/*
 	   Service must be running locally in order to consider for
 	   a relocate
 	 */
-	if (svcStatus->rs_state != RG_STATE_STARTED ||
+	if ((svcStatus->rs_state != RG_STATE_STARTING &&
+	    svcStatus->rs_state != RG_STATE_STARTED) ||
 	    svcStatus->rs_owner != my_id())
 		return;
 
@@ -476,14 +580,73 @@ consider_relocate(char *svcName, rg_state_t *svcStatus, uint64_t nodeid,
 	if (a <= b)
 		return;
 
-	clulog(LOG_DEBUG, "Relocating group %s to better node %s\n",
+	if (0) { /* Migrate not supported on RHEL4 */
+		req = RG_MIGRATE;
+	}
+
+	clulog(LOG_NOTICE, "%s %s to better node %s\n",
+	       req==RG_MIGRATE ? "Migrating":"Relocating",
 	       svcName,
 	       memb_id_to_name(membership, nodeid));
 
-	rt_enqueue_request(svcName, RG_RELOCATE, -1, 0, nodeid, 0, 0);
+	rt_enqueue_request(svcName, req, NULL, 0, nodeid, 0, 0);
+}
+
+
+char **
+get_service_names(int *len)
+{
+	resource_node_t *node = NULL;
+	int nservices, ncopied = 0, x;
+	char **ret = NULL;
+	char rg_name[64];
+
+	pthread_rwlock_rdlock(&resource_lock);
+
+	nservices = 0;
+	list_do(&_tree, node) {
+		++nservices;
+	} while (!list_done(&_tree, node));
+	
+	ret = malloc(sizeof(char *) * (nservices + 1));
+	if (!ret)
+		goto out_fail;
+
+	memset(ret, 0, sizeof(char *) * (nservices + 1));
+	nservices = 0;
+	list_for(&_tree, node, nservices) {
+		res_build_name(rg_name, sizeof(rg_name),
+			       node->rn_resource);
+
+		if (!strlen(rg_name))
+			continue;
+
+		ret[ncopied] = strdup(rg_name);
+		if (ret[ncopied]) {
+			ncopied++;
+		} else {
+			goto out_fail;
+		}
+	}
+
+	if (len)
+		*len = ncopied;
+	pthread_rwlock_unlock(&resource_lock);
+	return ret;
+
+out_fail:
+	pthread_rwlock_unlock(&resource_lock);
+	for (x = 0; x < ncopied; x++)
+		free(ret[x]);
+	if (ret)
+		free(ret);
+	return NULL;
 }
 
 
+
+
+
 /**
  * Called to decide what services to start locally during a node_event.
  * Originally a part of node_event, it is now its own function to cut down
@@ -1140,6 +1303,13 @@ check_config_update(void)
 }
 
 
+void
+dump_config_version(FILE *fp)
+{
+	fprintf(fp, "Cluster configuration version %d\n\n", config_version);
+}
+
+
 /**
   Initialize resource groups.  This reads all the resource groups from 
   CCS, builds the tree, etc.  Ideally, we'll have a similar function 
@@ -1149,12 +1319,14 @@ check_config_update(void)
 int
 init_resource_groups(int reconfigure)
 {
-	int fd, x;
+	int fd, x, y, cnt;
 
+	event_table_t *evt = NULL;
 	resource_t *reslist = NULL, *res;
 	resource_rule_t *rulelist = NULL, *rule;
 	resource_node_t *tree = NULL;
 	fod_t *domains = NULL, *fod;
+	event_t *evp;
 	char *val;
 
 	if (reconfigure)
@@ -1178,10 +1350,11 @@ init_resource_groups(int reconfigure)
 		pthread_mutex_lock(&config_mutex);
 		config_version = atoi(val);
 		pthread_mutex_unlock(&config_mutex);
+		free(val);
 	}
-
+	
 	if (ccs_get(fd, "/cluster/rm/@statusmax", &val) == 0) {
-		if (strlen(val))
+		if (strlen(val)) 
 			rg_set_statusmax(atoi(val));
 		free(val);
 	}
@@ -1214,6 +1387,24 @@ init_resource_groups(int reconfigure)
 	x = 0;
 	list_do(&domains, fod) { ++x; } while (!list_done(&domains, fod));
 	clulog(LOG_DEBUG, "%d domains defined\n", x);
+	construct_events(fd, &evt);
+	cnt = 0;
+	if (evt) {
+		for (x=0; x <= evt->max_prio; x++) {
+			if (!evt->entries[x])
+				continue;
+			
+			y = 0;
+
+			list_do(&evt->entries[x], evp) {
+				++y;
+			} while (!list_done(&evt->entries[x], evp));
+
+			cnt += y;
+		}
+	}
+	clulog(LOG_DEBUG, "%d events defined\n", x);
+	
 
 	/* Reconfiguration done */
 	ccs_unlock(fd);
@@ -1242,6 +1433,9 @@ init_resource_groups(int reconfigure)
 	if (_domains)
 		deconstruct_domains(&_domains);
 	_domains = domains;
+	if (master_event_table)
+		deconstruct_events(&master_event_table);
+	master_event_table = evt;
 	pthread_rwlock_unlock(&resource_lock);
 
 	if (reconfigure) {
@@ -1282,6 +1476,94 @@ get_recovery_policy(char *rg_name, char *buf, size_t buflen)
 }
 
 
+int
+get_service_property(char *rg_name, char *prop, char *buf, size_t buflen)
+{
+	int ret = 0;
+	resource_t *res;
+	char *val;
+
+	memset(buf, 0, buflen);
+
+#if 0
+	if (!strcmp(prop, "domain")) {
+		/* not needed */
+		strncpy(buf, "", buflen);
+	} else if (!strcmp(prop, "autostart")) {
+		strncpy(buf, "1", buflen);
+	} else if (!strcmp(prop, "hardrecovery")) {
+		strncpy(buf, "0", buflen);
+	} else if (!strcmp(prop, "exclusive")) {
+		strncpy(buf, "0", buflen);
+	} else if (!strcmp(prop, "nfslock")) {
+		strncpy(buf, "0", buflen);
+	} else if (!strcmp(prop, "recovery")) {
+		strncpy(buf, "restart", buflen);
+	} else if (!strcmp(prop, "depend")) {
+		/* not needed */
+		strncpy(buf, "", buflen);
+	} else {
+		/* not found / no defaults */
+		ret = -1;
+	}
+#endif
+
+	pthread_rwlock_rdlock(&resource_lock);
+	res = find_root_by_ref(&_resources, rg_name);
+	if (res) {
+		val = res_attr_value(res, prop);
+		if (val) {
+			ret = 0;
+			strncpy(buf, val, buflen);
+		}
+	}
+	pthread_rwlock_unlock(&resource_lock);
+
+#if 0
+	if (ret == 0)
+		printf("%s(%s, %s) = %s\n", __FUNCTION__, rg_name, prop, buf);
+	else 
+		printf("%s(%s, %s) = NOT FOUND\n", __FUNCTION__, rg_name, prop);
+#endif
+
+	return ret;
+}
+
+
+int
+add_restart(char *rg_name)
+{
+	resource_node_t *node;
+	int ret = 1;
+
+	pthread_rwlock_rdlock(&resource_lock);
+	node = node_by_ref(&_tree, rg_name);
+	if (node) {
+		ret = restart_add(node->rn_restart_counter);
+	}
+	pthread_rwlock_unlock(&resource_lock);
+
+	return ret;
+}
+
+
+int
+check_restart(char *rg_name)
+{
+	resource_node_t *node;
+	int ret = 0;
+
+	pthread_rwlock_rdlock(&resource_lock);
+	node = node_by_ref(&_tree, rg_name);
+	if (node) {
+		ret = restart_threshold_exceeded(node->rn_restart_counter);
+	}
+	pthread_rwlock_unlock(&resource_lock);
+
+	return ret;
+}
+
+
 void
 kill_resource_groups(void)
 {
diff --git a/rgmanager/src/daemons/main.c b/rgmanager/src/daemons/main.c
index 1f0db93..2b52f23 100644
--- a/rgmanager/src/daemons/main.c
+++ b/rgmanager/src/daemons/main.c
@@ -586,7 +586,7 @@ event_loop(int clusterfd)
 		return 0;
 
 	/* No new messages.  Drop in the status check requests.  */
-	if (n == 0) {
+	if (n == 0 && rg_quorate()) {
 		do_status_checks();
 		return 0;
 	}
diff --git a/rgmanager/src/daemons/reslist.c b/rgmanager/src/daemons/reslist.c
index dbf685a..745d88a 100644
--- a/rgmanager/src/daemons/reslist.c
+++ b/rgmanager/src/daemons/reslist.c
@@ -19,7 +19,6 @@
 #include <libxml/parser.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/xpath.h>
-#include <magma.h>
 #include <ccs.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -27,6 +26,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <list.h>
+#include <restart_counter.h>
 #include <reslist.h>
 #include <pthread.h>
 #ifndef NO_CCS
@@ -37,6 +37,13 @@
 char *attr_value(resource_node_t *node, char *attrname);
 char *rg_attr_value(resource_node_t *node, char *attrname);
 
+void
+res_build_name(char *buf, size_t buflen, resource_t *res)
+{
+	snprintf(buf, buflen, "%s:%s", res->r_rule->rr_type,
+		 res->r_attrs[0].ra_value);
+}
+
 /**
    Find and determine an attribute's value. 
 
@@ -170,18 +177,29 @@ primary_attr_value(resource_t *res)
 /**
    Compare two resources.
 
+  @param left	Left resource
+  @param right	Right resource	
+  @return	-1 on different resource, 0 if the same, 1 if different,
+		2 if different, but only safe resources are different
+
  */
 int
 rescmp(resource_t *left, resource_t *right)
 {
-	int x, y = 0, found;
+	int x, y = 0, found = 0, ret = 0;
+
 
 	/* Completely different resource class... */
 	if (strcmp(left->r_rule->rr_type, right->r_rule->rr_type)) {
-		//printf("Er, wildly different resource type! ");
 		return -1;
 	}
 
+	/*
+	printf("Comparing %s:%s to %s:%s\n",
+	       left->r_rule->rr_type, left->r_attrs[0].ra_value,
+	       right->r_rule->rr_type, right->r_attrs[0].ra_value)
+	 */
+
 	for (x = 0; left->r_attrs && left->r_attrs[x].ra_name; x++) {
 
 		found = 0;
@@ -197,35 +215,52 @@ rescmp(resource_t *left, resource_t *right)
 			    left->r_attrs[x].ra_flags) {
 				/* Flags are different.  Change in
 				   resource agents? */
-				//printf("flags differ ");
+				/*
+				printf("* flags differ %08x vs %08x\n",
+				       left->r_attrs[x].ra_flags,
+				       right->r_attrs[y].ra_flags);
+				 */
 				return 1;
 			}
 
 			if (strcmp(right->r_attrs[y].ra_value,
 				   left->r_attrs[x].ra_value)) {
 				/* Different attribute value. */
-				//printf("different value for attr '%s'  ",
-				       //right->r_attrs[y].ra_name);
-				return 1;
+				/*
+				printf("* different value for attr '%s':"
+				       " '%s' vs '%s'",
+				       right->r_attrs[y].ra_name,
+				       left->r_attrs[x].ra_value,
+				       right->r_attrs[y].ra_value);
+				 */
+				if (left->r_attrs[x].ra_flags & RA_RECONFIG) {
+					/* printf(" [SAFE]\n"); */
+					ret = 2;
+			 	} else {
+					/* printf("\n"); */
+					return 1;
+				}
 			}
 		}
 
 		/* Attribute missing -> different attribute value. */
 		if (!found) {
-			//printf("Attribute %s deleted  ",
-			       //left->r_attrs[x].ra_name);
+			/*
+			printf("* Attribute '%s' deleted\n",
+			       left->r_attrs[x].ra_name);
+			 */
 			return 1;
 		}
 	}
 
 	/* Different attribute count */
 	if (x != y) {
-		//printf("Attribute count differ (attributes added!) ");
+		/* printf("* Attribute count differ (attributes added!) "); */
 		return 1;
 	}
 
 	/* All the same */
-	return 0;
+	return ret;
 }
 
 
@@ -280,11 +315,24 @@ resource_t *
 find_root_by_ref(resource_t **reslist, char *ref)
 {
 	resource_t *curr;
+	char ref_buf[128];
+	char *type;
+	char *name = ref;
 	int x;
 
+	snprintf(ref_buf, sizeof(ref_buf), "%s", ref);
+
+	type = ref_buf;
+	if ((name = strchr(ref_buf, ':'))) {
+		*name = 0;
+		name++;
+	} else {
+		/* Default type */
+		type = "service";
+		name = ref;
+	}
+
 	list_do(reslist, curr) {
-		if (curr->r_rule->rr_root == 0)
-			continue;
 
 		/*
 		   This should be one operation - the primary attr
@@ -292,15 +340,18 @@ find_root_by_ref(resource_t **reslist, char *ref)
 		 */
 		for (x = 0; curr->r_attrs && curr->r_attrs[x].ra_name;
 		     x++) {
+			if (strcmp(type, curr->r_rule->rr_type))
+				continue;
 			if (!(curr->r_attrs[x].ra_flags & RA_PRIMARY))
 				continue;
-			if (strcmp(ref, curr->r_attrs[x].ra_value))
+			if (strcmp(name, curr->r_attrs[x].ra_value))
 				continue;
 
 			return curr;
 		}
 	} while (!list_done(reslist, curr));
 
+
 	return NULL;
 }
 
@@ -421,14 +472,14 @@ xpath_get_one(xmlDocPtr doc, xmlXPathContextPtr ctx, char *query)
 	if (((node->type == XML_ATTRIBUTE_NODE) && strstr(query, "@*")) ||
 	    ((node->type == XML_ELEMENT_NODE) && strstr(query, "child::*"))){
 		if (node->children && node->children->content)
-	  		size = strlen(node->children->content)+
-				      strlen(node->name)+2;
+	  		size = strlen((char *)node->children->content)+
+				      strlen((char *)node->name)+2;
 		else 
-			size = strlen(node->name)+2;
+			size = strlen((char *)node->name)+2;
 		nnv = 1;
 	} else {
 		if (node->children && node->children->content) {
-			size = strlen(node->children->content)+1;
+			size = strlen((char *)node->children->content)+1;
 		} else {
 			goto out;
 		}
@@ -524,6 +575,8 @@ print_resource(resource_t *res)
 		printf(" [NEEDSTOP]");
 	if (res->r_flags & RF_COMMON)
 		printf(" [COMMON]");
+	if (res->r_flags & RF_RECONFIG)
+		printf(" [RECONFIG]");
 	printf("\n");
 
 	if (res->r_rule->rr_maxrefs)
@@ -559,6 +612,8 @@ print_resource(resource_t *res)
 			printf(" unique");
 		if (res->r_attrs[x].ra_flags & RA_REQUIRED)
 			printf(" required");
+		if (res->r_attrs[x].ra_flags & RA_RECONFIG)
+			printf(" reconfig");
 		if (res->r_attrs[x].ra_flags & RA_INHERIT)
 			printf(" inherit(\"%s\")", res->r_attrs[x].ra_value);
 		printf(" ]\n");
@@ -817,6 +872,7 @@ load_resources(int ccsfd, resource_t **reslist, resource_rule_t **rulelist)
 				      "Error storing %s resource\n",
 				      newres->r_rule->rr_type);
 #endif
+
 			       destroy_resource(newres);
 		       }
 
diff --git a/rgmanager/src/daemons/resrules.c b/rgmanager/src/daemons/resrules.c
index 282eb5a..b8317d4 100644
--- a/rgmanager/src/daemons/resrules.c
+++ b/rgmanager/src/daemons/resrules.c
@@ -19,14 +19,16 @@
 #include <libxml/parser.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/xpath.h>
-#include <magma.h>
 #include <ccs.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <resgroup.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <list.h>
+#include <ctype.h>
+#include <restart_counter.h>
 #include <reslist.h>
 #include <pthread.h>
 #include <dirent.h>
@@ -599,6 +601,17 @@ print_resource_rule(resource_rule_t *rr)
 		printf("Max instances: %d\n", rr->rr_maxrefs);
 	if (rr->rr_agent)
 		printf("Agent: %s\n", basename(rr->rr_agent));
+
+	printf("Flags: ");
+	if (rr->rr_flags) {
+		if (rr->rr_flags & RF_INIT)
+			printf("init_on_add ");
+		if (rr->rr_flags & RF_DESTROY)
+			printf("destroy_on_delete ");
+	} else {
+		printf("(none)");
+	}
+	printf("\n");
 	
 	printf("Attributes:\n");
 	if (!rr->rr_attrs) {
@@ -614,18 +627,25 @@ print_resource_rule(resource_rule_t *rr)
 			continue;
 		}
 
-		printf(" [");
-		if (rr->rr_attrs[x].ra_flags & RA_PRIMARY)
-			printf(" primary");
-		if (rr->rr_attrs[x].ra_flags & RA_UNIQUE)
-			printf(" unique");
-		if (rr->rr_attrs[x].ra_flags & RA_REQUIRED)
-			printf(" required");
-		if (rr->rr_attrs[x].ra_flags & RA_INHERIT)
-			printf(" inherit");
-		else if (rr->rr_attrs[x].ra_value)
-			printf(" default=\"%s\"", rr->rr_attrs[x].ra_value);
-		printf(" ]\n");
+		if (rr->rr_attrs[x].ra_flags) {
+			printf(" [");
+			if (rr->rr_attrs[x].ra_flags & RA_PRIMARY)
+				printf(" primary");
+			if (rr->rr_attrs[x].ra_flags & RA_UNIQUE)
+				printf(" unique");
+			if (rr->rr_attrs[x].ra_flags & RA_REQUIRED)
+				printf(" required");
+			if (rr->rr_attrs[x].ra_flags & RA_INHERIT)
+				printf(" inherit");
+			if (rr->rr_attrs[x].ra_flags & RA_RECONFIG)
+				printf(" reconfig");
+			printf(" ]");
+		}
+
+		if (rr->rr_attrs[x].ra_value)
+			printf(" default=\"%s\"\n", rr->rr_attrs[x].ra_value);
+		else
+			printf("\n");
 	}
 
 actions:
@@ -761,6 +781,18 @@ _get_rule_attrs(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base,
 		}
 
 		/*
+		   See if this can be reconfigured on the fly without a 
+		   stop/start
+		 */
+		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@reconfig",
+			 base, x);
+		if ((ret = xpath_get_one(doc,ctx,xpath))) {
+			if ((atoi(ret) != 0) || (ret[0] == 'y'))
+				flags |= RA_RECONFIG;
+			free(ret);
+		}
+
+		/*
 		   See if this is supposed to be inherited
 		 */
 		snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@inherit",
@@ -891,6 +923,10 @@ read_pipe(int fd, char **file, size_t *length)
 
 		n = read(fd, buf, sizeof(buf));
 		if (n < 0) {
+
+			if (errno == EINTR)
+				continue;
+
 			if (*file)
 				free(*file);
 			return -1;
@@ -899,7 +935,7 @@ read_pipe(int fd, char **file, size_t *length)
 		if (n == 0 && (!*length))
 			return 0;
 
-		if (n != sizeof(buf)) {
+		if (n == 0) {
 			done = 1;
 		}
 
@@ -1024,6 +1060,7 @@ load_resource_rulefile(char *filename, resource_rule_t **rules)
 			break;
 		memset(rr,0,sizeof(*rr));
 
+		rr->rr_flags = RF_INIT | RF_DESTROY;
 		rr->rr_type = type;
 		snprintf(base, sizeof(base), "/resource-agent[%d]", ruleid);
 
@@ -1035,12 +1072,13 @@ load_resource_rulefile(char *filename, resource_rule_t **rules)
 		snprintf(base, sizeof(base),
 			 "/resource-agent[%d]/special[ tag=\"rgmanager\"]",
 			 ruleid);
-		_get_root(doc, ctx, base, rr);
 		_get_maxparents(doc, ctx, base, rr);
+		_get_rule_flag(doc, ctx, base, rr, "init_on_add", RF_INIT);
+		_get_rule_flag(doc, ctx, base, rr, "destroy_on_delete", RF_DESTROY);
 		rr->rr_agent = strdup(filename);
 
 		/*
-		   Second, add the allowable-children fields
+		   Second, add the children fields
 		 */
 		_get_childtypes(doc, ctx, base, rr);
 
@@ -1095,8 +1133,9 @@ load_resource_rules(const char *rpath, resource_rule_t **rules)
 {
 	DIR *dir;
 	struct dirent *de;
-	char *fn;//, *dot;
+	char *fn, *dot;
 	char path[2048];
+	struct stat st_buf;
 
 	dir = opendir(rpath);
 	if (!dir)
@@ -1109,14 +1148,40 @@ load_resource_rules(const char *rpath, resource_rule_t **rules)
 		if (!fn)
 			continue;
 		
+		/* Ignore files with common backup extension */
 		if ((fn != NULL) && (strlen(fn) > 0) && 
 			(fn[strlen(fn)-1] == '~')) 
 			continue;
 
+		/* Ignore hidden files */
+		if (*fn == '.')
+			continue;
+
+		dot = strrchr(fn, '.');
+		if (dot) {
+			/* Ignore RPM installed save files, patches,
+			   diffs, etc. */
+			if (!strncasecmp(dot, ".rpm", 4)) {
+				fprintf(stderr, "Warning: "
+					"Ignoring %s/%s: Bad extension %s\n",
+					rpath, de->d_name, dot);
+				continue;
+			}
+		}
+
 		snprintf(path, sizeof(path), "%s/%s",
 			 rpath, de->d_name);
 
-		load_resource_rulefile(path, rules);
+		if (stat(path, &st_buf) < 0)
+			continue;
+
+		if (S_ISDIR(st_buf.st_mode))
+			continue;
+
+ 		if (st_buf.st_mode & (S_IXUSR|S_IXOTH|S_IXGRP)) {
+ 			//printf("Loading resource rule from %s\n", path);
+  			load_resource_rulefile(path, rules);
+ 		}
 	}
 	xmlCleanupParser();
 
diff --git a/rgmanager/src/daemons/restart_counter.c b/rgmanager/src/daemons/restart_counter.c
new file mode 100644
index 0000000..c889c6d
--- /dev/null
+++ b/rgmanager/src/daemons/restart_counter.c
@@ -0,0 +1,205 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/* Time-based restart counters for rgmanager */
+
+#include <stdio.h>
+#include <list.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <restart_counter.h>
+
+
+
+#define RESTART_INFO_MAGIC 0x184820ab
+
+typedef struct {
+	list_head();
+	time_t restart_time;
+} restart_item_t;
+
+typedef struct {
+	int magic;
+	time_t expire_timeout;
+	int max_restarts;
+	int restart_count;
+	restart_item_t *restart_nodes;
+} restart_info_t;
+
+
+#define VALIDATE(arg, ret) \
+do { \
+	if (!arg) {\
+		errno = EINVAL; \
+		return ret; \
+	} \
+	if (((restart_info_t *)arg)->magic != RESTART_INFO_MAGIC) {\
+		errno = EINVAL; \
+		return ret; \
+	} \
+} while(0)
+
+
+/* Remove expired restarts */
+static int
+restart_timer_purge(restart_counter_t arg, time_t now)
+{
+	restart_info_t *restarts = (restart_info_t *)arg;
+	restart_item_t *i;
+	int x, done = 0;
+
+	VALIDATE(arg, -1);
+
+	/* No timeout */
+	if (restarts->expire_timeout == 0)
+		return 0;
+
+	do {
+		done = 1;
+		list_for(&restarts->restart_nodes, i, x) {
+			if ((now - i->restart_time) >=
+			    restarts->expire_timeout) {
+				restarts->restart_count--;
+				list_remove(&restarts->restart_nodes, i);
+				done = 0;
+				break;
+			}
+		}
+	} while(!done);
+
+	return 0;
+}
+
+
+int
+restart_count(restart_counter_t arg)
+{
+	restart_info_t *restarts = (restart_info_t *)arg;
+	time_t now;
+
+	VALIDATE(arg, -1);
+	now = time(NULL);
+	restart_timer_purge(arg, now);
+	return restarts->restart_count;
+}
+
+
+int
+restart_threshold_exceeded(restart_counter_t arg)
+{
+	restart_info_t *restarts = (restart_info_t *)arg;
+	time_t now;
+
+	VALIDATE(arg, -1);
+	now = time(NULL);
+	restart_timer_purge(arg, now);
+	if (restarts->restart_count >= restarts->max_restarts)
+		return 1;
+	return 0;
+}
+
+
+/* Add a restart entry to the list.  Returns 1 if restart
+   count is exceeded */
+int
+restart_add(restart_counter_t arg)
+{
+	restart_info_t *restarts = (restart_info_t *)arg;
+	restart_item_t *i;
+	time_t t;
+
+	if (!arg)
+		/* No max restarts / threshold = always
+		   ok to restart! */
+		return 0;
+
+	VALIDATE(arg, -1);
+
+	i = malloc(sizeof(*i));
+	if (!i) {
+		return -1;
+	}
+
+	t = time(NULL);
+	i->restart_time = t;
+
+	list_insert(&restarts->restart_nodes, i);
+	restarts->restart_count++;
+
+	/* Check and remove old entries */
+	restart_timer_purge(restarts, t);
+
+	if (restarts->restart_count >= restarts->max_restarts)
+		return 1;
+
+	return 0;
+}
+
+
+int
+restart_clear(restart_counter_t arg)
+{
+	restart_info_t *restarts = (restart_info_t *)arg;
+	restart_item_t *i;
+
+	VALIDATE(arg, -1);
+	while ((i = restarts->restart_nodes)) {
+		list_remove(&restarts->restart_nodes, i);
+		free(i);
+	}
+
+	restarts->restart_count = 0;
+
+	return 0;
+}
+
+
+restart_counter_t
+restart_init(time_t expire_timeout, int max_restarts)
+{
+	restart_info_t *info;
+
+	if (max_restarts < 0) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	info = malloc(sizeof(*info));
+	if (info == NULL)
+		return NULL;
+
+	info->magic = RESTART_INFO_MAGIC;
+	info->expire_timeout = expire_timeout;
+	info->max_restarts = max_restarts;
+	info->restart_count = 0;
+	info->restart_nodes = NULL;
+
+	return (void *)info;
+}
+
+
+int
+restart_cleanup(restart_counter_t arg)
+{
+	VALIDATE(arg, -1);
+	restart_clear(arg);
+	free(arg);
+	return 0;
+}
diff --git a/rgmanager/src/daemons/restree.c b/rgmanager/src/daemons/restree.c
index 95b2450..fc9cde1 100644
--- a/rgmanager/src/daemons/restree.c
+++ b/rgmanager/src/daemons/restree.c
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2004
+  Copyright Red Hat, Inc. 2004-2006
 
   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
@@ -30,6 +30,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <list.h>
+#include <restart_counter.h>
 #include <reslist.h>
 #include <pthread.h>
 #include <clulog.h>
@@ -39,12 +40,15 @@
 void malloc_zap_mutex(void);
 #endif
 
-
 /* XXX from resrules.c */
 int store_childtype(resource_child_t **childp, char *name, int start,
 		    int stop, int forbid, int flags);
 int _res_op(resource_node_t **tree, resource_t *first, char *type,
 	    void * __attribute__((unused))ret, int op);
+static inline int
+_res_op_internal(resource_node_t **tree, resource_t *first,
+		 char *type, void *__attribute__((unused))ret, int realop,
+		 resource_node_t *node);
 void print_env(char **env);
 static inline int _res_op_internal(resource_node_t **tree, resource_t *first,
 		 char *type, void *__attribute__((unused))ret, int realop,
@@ -63,22 +67,6 @@ void * act_dup(resource_act_t *acts);
 time_t get_time(char *action, int depth, resource_node_t *node);
 
 
-const char *res_ops[] = {
-	"start",
-	"stop",
-	"status",
-	"resinfo",
-	"restart",
-	"reload",
-	"condrestart",		/* Unused */
-	"recover",		
-	"condstart",
-	"condstop",
-	"monitor",
-	"meta-data",		/* printenv */
-	"validate-all"
-};
-
 
 const char *ocf_errors[] = {
 	"success",				// 0
@@ -110,14 +98,13 @@ _no_op_mode(int arg)
 const char *
 ocf_strerror(int ret)
 {
-	if (ret < OCF_RA_MAX)
+	if (ret >= 0 && ret < OCF_RA_MAX)
 		return ocf_errors[ret];
 
 	return "unspecified";
 }
 
 
-
 /**
    Destroys an environment variable array.
 
@@ -143,7 +130,7 @@ kill_env(char **env)
    @see			build_env
  */
 static void
-add_ocf_stuff(resource_t *res, char **env, int depth)
+add_ocf_stuff(resource_t *res, char **env, int depth, int refcnt)
 {
 	char ver[10];
 	char *minor, *val;
@@ -227,6 +214,17 @@ add_ocf_stuff(resource_t *res, char **env, int depth)
 		return;
 	snprintf(val, n, "%s=%s", OCF_CHECK_LEVEL_STR, ver);
 	*env = val; env++;
+
+	/*
+	   Store the resource local refcnt (0 for now)
+	 */
+	snprintf(ver, sizeof(ver), "%d", refcnt);
+	n = strlen(OCF_REFCNT_STR) + strlen(ver) + 2;
+	val = malloc(n);
+	if (!val)
+		return;
+	snprintf(val, n, "%s=%s", OCF_REFCNT_STR, ver);
+	*env = val; env++;
 }
 
 
@@ -234,14 +232,13 @@ add_ocf_stuff(resource_t *res, char **env, int depth)
    Allocate and fill an environment variable array.
 
    @param node		Node in resource tree to use for parameters
-   @param op		Operation (start/stop/status/monitor/etc.)
    @param depth		Depth (status/monitor/etc.)
    @return		Newly allocated environment array or NULL if
    			one could not be formed.
    @see			kill_env res_exec add_ocf_stuff
  */
 static char **
-build_env(resource_node_t *node, int op, int depth)
+build_env(resource_node_t *node, int depth, int refcnt)
 {
 	resource_t *res = node->rn_resource;
 	char **env;
@@ -249,7 +246,7 @@ build_env(resource_node_t *node, int op, int depth)
 	int x, attrs, n;
 
 	for (attrs = 0; res->r_attrs && res->r_attrs[attrs].ra_name; attrs++);
-	attrs += 7; /*
+	attrs += 8; /*
 		   Leave space for:
 		   OCF_RA_VERSION_MAJOR
 		   OCF_RA_VERSION_MINOR
@@ -257,6 +254,7 @@ build_env(resource_node_t *node, int op, int depth)
 		   OCF_RESOURCE_INSTANCE
 		   OCF_RESOURCE_TYPE
 		   OCF_CHECK_LEVEL
+		   OCF_RESKEY_RGMANAGER_meta_refcnt
 		   (null terminator)
 		 */
 
@@ -296,7 +294,7 @@ build_env(resource_node_t *node, int op, int depth)
 		++attrs;
 	}
 
-	add_ocf_stuff(res, &env[attrs], depth);
+	add_ocf_stuff(res, &env[attrs], depth, refcnt);
 
 	return env;
 }
@@ -346,46 +344,35 @@ restore_signals(void)
    @see			build_env
  */
 int
-res_exec(resource_node_t *node, int op, int depth)
+res_exec(resource_node_t *node, int op, const char *arg, int depth)
 {
 	int childpid, pid;
 	int ret = 0;
 	char **env = NULL;
 	resource_t *res = node->rn_resource;
+	const char *op_str = agent_op_str(op);
 	char fullpath[2048];
 
-	if (!res->r_rule->rr_agent) {
-		clulog(LOG_DEBUG,
-		       "%s on %s \"%s\" no rr_agent\n",
-		       res_ops[op], res->r_rule->rr_type,
-		       res->r_attrs->ra_value);
+	if (!res->r_rule->rr_agent)
 		return 0;
-	}
 
 #ifdef DEBUG
-	env = build_env(node, op);
-	if (!env) {
-		clulog(LOG_DEBUG,
-		       "%s on %s \"%s\" build_env failed %d\n",
-		       res_ops[op], res->r_rule->rr_type,
-		       res->r_attrs->ra_value, errno);
+	env = build_env(node, depth, node->rn_resource->r_incarnations);
+	if (!env)
 		return -errno;
-	}
 #endif
 
 #ifdef NO_CCS
 	if (_no_op_mode_) {
-		printf("[%s] %s:%s\n", res_ops[op],
-			res->r_rule->rr_type, res->r_attrs->ra_value);
+		printf("[%s] %s:%s\n", op_str, res->r_rule->rr_type,
+		       res->r_attrs->ra_value);
 		return 0;
 	}
 #endif
 
 	childpid = fork();
-	if (childpid < 0) {
-		clulog(LOG_ERR, "%s: fork failed (%d)!\n", __func__, errno);
+	if (childpid < 0)
 		return -errno;
-	}
 
 	if (!childpid) {
 		/* Child */ 
@@ -394,21 +381,16 @@ res_exec(resource_node_t *node, int op, int depth)
 #endif
 #if 0
 		printf("Exec of script %s, action %s type %s\n",
-			res->r_rule->rr_agent, res_ops[op],
+			res->r_rule->rr_agent, agent_op_str(op),
 			res->r_rule->rr_type);
 #endif
 
 #ifndef DEBUG
-		env = build_env(node, op, depth);
+		env = build_env(node, depth, node->rn_resource->r_incarnations);
 #endif
 
-		if (!env) {
-			clulog(LOG_DEBUG,
-		       		"%s on %s \"%s\" build_env failed (ENOMEM)\n",
-		       		res_ops[op], res->r_rule->rr_type,
-		       		res->r_attrs->ra_value);
+		if (!env)
 			exit(-ENOMEM);
-		}
 
 		if (res->r_rule->rr_agent[0] != '/')
 			snprintf(fullpath, sizeof(fullpath), "%s/%s",
@@ -419,7 +401,10 @@ res_exec(resource_node_t *node, int op, int depth)
 
 		restore_signals();
 
-		execle(fullpath, fullpath, res_ops[op], NULL, env);
+		if (arg)
+			execle(fullpath, fullpath, op_str, arg, NULL, env);
+		else
+			execle(fullpath, fullpath, op_str, NULL, env);
 	}
 
 #ifdef DEBUG
@@ -436,16 +421,16 @@ res_exec(resource_node_t *node, int op, int depth)
 
 		ret = WEXITSTATUS(ret);
 
+#ifndef NO_CCS
+		if ((op == RS_STATUS &&
+		     node->rn_state == RES_STARTED && ret) ||
+		    (op != RS_STATUS && ret)) {
+#else
 		if (ret) {
+#endif
 			clulog(LOG_NOTICE,
-			       "%s on %s:%s returned %d (%s)\n",
-			       res_ops[op], res->r_rule->rr_type,
-			       res->r_attrs->ra_value, ret,
-			       ocf_strerror(ret));
-		} else {
-			clulog(LOG_DEBUG,
-			       "%s on %s:%s returned %d (%s)\n",
-			       res_ops[op], res->r_rule->rr_type,
+			       "%s on %s \"%s\" returned %d (%s)\n",
+			       op_str, res->r_rule->rr_type,
 			       res->r_attrs->ra_value, ret,
 			       ocf_strerror(ret));
 		}
@@ -456,15 +441,43 @@ res_exec(resource_node_t *node, int op, int depth)
 	if (!WIFSIGNALED(ret))
 		assert(0);
 
-	clulog(LOG_ERR,
-	       "%s on %s:%s caught signal %d\n",
-	       res_ops[op], res->r_rule->rr_type,
-	       res->r_attrs->ra_value, WTERMSIG(ret));
-
 	return -EFAULT;
 }
 
 
+static inline void
+assign_restart_policy(resource_t *curres, resource_node_t *parent,
+		      resource_node_t *node)
+{
+	char *val;
+	int max_restarts = 0;
+	time_t restart_expire_time = 0;
+
+	node->rn_restart_counter = NULL;
+
+	if (!curres || !node)
+		return;
+	if (parent) /* Non-parents don't get one for now */
+		return;
+
+	val = res_attr_value(curres, "max_restarts");
+	if (!val)
+		return;
+	max_restarts = atoi(val);
+	if (max_restarts <= 0)
+		return;
+	val = res_attr_value(curres, "restart_expire_time");
+	if (val) {
+		restart_expire_time = (time_t)expand_time(val);
+		if (!restart_expire_time)
+			return;
+	}
+
+	node->rn_restart_counter = restart_init(restart_expire_time,
+						max_restarts);
+}
+
+
 static inline int
 do_load_resource(int ccsfd, char *base,
 	         resource_rule_t *rule,
@@ -545,7 +558,21 @@ do_load_resource(int ccsfd, char *base,
 	node->rn_parent = parent;
 	node->rn_resource = curres;
 	node->rn_state = RES_STOPPED;
+	node->rn_flags = 0;
 	node->rn_actions = (resource_act_t *)act_dup(curres->r_actions);
+	assign_restart_policy(curres, parent, node);
+
+	snprintf(tok, sizeof(tok), "%s/@__independent_subtree", base);
+#ifndef NO_CCS
+	if (ccs_get(ccsfd, tok, &ref) == 0) {
+#else
+	if (conf_get(tok, &ref) == 0) {
+#endif
+		if (atoi(ref) > 0 || strcasecmp(ref, "yes") == 0)
+			node->rn_flags |= RF_INDEPENDENT;
+		free(ref);
+	}
+
 	curres->r_refs++;
 
 	*newnode = node;
@@ -685,8 +712,10 @@ build_tree(int ccsfd, resource_node_t **tree,
 			}
 		}
 		/* No resource rule matching the child?  Press on... */
-		if (!flags)
+		if (!flags) {
+			free(ref);
 			continue;
+		}
 
 		flags = 0;
 		/* Don't descend on anything we should have already picked
@@ -706,12 +735,9 @@ build_tree(int ccsfd, resource_node_t **tree,
 			break;
 		}
 
-		if (flags == 2) {
-			free(ref);
-			continue;
-		}
-
 		free(ref);
+		if (flags == 2)
+			continue;
 
 		x = 1;
 		switch(do_load_resource(ccsfd, tok, childrule, tree,
@@ -758,21 +784,13 @@ build_resource_tree(int ccsfd, resource_node_t **tree,
 		    resource_rule_t **rulelist,
 		    resource_t **reslist)
 {
-	resource_rule_t *curr;
 	resource_node_t *root = NULL;
 	char tok[512];
 
 	snprintf(tok, sizeof(tok), "%s", RESOURCE_TREE_ROOT);
 
 	/* Find and build the list of root nodes */
-	list_do(rulelist, curr) {
-
-		if (!curr->rr_root)
-			continue;
-
-		build_tree(ccsfd, &root, NULL, NULL/*curr*/, rulelist, reslist, tok);
-
-	} while (!list_done(rulelist, curr));
+	build_tree(ccsfd, &root, NULL, NULL/*curr*/, rulelist, reslist, tok);
 
 	if (root)
 		*tree = root;
@@ -797,6 +815,11 @@ destroy_resource_tree(resource_node_t **tree)
 			destroy_resource_tree(&(*tree)->rn_child);
 
 		list_remove(tree, node);
+
+		if (node->rn_restart_counter) {
+			restart_cleanup(node->rn_restart_counter);
+		}
+
 		if(node->rn_actions){
 			free(node->rn_actions);
 		}
@@ -824,6 +847,8 @@ _print_resource_tree(resource_node_t **tree, int level)
 				printf("NEEDSTART ");
 			if (node->rn_flags & RF_COMMON)
 				printf("COMMON ");
+			if (node->rn_flags & RF_INDEPENDENT)
+				printf("INDEPENDENT ");
 			printf("]");
 		}
 		printf(" {\n");
@@ -879,16 +904,17 @@ _do_child_levels(resource_node_t **tree, resource_t *first, void *ret,
 
 #if 0
 			printf("%s children of %s type %s (level %d)\n",
-			       res_ops[op],
+			       agent_op_str(op),
 			       node->rn_resource->r_rule->rr_type,
 			       rule->rr_childtypes[x].rc_name, l);
 #endif
 
 			/* Do op on all children at our level */
-			rv += _res_op(&node->rn_child, first,
+			rv |= _res_op(&node->rn_child, first,
 			     	     rule->rr_childtypes[x].rc_name, 
 		     		     ret, op);
-			if (rv != 0 && op != RS_STOP)
+
+			if (rv & SFL_FAILURE && op != RS_STOP)
 				return rv;
 		}
 
@@ -900,46 +926,6 @@ _do_child_levels(resource_node_t **tree, resource_t *first, void *ret,
 }
 
 
-#if 0
-static inline int
-_do_child_default_level(resource_node_t **tree, resource_t *first,
-			void *ret, int op)
-{
-	resource_node_t *node = *tree;
-	resource_t *res = node->rn_resource;
-	resource_rule_t *rule = res->r_rule;
-	int x, rv = 0, lev;
-
-	for (x = 0; rule->rr_childtypes &&
-	     rule->rr_childtypes[x].rc_name; x++) {
-
-		if(op == RS_STOP)
-			lev = rule->rr_childtypes[x].rc_stoplevel;
-		else
-			lev = rule->rr_childtypes[x].rc_startlevel;
-
-		if (lev)
-			continue;
-
-		/*
-		printf("%s children of %s type %s (default level)\n",
-		       res_ops[op],
-		       node->rn_resource->r_rule->rr_type,
-		       rule->rr_childtypes[x].rc_name);
-		 */
-
-		rv = _res_op(&node->rn_child, first,
-			     rule->rr_childtypes[x].rc_name, 
-			     ret, op);
-		if (rv != 0)
-			return rv;
-	}
-
-	return 0;
-}
-#endif
-
-
 static inline int
 _xx_child_internal(resource_node_t *node, resource_t *first,
 		   resource_node_t *child, void *ret, int op)
@@ -973,13 +959,14 @@ _do_child_default_level(resource_node_t **tree, resource_t *first,
 
 	if (op == RS_START || op == RS_STATUS) {
 		list_for(&node->rn_child, child, y) {
-			rv = _xx_child_internal(node, first, child, ret, op);
-			if (rv)
+			rv |= _xx_child_internal(node, first, child, ret, op);
+
+			if (rv & SFL_FAILURE)
 				return rv;
 		}
 	} else {
 		list_for_rev(&node->rn_child, child, y) {
-			rv += _xx_child_internal(node, first, child, ret, op);
+			rv |= _xx_child_internal(node, first, child, ret, op);
 		}
 	}
 
@@ -1019,26 +1006,39 @@ _res_op_by_level(resource_node_t **tree, resource_t *first, void *ret,
 		return _res_op(&node->rn_child, first, NULL, ret, op);
 
 	if (op == RS_START || op == RS_STATUS) {
-		rv =  _do_child_levels(tree, first, ret, op);
-	       	if (rv != 0)
+		rv |= _do_child_levels(tree, first, ret, op);
+	       	if (rv & SFL_FAILURE)
 			return rv;
 
 		/* Start default level after specified ones */
-		rv =  _do_child_default_level(tree, first, ret, op);
+		rv |= _do_child_default_level(tree, first, ret, op);
 
 	} /* stop */ else {
 
-		rv =  _do_child_default_level(tree, first, ret, op);
-	       	if (rv != 0)
-			return rv;
-
-		rv =  _do_child_levels(tree, first, ret, op);
+		rv |= _do_child_default_level(tree, first, ret, op);
+		rv |= _do_child_levels(tree, first, ret, op);
 	}
 
 	return rv;
 }
 
 
+void
+mark_nodes(resource_node_t *node, int state, int flags)
+{
+	int x;
+	resource_node_t *child;
+
+	list_for(&node->rn_child, child, x) {
+		if (child->rn_child)
+			mark_nodes(child->rn_child, state, flags);
+	}
+
+	node->rn_state = state;
+	node->rn_flags |= (RF_NEEDSTART | RF_NEEDSTOP);
+}
+
+
 /**
    Do a status on a resource node.  This takes into account the last time the
    status operation was run and selects the highest possible resource depth
@@ -1075,7 +1075,8 @@ do_status(resource_node_t *node)
 
 		/* Ok, it's a 'status' action. See if enough time has
 		   elapsed for a given type of status action */
-		if (delta < node->rn_actions[x].ra_interval)
+		if (delta < node->rn_actions[x].ra_interval ||
+		    !node->rn_actions[x].ra_interval)
 			continue;
 
 		if (idx == -1 ||
@@ -1090,29 +1091,22 @@ do_status(resource_node_t *node)
 		return 0;
 	}
 
+
 	node->rn_actions[idx].ra_last = now;
-	x = res_exec(node, RS_STATUS, node->rn_actions[idx].ra_depth);
+	x = res_exec(node, RS_STATUS, NULL, node->rn_actions[idx].ra_depth);
 
 	node->rn_last_status = x;
 	node->rn_last_depth = node->rn_actions[idx].ra_depth;
 	node->rn_checked = 1;
 
-	/* Clear check levels below ours. */
-	for (x=0; node->rn_actions[x].ra_name; x++) {
-		if (strcmp(node->rn_actions[x].ra_name, "status"))
-			continue;
-		if (node->rn_actions[x].ra_depth <= node->rn_last_depth)
-			node->rn_actions[x].ra_last = now;
-	}
-
-	if (node->rn_last_status == 0)
+	if (x == 0)
 		return 0;
 
 	if (!has_recover)
-		return node->rn_last_status;
+		return x;
 
 	/* Strange/failed status. Try to recover inline. */
-	if ((x = res_exec(node, RS_RECOVER, 0)) == 0)
+	if ((x = res_exec(node, RS_RECOVER, NULL, 0)) == 0)
 		return 0;
 
 	return x;
@@ -1162,8 +1156,9 @@ clear_checks(resource_node_t *node)
 {
 	time_t now;
 	int x = 0;
+	resource_t *res = node->rn_resource;
 
-	now = time(NULL);
+	now = res->r_started;
 
 	for (; node->rn_actions[x].ra_name; x++) {
 
@@ -1197,136 +1192,12 @@ clear_checks(resource_node_t *node)
 			in the subtree).
    @see			_res_op_by_level res_exec
  */
-#if 0
-int
-_res_op(resource_node_t **tree, resource_t *first,
-	char *type, void * __attribute__((unused))ret, int realop)
-{
-	int rv, me;
-	resource_node_t *node;
-	int op;
-
-	list_do(tree, node) {
-
-		/* Restore default operation. */
-		op = realop;
-
-		/* If we're starting by type, do that funky thing. */
-		if (type && strlen(type) &&
-		    strcmp(node->rn_resource->r_rule->rr_type, type))
-			continue;
-
-		/* If the resource is found, all nodes in the subtree must
-		   have the operation performed as well. */
-		me = !first || (node->rn_resource == first);
-
-		/*
-		printf("begin %s: %s %s [0x%x]\n", res_ops[op],
-		       node->rn_resource->r_rule->rr_type,
-		       primary_attr_value(node->rn_resource),
-		       node->rn_flags);
-		 */
-
-		if (me) {
-			/*
-			   If we've been marked as a node which
-			   needs to be started or stopped, clear
-			   that flag and start/stop this resource
-			   and all resource babies.
-
-			   Otherwise, don't do anything; look for
-			   children with RF_NEEDSTART and
-			   RF_NEEDSTOP flags.
-
-			   CONDSTART and CONDSTOP are no-ops if
-			   the appropriate flag is not set.
-			 */
-		       	if ((op == RS_CONDSTART) &&
-			    (node->rn_flags & RF_NEEDSTART)) {
-				/*
-				printf("Node %s:%s - CONDSTART\n",
-				       node->rn_resource->r_rule->rr_type,
-				       primary_attr_value(node->rn_resource));
-				 */
-				op = RS_START;
-			}
-
-			if ((op == RS_CONDSTOP) &&
-			    (node->rn_flags & RF_NEEDSTOP)) {
-				/*
-				printf("Node %s:%s - CONDSTOP\n",
-				       node->rn_resource->r_rule->rr_type,
-				       primary_attr_value(node->rn_resource));
-				 */
-				op = RS_STOP;
-			}
-		}
-
-		/* Start starts before children */
-		if (me && (op == RS_START)) {
-			node->rn_flags &= ~RF_NEEDSTART;
-
-			rv = res_exec(node, op, 0);
-			if (rv != 0) {
-				node->rn_state = RES_FAILED;
-				return rv;
-			}
-
-			set_time("start", 0, node);
-			clear_checks(node);
-
-			if (node->rn_state != RES_STARTED) {
-				++node->rn_resource->r_incarnations;
-				node->rn_state = RES_STARTED;
-			}
-		}
-
-		if (node->rn_child) {
-			rv = _res_op_by_level(&node, me?NULL:first, ret, op);
-			if (rv != 0)
-				return rv;
-		}
-
-		/* Stop/status/etc stops after children have stopped */
-		if (me && (op == RS_STOP)) {
-			node->rn_flags &= ~RF_NEEDSTOP;
-			rv = res_exec(node, op, 0);
-
-			if (rv != 0) {
-				node->rn_state = RES_FAILED;
-				return rv;
-			}
-
-			if (node->rn_state != RES_STOPPED) {
-				--node->rn_resource->r_incarnations;
-				node->rn_state = RES_STOPPED;
-			}
-
-		} else if (me && (op == RS_STATUS)) {
-
-			rv = do_status(node);
-			if (rv != 0)
-				return rv;
-		}
-
-		/*
-		printf("end %s: %s %s\n", res_ops[op],
-		       node->rn_resource->r_rule->rr_type,
-		       primary_attr_value(node->rn_resource));
-		 */
-	} while (!list_done(tree, node));
-
-	return 0;
-}
-#endif
-
-
 static inline int
 _res_op_internal(resource_node_t **tree, resource_t *first,
 		 char *type, void *__attribute__((unused))ret, int realop,
 		 resource_node_t *node)
 {
-	int rv, me, op;
+	int rv = 0, me, op;
 
 	/* Restore default operation. */
 	op = realop;
@@ -1378,12 +1249,18 @@ _res_op_internal(resource_node_t **tree, resource_t *first,
 
 	/* Start starts before children */
 	if (me && (op == RS_START)) {
-		node->rn_flags &= ~RF_NEEDSTART;
 
-		rv = res_exec(node, op, 0);
+		if (node->rn_flags & RF_RECONFIG &&
+		    realop == RS_CONDSTART) {
+			rv = res_exec(node, RS_RECONFIG, NULL, 0);
+			op = realop; /* reset to CONDSTART */
+		} else {
+			rv = res_exec(node, op, NULL, 0);
+		}
+		node->rn_flags &= ~(RF_NEEDSTART | RF_RECONFIG);
 		if (rv != 0) {
 			node->rn_state = RES_FAILED;
-			return rv;
+			return SFL_FAILURE;
 		}
 
 		set_time("start", 0, node);
@@ -1396,24 +1273,53 @@ _res_op_internal(resource_node_t **tree, resource_t *first,
 	} else if (me && (op == RS_STATUS)) {
 		/* Check status before children*/
 		rv = do_status(node);
-		if (rv != 0)
-			return rv;
-	}
+		if (rv != 0) {
+			/*
+			   If this node's status has failed, all of its
+			   dependent children are failed, whether or not this
+			   node is independent or not.
+			 */
+			mark_nodes(node, RES_FAILED,
+				   RF_NEEDSTART | RF_NEEDSTOP);
+
+			/* If we're an independent subtree, return a flag
+			   stating that this section is recoverable apart
+			   from siblings in the resource tree.  All child
+			   resources of this node must be restarted,
+			   but siblings of this node are not affected. */
+			if (node->rn_flags & RF_INDEPENDENT)
+				return SFL_RECOVERABLE;
+
+			return SFL_FAILURE;
+		}
 
-	if (node->rn_child) {
-		rv = _res_op_by_level(&node, me?NULL:first, ret, op);
-		if (rv != 0)
-			return rv;
 	}
 
+       if (node->rn_child) {
+                rv |= _res_op_by_level(&node, me?NULL:first, ret, op);
+
+               /* If one or more child resources are failed and at least one
+		  of them is not an independent subtree then let's check if
+		  if we are an independent subtree.  If so, mark ourself
+		  and all our children as failed and return a flag stating
+		  that this section is recoverable apart from siblings in
+		  the resource tree. */
+		if (op == RS_STATUS && (rv & SFL_FAILURE) &&
+		    (node->rn_flags & RF_INDEPENDENT)) {
+			mark_nodes(node, RES_FAILED,
+				   RF_NEEDSTART | RF_NEEDSTOP);
+			rv = SFL_RECOVERABLE;
+		}
+	}
+ 			
 	/* Stop should occur after children have stopped */
 	if (me && (op == RS_STOP)) {
 		node->rn_flags &= ~RF_NEEDSTOP;
-		rv = res_exec(node, op, 0);
+		rv = res_exec(node, op, NULL, 0);
 
 		if (rv != 0) {
 			node->rn_state = RES_FAILED;
-			return rv;
+			return SFL_FAILURE;
 		}
 
 		if (node->rn_state != RES_STOPPED) {
@@ -1426,7 +1332,7 @@ _res_op_internal(resource_node_t **tree, resource_t *first,
 	       //node->rn_resource->r_rule->rr_type,
 	       //primary_attr_value(node->rn_resource));
 	
-	return 0;
+	return rv;
 }
 
 
@@ -1452,24 +1358,31 @@ _res_op(resource_node_t **tree, resource_t *first,
 	char *type, void * __attribute__((unused))ret, int realop)
 {
   	resource_node_t *node;
- 	int count = 0, rv;
+ 	int count = 0, rv = 0;
  	
  	if (realop == RS_STOP) {
  		list_for_rev(tree, node, count) {
- 			rv = _res_op_internal(tree, first, type, ret, realop,
- 					      node);
- 			if (rv != 0) 
- 				return rv;
+ 			rv |= _res_op_internal(tree, first, type, ret, realop,
+ 					       node);
  		}
  	} else {
  		list_for(tree, node, count) {
- 			rv = _res_op_internal(tree, first, type, ret, realop,
- 					      node);
- 			if (rv != 0) 
+ 			rv |= _res_op_internal(tree, first, type, ret, realop,
+ 					       node);
+
+			/* If we hit a problem during a 'status' op in an
+			   independent subtree, rv will have the
+			   SFL_RECOVERABLE bit set, but not SFL_FAILURE.
+			   If we ever hit SFL_FAILURE during a status
+			   operation, we're *DONE* - even if the subtree
+			   is flagged w/ indy-subtree */
+			  
+ 			if (rv & SFL_FAILURE) 
  				return rv;
  		}
  	}
-	return 0;
+
+	return rv;
 }
 
 /**
@@ -1564,6 +1477,7 @@ int
 resource_delta(resource_t **leftres, resource_t **rightres)
 {
 	resource_t *lc, *rc;
+	int ret;
 
 	list_do(leftres, lc) {
 		rc = find_resource_by_ref(rightres, lc->r_rule->rr_type,
@@ -1576,10 +1490,25 @@ resource_delta(resource_t **leftres, resource_t **rightres)
 		}
 
 		/* Ok, see if the resource is the same */
-		if (rescmp(lc, rc) == 0) {
+		ret = rescmp(lc, rc);
+		if (ret	== 0) {
+			rc->r_flags |= RF_COMMON;
+			continue;
+		}
+
+		if (ret == 2) {
+			/* return of 2 from rescmp means
+			   the two resources differ only 
+			   by reconfigurable bits */
+			/* Do nothing on condstop phase;
+			   do a "reconfig" instead of 
+			   "start" on conststart phase */
 			rc->r_flags |= RF_COMMON;
+			rc->r_flags |= RF_NEEDSTART;
+			rc->r_flags |= RF_RECONFIG;
 			continue;
 		}
+
 		rc->r_flags |= RF_COMMON;
 
 		/* Resource has changed.  Flag it. */
@@ -1641,12 +1570,17 @@ resource_tree_delta(resource_node_t **ltree, resource_node_t **rtree)
 			   or is new), then we don't really care about its
 			   children.
 			 */
+
 			if (rn->rn_resource->r_flags & RF_NEEDSTART) {
 				rn->rn_flags |= RF_NEEDSTART;
-				continue;
+				if ((rn->rn_resource->r_flags & RF_RECONFIG) == 0)
+					continue;
 			}
 
-			if (rc == 0) {
+			if (rc == 0 || rc == 2) {
+				if (rc == 2)
+					rn->rn_flags |= RF_NEEDSTART | RF_RECONFIG;
+
 				/* Ok, same resource.  Recurse. */
 				ln->rn_flags |= RF_COMMON;
 				rn->rn_flags |= RF_COMMON;
diff --git a/rgmanager/src/daemons/rg_event.c b/rgmanager/src/daemons/rg_event.c
new file mode 100644
index 0000000..f614668
--- /dev/null
+++ b/rgmanager/src/daemons/rg_event.c
@@ -0,0 +1,573 @@
+/*
+  Copyright Red Hat, Inc. 2006-2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+#include <resgroup.h>
+#include <rg_locks.h>
+#include <gettid.h>
+#include <assert.h>
+#include <libcman.h>
+#include <ccs.h>
+#include <clulog.h>
+#include <lock.h>
+#include <event.h>
+#include <stdint.h>
+#include <vf.h>
+#include <members.h>
+
+
+/**
+ * resource group event queue.
+ */
+static event_t *event_queue = NULL;
+#ifdef WRAP_LOCKS
+static pthread_mutex_t event_queue_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t mi_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+#else
+static pthread_mutex_t event_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mi_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+static pthread_t event_thread = 0;
+static int transition_throttling = 5;
+static int central_events = 0;
+
+extern int running;
+extern int shutdown_pending;
+static int _master = 0;
+static struct dlm_lksb _master_lock;
+static int _xid = 0;
+static event_master_t *mi = NULL;
+
+void hard_exit(void);
+int init_resource_groups(int);
+void flag_shutdown(int sig);
+void flag_reconfigure(int sig);
+
+event_table_t *master_event_table = NULL;
+
+
+void
+set_transition_throttling(int nsecs)
+{
+	if (nsecs < 0)
+		nsecs = 0;
+	transition_throttling = nsecs;
+}
+
+
+void
+set_central_events(int flag)
+{
+	central_events = flag;
+}
+
+
+int
+central_events_enabled(void)
+{
+	return central_events;
+}
+
+
+/**
+  Called to handle the transition of a cluster member from up->down or
+  down->up.  This handles initializing services (in the local node-up case),
+  exiting due to loss of quorum (local node-down), and service fail-over
+  (remote node down).  This is the distributed node event processor;
+  for the local-only node event processor, see slang_event.c
+ 
+  @param nodeID		ID of the member which has come up/gone down.
+  @param nodeStatus		New state of the member in question.
+  @see eval_groups
+ */
+void
+node_event(int local, int nodeID, int nodeStatus, int clean)
+{
+	if (!running)
+		return;
+
+	if (local) {
+
+		/* Local Node Event */
+		if (nodeStatus == 0) {
+			clulog(LOG_ERR, "Exiting uncleanly\n");
+			hard_exit();
+		}
+
+		if (!rg_initialized()) {
+			if (init_resource_groups(0) != 0) {
+				clulog(LOG_ERR,
+				       "#36: Cannot initialize services\n");
+				hard_exit();
+			}
+		}
+
+		if (shutdown_pending) {
+			clulog(LOG_NOTICE, "Processing delayed exit signal\n");
+			running = 0;
+			return;
+		}
+		setup_signal(SIGINT, flag_shutdown);
+		setup_signal(SIGTERM, flag_shutdown);
+		setup_signal(SIGHUP, flag_reconfigure);
+
+		eval_groups(1, nodeID, 1);
+		return;
+	}
+
+	/*
+	 * Nothing to do for events from other nodes if we are not ready.
+	 */
+	if (!rg_initialized()) {
+		clulog(LOG_DEBUG, "Services not initialized.\n");
+		return;
+	}
+
+	eval_groups(0, nodeID, nodeStatus);
+}
+
+
+/**
+   Query CCS to see whether a node has fencing enabled or not in
+   the configuration.  This does not check to see if it's in the
+   fence domain.
+ */
+int
+node_has_fencing(int nodeid)
+{
+	int ccs_desc;
+	char *val = NULL;
+	char buf[1024];
+	int ret = 1;
+	
+	ccs_desc = ccs_connect();
+	if (ccs_desc < 0) {
+		clulog(LOG_ERR, "Unable to connect to ccsd; cannot handle"
+		       " node event!\n");
+		/* Assume node has fencing */
+		return 1;
+	}
+
+	snprintf(buf, sizeof(buf), 
+		 "/cluster/clusternodes/clusternode[ nodeid=\"%d\"]"
+		 "/fence/method/device/@name", nodeid);
+
+	if (ccs_get(ccs_desc, buf, &val) != 0)
+		ret = 0;
+	if (val) 
+		free(val);
+	ccs_disconnect(ccs_desc);
+	return ret;
+}
+
+
+/**
+   Quick query to cman to see if a node has been fenced.
+ */
+int
+node_fenced(int nodeid)
+{
+	cman_handle_t ch;
+	int fenced = 0;
+	uint64_t fence_time;
+
+	ch = cman_init(NULL);
+	if (cman_get_fenceinfo(ch, nodeid, &fence_time, &fenced, NULL) < 0)
+		fenced = 0;
+
+	cman_finish(ch);
+
+	return fenced;
+}
+
+
+/**
+   Callback from view-formation when a commit occurs for the Transition-
+   Master key.
+ */
+int32_t
+master_event_callback(char *key, uint64_t viewno,
+		      void *data, uint32_t datalen)
+{
+	event_master_t *m;
+
+	m = data;
+	if (datalen != (uint32_t)sizeof(*m)) {
+		clulog(LOG_ERR, "%s: wrong size\n", __FUNCTION__);
+		return 1;
+	}
+
+	swab_event_master_t(m);
+	if (m->m_magic != EVENT_MASTER_MAGIC) {
+		clulog(LOG_ERR, "%s: wrong size\n", __FUNCTION__);
+		return 1;
+	}
+
+	if (m->m_nodeid == my_id())
+		clulog(LOG_DEBUG, "Master Commit: I am master\n");
+	else 
+		clulog(LOG_DEBUG, "Master Commit: %d is master\n", m->m_nodeid);
+
+	pthread_mutex_lock(&mi_mutex);
+	if (mi)
+		free(mi);
+	mi = m;
+	pthread_mutex_unlock(&mi_mutex);
+
+	return 0;
+}
+
+
+/**
+  Read the Transition-Master key from vf if it exists.  If it doesn't,
+  attempt to become the transition-master.
+ */
+static int
+find_master(void)
+{
+	event_master_t *masterinfo = NULL;
+	void *data;
+	uint32_t sz;
+	cluster_member_list_t *m;
+	uint64_t vn;
+	int master_id = -1;
+
+	m = member_list();
+	if (vf_read(m, "Transition-Master", &vn,
+		    (void **)(&data), &sz) < 0) {
+		clulog(LOG_ERR, "Unable to discover master"
+		       " status\n");
+		masterinfo = NULL;
+	} else {
+		masterinfo = (event_master_t *)data;
+	}
+	free_member_list(m);
+
+	if (masterinfo && (sz >= sizeof(*masterinfo))) {
+		swab_event_master_t(masterinfo);
+		if (masterinfo->m_magic == EVENT_MASTER_MAGIC) {
+			clulog(LOG_DEBUG, "Master Locate: %d is master\n",
+			       masterinfo->m_nodeid);
+			pthread_mutex_lock(&mi_mutex);
+			if (mi)
+				free(mi);
+			mi = masterinfo;
+			pthread_mutex_unlock(&mi_mutex);
+			master_id = masterinfo->m_nodeid;
+		}
+	}
+
+	return master_id;
+}
+
+
+/**
+  Return a copy of the cached event_master_t structure to the
+  caller.
+ */
+int
+event_master_info_cached(event_master_t *mi_out)
+{
+	if (!central_events || !mi_out) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	pthread_mutex_lock(&mi_mutex);
+	if (!mi) {
+		pthread_mutex_unlock(&mi_mutex);
+		errno = -ENOENT;
+		return -1;
+	}
+
+	memcpy(mi_out, mi, sizeof(*mi));
+	pthread_mutex_unlock(&mi_mutex);
+	return 0;
+}
+
+
+/**
+  Return the node ID of the master.  If none exists, become
+  the master and return our own node ID.
+ */
+int
+event_master(void)
+{
+	cluster_member_list_t *m = NULL;
+	event_master_t masterinfo;
+	int master_id = -1;
+
+	/* We hold this forever. */
+	if (_master)
+		return my_id();
+
+	m = member_list();
+	pthread_mutex_lock(&mi_mutex);
+
+	if (mi) {
+		master_id = mi->m_nodeid;
+		pthread_mutex_unlock(&mi_mutex);
+		if (memb_online(m, master_id)) {
+			//clulog(LOG_DEBUG, "%d is master\n", mi->m_nodeid);
+			goto out;
+		}
+	}
+
+	pthread_mutex_unlock(&mi_mutex);
+
+	memset(&_master_lock, 0, sizeof(_master_lock));
+	if (clu_lock(LKM_EXMODE, &_master_lock, LKF_NOQUEUE,
+		     "Transition-Master") < 0) {
+		/* not us, find out who is master */
+		master_id = find_master();
+		goto out;
+	}
+
+	if (_master_lock.sb_status != 0) {
+		master_id = -1;
+		goto out;
+	}
+
+	_master = 1;
+
+	memset(&masterinfo, 0, sizeof(masterinfo));
+	masterinfo.m_magic = EVENT_MASTER_MAGIC;
+	masterinfo.m_nodeid = my_id();
+	masterinfo.m_master_time = (uint64_t)time(NULL);
+	swab_event_master_t(&masterinfo);
+
+	if (vf_write(m, VFF_IGN_CONN_ERRORS | VFF_RETRY,
+		     "Transition-Master", &masterinfo,
+		     sizeof(masterinfo)) < 0) {
+		clulog(LOG_ERR, "Unable to advertise master"
+		       " status to all nodes\n");
+	}
+
+	master_id = my_id();
+out:
+	free_member_list(m);
+	return master_id;
+}
+
+
+
+void group_event(char *name, uint32_t state, int owner);
+
+/**
+  Event handling function.  This only stays around as long as
+  events are on the queue.
+ */
+void *
+_event_thread_f(void *arg)
+{
+	event_t *ev;
+	int notice = 0, count = 0;
+
+	while (1) {
+		pthread_mutex_lock(&event_queue_mutex);
+		ev = event_queue;
+		if (ev)
+			list_remove(&event_queue, ev);
+		else
+			break; /* We're outta here */
+
+		++count;
+		/* Event thread usually doesn't hang around.  When it's
+	   	   spawned, sleep for this many seconds in order to let
+	   	   some events queue up */
+		if ((count==1) && transition_throttling && !central_events)
+			sleep(transition_throttling);
+
+		pthread_mutex_unlock(&event_queue_mutex);
+
+		if (ev->ev_type == EVENT_CONFIG) {
+			/*
+			clulog(LOG_NOTICE, "Config Event: %d -> %d\n",
+			       ev->ev.config.cfg_oldversion,
+			       ev->ev.config.cfg_version);
+			 */
+			init_resource_groups(1);
+			free(ev);
+			continue;
+		}
+
+		if (central_events) {
+			/* If the master node died or there isn't
+			   one yet, take the master lock. */
+			if (event_master() == my_id()) {
+				slang_process_event(master_event_table,
+						    ev);
+			} 
+			free(ev);
+			continue;
+			/* ALL OF THE CODE BELOW IS DISABLED
+			   when using central_events */
+		}
+
+		if (ev->ev_type == EVENT_RG) {
+			/*
+			clulog(LOG_NOTICE, "RG Event: %s %s %d\n",
+			       ev->ev.group.rg_name,
+			       rg_state_str(ev->ev.group.rg_state),
+			       ev->ev.group.rg_owner);
+			 */
+			group_event(ev->ev.group.rg_name,
+				    ev->ev.group.rg_state,
+				    ev->ev.group.rg_owner);
+		} else if (ev->ev_type == EVENT_NODE) {
+			/*
+			clulog(LOG_NOTICE, "Node Event: %s %d %s %s\n",
+			       ev->ev.node.ne_local?"Local":"Remote",
+			       ev->ev.node.ne_nodeid,
+			       ev->ev.node.ne_state?"UP":"DOWN",
+			       ev->ev.node.ne_clean?"Clean":"Dirty")
+			 */
+
+			if (ev->ev.node.ne_state == 0 &&
+			    !ev->ev.node.ne_clean &&
+			    node_has_fencing(ev->ev.node.ne_nodeid)) {
+				notice = 0;
+				while (!node_fenced(ev->ev.node.ne_nodeid)) {
+					if (!notice) {
+						notice = 1;
+						clulog(LOG_INFO, "Waiting for "
+						       "node #%d to be fenced\n",
+						       ev->ev.node.ne_nodeid);
+					}
+					sleep(2);
+				}
+
+				if (notice)
+					clulog(LOG_INFO, "Node #%d fenced; "
+					       "continuing\n",
+					       ev->ev.node.ne_nodeid);
+			}
+
+			node_event(ev->ev.node.ne_local,
+				   ev->ev.node.ne_nodeid,
+				   ev->ev.node.ne_state,
+				   ev->ev.node.ne_clean);
+		}
+
+		free(ev);
+	}
+
+	if (!central_events || _master) {
+		clulog(LOG_DEBUG, "%d events processed\n", count);
+	}
+	/* Mutex held */
+	event_thread = 0;
+	pthread_mutex_unlock(&event_queue_mutex);
+	pthread_exit(NULL);
+}
+
+
+static void
+insert_event(event_t *ev)
+{
+	pthread_attr_t attrs;
+	pthread_mutex_lock (&event_queue_mutex);
+	ev->ev_transaction = ++_xid;
+	list_insert(&event_queue, ev);
+	if (event_thread == 0) {
+        	pthread_attr_init(&attrs);
+        	pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED);
+        	pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
+		pthread_attr_setstacksize(&attrs, 262144);
+
+		pthread_create(&event_thread, &attrs, _event_thread_f, NULL);
+        	pthread_attr_destroy(&attrs);
+	}
+	pthread_mutex_unlock (&event_queue_mutex);
+}
+
+
+static event_t *
+new_event(void)
+{
+	event_t *ev;
+
+	while (1) {
+		ev = malloc(sizeof(*ev));
+		if (ev) {
+			break;
+		}
+		sleep(1);
+	}
+	memset(ev,0,sizeof(*ev));
+	ev->ev_type = EVENT_NONE;
+
+	return ev;
+}
+
+
+void
+rg_event_q(char *name, uint32_t state, int owner, int last)
+{
+	event_t *ev = new_event();
+
+	ev->ev_type = EVENT_RG;
+
+	strncpy(ev->ev.group.rg_name, name, 128);
+	ev->ev.group.rg_state = state;
+	ev->ev.group.rg_owner = owner;
+	ev->ev.group.rg_last_owner = last;
+
+	insert_event(ev);
+}
+
+
+void
+node_event_q(int local, int nodeID, int state, int clean)
+{
+	event_t *ev = new_event();
+
+	ev->ev_type = EVENT_NODE;
+	ev->ev.node.ne_state = state;
+	ev->ev.node.ne_local = local;
+	ev->ev.node.ne_nodeid = nodeID;
+	ev->ev.node.ne_clean = clean;
+	insert_event(ev);
+}
+
+
+void
+config_event_q(int old_version, int new_version)
+{
+	event_t *ev = new_event();
+
+	ev->ev_type = EVENT_CONFIG;
+	ev->ev.config.cfg_version = new_version;
+	ev->ev.config.cfg_oldversion = old_version;
+	insert_event(ev);
+}
+
+void
+user_event_q(char *svc, int request,
+	     int arg1, int arg2, int target, msgctx_t *ctx)
+{
+	event_t *ev = new_event();
+
+	ev->ev_type = EVENT_USER;
+	strncpy(ev->ev.user.u_name, svc, sizeof(ev->ev.user.u_name));
+	ev->ev.user.u_request = request;
+	ev->ev.user.u_arg1 = arg1;
+	ev->ev.user.u_arg2 = arg2;
+	ev->ev.user.u_target = target;
+	ev->ev.user.u_ctx = ctx;
+	insert_event(ev);
+}
+
diff --git a/rgmanager/src/daemons/service_op.c b/rgmanager/src/daemons/service_op.c
new file mode 100644
index 0000000..6ee636a
--- /dev/null
+++ b/rgmanager/src/daemons/service_op.c
@@ -0,0 +1,204 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+#include <assert.h>
+#include <platform.h>
+#include <message.h>
+#include <members.h>
+#include <stdio.h>
+#include <string.h>
+#include <resgroup.h>
+#include <clulog.h>
+#include <lock.h>
+#include <rg_locks.h>
+#include <ccs.h>
+#include <rg_queue.h>
+#include <msgsimple.h>
+#include <res-ocf.h>
+#include <event.h>
+
+
+/*
+ * Send a message to the target node to start the service.
+ */
+int svc_start_remote(char *svcName, int request, uint32_t target);
+void svc_report_failure(char *);
+int get_service_state_internal(char *svcName, rg_state_t *svcStatus);
+
+
+/**
+ *
+ */
+int
+service_op_start(char *svcName,
+		 int *target_list,
+		 int target_list_len,
+		 int *new_owner)
+{
+	int target;
+	int ret, x;
+	int excl = 0, dep = 0, fail = 0;
+	rg_state_t svcStatus;
+	
+	if (get_service_state_internal(svcName, &svcStatus) < 0) {
+		return RG_EFAIL;
+	}
+
+	if (svcStatus.rs_state == RG_STATE_FAILED ||
+	    svcStatus.rs_state == RG_STATE_UNINITIALIZED)
+		return RG_EINVAL;
+
+	for (x = 0; x < target_list_len; x++) {
+
+		target = target_list[x];
+		ret = svc_start_remote(svcName, RG_START_REMOTE,
+				       target);
+		switch (ret) {
+		case RG_ERUN:
+			/* Someone stole the service while we were 
+			   trying to start it */
+			get_rg_state_local(svcName, &svcStatus);
+			if (new_owner)
+				*new_owner = svcStatus.rs_owner;
+			return 0;
+		case RG_EEXCL:
+			++excl;
+			continue;
+		case RG_EDEPEND:
+			++dep;
+			continue;
+		case RG_EFAIL:
+			++fail;
+			continue;
+		case RG_EABORT:
+			svc_report_failure(svcName);
+			return RG_EFAIL;
+		default:
+			/* deliberate fallthrough */
+			clulog(LOG_ERR,
+			       "#61: Invalid reply from member %d during"
+			       " start operation!\n", target);
+		case RG_NO:
+			/* state uncertain */
+			clulog(LOG_CRIT, "State Uncertain: svc:%s "
+			       "nid:%d req:%s ret:%d\n", svcName,
+			       target, rg_req_str(RG_START_REMOTE), ret);
+			return 0;
+		case 0:
+			if (new_owner)
+				*new_owner = target;
+			clulog(LOG_NOTICE, "Service %s is now running "
+			       "on member %d\n", svcName, (int)target);
+			return 0;
+		}
+	}
+
+	ret = RG_EFAIL;
+	if (excl == target_list_len) 
+		ret = RG_EEXCL;
+	else if (dep == target_list_len)
+		ret = RG_EDEPEND;
+
+	clulog(LOG_INFO, "Start failed; node reports: %d failures, "
+	       "%d exclusive, %d dependency errors\n", fail, excl, dep);
+	return ret;
+}
+
+
+int
+service_op_stop(char *svcName, int do_disable, int event_type)
+{
+	SmMessageSt msg;
+	int msg_ret;
+	msgctx_t ctx;
+	rg_state_t svcStatus;
+	int msgtarget = my_id();
+
+	/* Build the message header */
+	msg.sm_hdr.gh_magic = GENERIC_HDR_MAGIC;
+	msg.sm_hdr.gh_command = RG_ACTION_REQUEST;
+	msg.sm_hdr.gh_arg1 = RG_ACTION_MASTER; 
+	msg.sm_hdr.gh_length = sizeof (SmMessageSt);
+
+	msg.sm_data.d_action = ((!do_disable) ? RG_STOP:RG_DISABLE);
+
+	if (msg.sm_data.d_action == RG_STOP && event_type == EVENT_USER)
+		msg.sm_data.d_action = RG_STOP_USER;
+
+	strncpy(msg.sm_data.d_svcName, svcName,
+		sizeof(msg.sm_data.d_svcName));
+	msg.sm_data.d_ret = 0;
+	msg.sm_data.d_svcOwner = 0;
+
+	/* Open a connection to the local node - it will decide what to
+	   do in this case. XXX inefficient; should queue requests
+	   locally and immediately forward requests otherwise */
+
+	if (get_service_state_internal(svcName, &svcStatus) < 0)
+		return RG_EFAIL;
+	if (svcStatus.rs_owner > 0)
+		msgtarget = svcStatus.rs_owner;
+
+	if (msg_open(MSG_CLUSTER, msgtarget, RG_PORT, &ctx, 2)< 0) {
+		clulog(LOG_ERR,
+		       "#58: Failed opening connection to member #%d\n",
+		       my_id());
+		return -1;
+	}
+
+	/* Encode */
+	swab_SmMessageSt(&msg);
+
+	/* Send stop message to the other node */
+	if (msg_send(&ctx, &msg, sizeof (SmMessageSt)) < 
+	    (int)sizeof (SmMessageSt)) {
+		clulog(LOG_ERR, "Failed to send complete message\n");
+		msg_close(&ctx);
+		return -1;
+	}
+
+	/* Check the response */
+	do {
+		msg_ret = msg_receive(&ctx, &msg,
+				      sizeof (SmMessageSt), 10);
+		if ((msg_ret == -1 && errno != ETIMEDOUT) ||
+		    (msg_ret > 0)) {
+			break;
+		}
+	} while(1);
+
+	if (msg_ret != sizeof (SmMessageSt)) {
+		clulog(LOG_WARNING, "Strange response size: %d vs %d\n",
+		       msg_ret, (int)sizeof(SmMessageSt));
+		return 0;	/* XXX really UNKNOWN */
+	}
+
+	/* Got a valid response from other node. */
+	msg_close(&ctx);
+
+	/* Decode */
+	swab_SmMessageSt(&msg);
+
+	return msg.sm_data.d_ret;
+}
+
+
+/*
+   TODO
+   service_op_migrate()
+ */
+
diff --git a/rgmanager/src/daemons/slang_event.c b/rgmanager/src/daemons/slang_event.c
new file mode 100644
index 0000000..92284b5
--- /dev/null
+++ b/rgmanager/src/daemons/slang_event.c
@@ -0,0 +1,1264 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as published
+  by the Free Software Foundation.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/**
+  @file S/Lang event handling & intrinsic functions + vars
+ */
+#include <platform.h>
+#include <resgroup.h>
+#include <list.h>
+#include <restart_counter.h>
+#include <reslist.h>
+#include <clulog.h>
+#include <members.h>
+#include <assert.h>
+#include <event.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <slang.h>
+#include <sys/syslog.h>
+#include <malloc.h>
+#include <clulog.h>
+#include <sets.h>
+
+static int __sl_initialized = 0;
+
+static char **_service_list = NULL;
+static int _service_list_len = 0;
+
+char **get_service_names(int *len); /* from groups.c */
+int get_service_property(char *rg_name, char *prop, char *buf, size_t buflen);
+void push_int_array(int *stuff, int len);
+
+
+/* ================================================================
+ * Node states 
+ * ================================================================ */
+static const int
+   _ns_online = 1,
+   _ns_offline = 0;
+
+/* ================================================================
+ * Event information 
+ * ================================================================ */
+static const int
+   _ev_none = EVENT_NONE,
+   _ev_node = EVENT_NODE,
+   _ev_service = EVENT_RG,
+   _ev_config = EVENT_CONFIG,
+   _ev_user = EVENT_USER;
+
+static const int
+   _rg_fail = RG_EFAIL,
+   _rg_success = RG_ESUCCESS,
+   _rg_edomain = RG_EDOMAIN,
+   _rg_edepend = RG_EDEPEND,
+   _rg_eabort = RG_EABORT,
+   _rg_einval = RG_EINVAL,
+   _rg_erun = RG_ERUN;
+
+static int
+   _stop_processing = 0,
+   _my_node_id = 0,
+   _node_state = 0,
+   _node_id = 0,
+   _node_clean = 0,
+   _service_owner = 0,
+   _service_last_owner = 0,
+   _service_restarts_exceeded = 0,
+   _user_request = 0,
+   _user_arg1 = 0,
+   _user_arg2 = 0,
+   _user_return = 0,
+   _rg_err = 0,
+   _event_type = 0;
+
+static char
+   *_node_name = NULL,
+   *_service_name = NULL,
+   *_service_state = NULL,
+   *_rg_err_str = "No Error";
+
+static int
+   _user_enable = RG_ENABLE,
+   _user_disable = RG_DISABLE,
+   _user_stop = RG_STOP_USER,		/* From clusvcadm */
+   _user_relo = RG_RELOCATE,
+   _user_restart = RG_RESTART,
+   _user_migrate = RG_MIGRATE;
+
+
+SLang_Intrin_Var_Type rgmanager_vars[] =
+{
+	/* Log levels (constants) */
+
+	/* Node state information */
+	MAKE_VARIABLE("NODE_ONLINE",	&_ns_online,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("NODE_OFFLINE",	&_ns_offline,	SLANG_INT_TYPE, 1),
+
+	/* Node event information */
+	MAKE_VARIABLE("node_self",	&_my_node_id,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("node_state",	&_node_state,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("node_id",	&_node_id,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("node_name",	&_node_name,	SLANG_STRING_TYPE,1),
+	MAKE_VARIABLE("node_clean",	&_node_clean,	SLANG_INT_TYPE, 1),
+
+	/* Service event information */
+	MAKE_VARIABLE("service_name",	&_service_name,	SLANG_STRING_TYPE,1),
+	MAKE_VARIABLE("service_state",	&_service_state,SLANG_STRING_TYPE,1),
+	MAKE_VARIABLE("service_owner",	&_service_owner,SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("service_last_owner", &_service_last_owner,
+		      					SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("service_restarts_exceeded", &_service_restarts_exceeded,
+		      					SLANG_INT_TYPE, 1),
+
+	/* User event information */
+	MAKE_VARIABLE("user_request",	&_user_request,	SLANG_INT_TYPE,1),
+	MAKE_VARIABLE("user_arg1",	&_user_arg1,	SLANG_INT_TYPE,1),
+	MAKE_VARIABLE("user_arg2",	&_user_arg2,	SLANG_INT_TYPE,1),
+	MAKE_VARIABLE("user_service",	&_service_name, SLANG_STRING_TYPE,1),
+	MAKE_VARIABLE("user_target",	&_service_owner,SLANG_INT_TYPE, 1),
+	/* Return code to user requests; i.e. clusvcadm */
+	MAKE_VARIABLE("user_return",	&_user_return,	SLANG_INT_TYPE, 0),
+
+	/* General event information */
+	MAKE_VARIABLE("event_type",	&_event_type,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("EVENT_NONE",	&_ev_none,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("EVENT_NODE",	&_ev_node,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("EVENT_CONFIG",	&_ev_config,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("EVENT_SERVICE",	&_ev_service,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("EVENT_USER",	&_ev_user,	SLANG_INT_TYPE, 1),
+
+	/* User request constants */
+	MAKE_VARIABLE("USER_ENABLE",	&_user_enable,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("USER_DISABLE",	&_user_disable,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("USER_STOP",	&_user_stop,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("USER_RELOCATE",	&_user_relo,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("USER_RESTART",	&_user_restart,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("USER_MIGRATE",	&_user_migrate,	SLANG_INT_TYPE, 1),
+
+	/* Errors */
+	MAKE_VARIABLE("rg_error",	&_rg_err,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("rg_error_string",&_rg_err_str,	SLANG_STRING_TYPE,1),
+
+	/* From constants.c */
+	MAKE_VARIABLE("FAIL",		&_rg_fail,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("SUCCESS",	&_rg_success,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("ERR_ABORT",	&_rg_eabort,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("ERR_INVALID",	&_rg_einval,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("ERR_DEPEND",	&_rg_edepend,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("ERR_DOMAIN",	&_rg_edomain,	SLANG_INT_TYPE, 1),
+	MAKE_VARIABLE("ERR_RUNNING",	&_rg_erun,	SLANG_INT_TYPE, 1),
+
+	SLANG_END_INTRIN_VAR_TABLE
+};
+
+
+#define rg_error(errortype) \
+do { \
+	_rg_err = errortype; \
+	_rg_err_str = ##errortype; \
+} while(0)
+
+
+int
+get_service_state_internal(char *svcName, rg_state_t *svcStatus)
+{
+	struct dlm_lksb lock;
+	char buf[32];
+
+	get_rg_state_local(svcName, svcStatus);
+	if (svcStatus->rs_state == RG_STATE_UNINITIALIZED) {
+		if (rg_lock(svcName, &lock) < 0) {
+			errno = ENOLCK;
+			return -1;
+		}
+
+		if (get_rg_state(svcName, svcStatus) < 0) {
+			errno = ENOENT;
+			rg_unlock(&lock);
+			return -1;
+		}
+
+		/* We got a copy from another node - don't flip the state */
+		if (svcStatus->rs_transition) {
+			rg_unlock(&lock);
+			return 0;
+		}
+
+		/* Finish initializing the service state */
+		svcStatus->rs_transition = (uint64_t)time(NULL);
+
+		if (get_service_property(svcName, "autostart",
+					 buf, sizeof(buf)) == 0) {
+			if (buf[0] == '0' || !strcasecmp(buf, "no")) {
+				svcStatus->rs_state = RG_STATE_DISABLED;
+			} else {
+				svcStatus->rs_state = RG_STATE_STOPPED;
+			}
+		}
+
+		set_rg_state(svcName, svcStatus);
+
+		rg_unlock(&lock);
+	}
+
+	return 0;
+}
+
+
+/*
+   (restarts, last_owner, owner, state) = get_service_status(servicename)
+ */
+void
+sl_service_status(char *svcName)
+{
+	rg_state_t svcStatus;
+	int restarts_exceeded = 0;
+	char *state_str;
+
+	if (get_service_state_internal(svcName, &svcStatus) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to get status for %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	restarts_exceeded = check_restart(svcName);
+	if (SLang_push_integer(restarts_exceeded) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to push restarts_exceeded %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	if (SLang_push_integer(svcStatus.rs_restarts) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to push restarts for %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	if (SLang_push_integer(svcStatus.rs_last_owner) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to push last owner of %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	switch(svcStatus.rs_state) {
+	case RG_STATE_DISABLED:
+	case RG_STATE_STOPPED:
+	case RG_STATE_FAILED:
+	case RG_STATE_RECOVER:
+	case RG_STATE_ERROR:
+		/* There is no owner for these states.  Ever.  */
+		svcStatus.rs_owner = -1;
+	}
+
+	if (SLang_push_integer(svcStatus.rs_owner) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to push owner of %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	state_str = strdup(rg_state_str(svcStatus.rs_state));
+	if (!state_str) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to duplicate state of %s",
+			     __FUNCTION__,
+			     svcName);
+		return;
+	}
+
+	if (SLang_push_malloced_string(state_str) < 0) {
+		SLang_verror(SL_RunTime_Error,
+			     "%s: Failed to push state of %s",
+			     __FUNCTION__,
+			     svcName);
+		free(state_str);
+	}
+}
+
+
+/**
+  (nofailback, restricted, ordered, nodelist) = service_domain_info(svcName);
+ */
+void
+sl_domain_info(char *svcName)
+{
+	int *nodelist = NULL, listlen;
+	char buf[64];
+	int flags = 0;
+
+	if (get_service_property(svcName, "domain", buf, sizeof(buf)) < 0) {
+		/* no nodes */
+		SLang_push_integer(0);
+
+		/* no domain? */
+/*
+		str = strdup("none");
+		if (SLang_push_malloced_string(str) < 0) {
+			free(state_str);
+			return;
+		}
+*/
+
+		/* not ordered */
+		SLang_push_integer(0);
+		/* not restricted */
+		SLang_push_integer(0);
+		/* nofailback not set */
+		SLang_push_integer(0);
+	}
+
+	if (node_domain_set_safe(buf, &nodelist, &listlen, &flags) < 0) {
+		SLang_push_integer(0);
+		SLang_push_integer(0);
+		SLang_push_integer(0);
+		SLang_push_integer(0);
+		return;
+	}
+
+	SLang_push_integer(!!(flags & FOD_NOFAILBACK));
+	SLang_push_integer(!!(flags & FOD_RESTRICTED));
+	SLang_push_integer(!!(flags & FOD_ORDERED));
+
+	push_int_array(nodelist, listlen);
+	free(nodelist);
+
+/*
+	str = strdup(buf);
+	if (SLang_push_malloced_string(str) < 0) {
+		free(state_str);
+		return;
+	}
+*/
+}
+
+
+static int
+get_int_array(int **nodelist, int *len)
+{
+	SLang_Array_Type *a = NULL;
+	SLindex_Type i;
+	int *nodes = NULL, t, ret = -1;
+
+	if (!nodelist || !len)
+		return -1;
+
+	t = SLang_peek_at_stack();
+	if (t == SLANG_INT_TYPE) {
+
+		nodes = malloc(sizeof(int) * 1);
+		if (!nodes)
+			goto out;
+		if (SLang_pop_integer(&nodes[0]) < 0)
+			goto out;
+
+		*len = 1;
+		ret = 0;
+
+	} else if (t == SLANG_ARRAY_TYPE) {
+		if (SLang_pop_array_of_type(&a, SLANG_INT_TYPE) < 0)
+			goto out;
+		if (a->num_dims > 1)
+			goto out;
+		if (a->dims[0] < 0)
+			goto out;
+		nodes = malloc(sizeof(int) * a->dims[0]);
+		if (!nodes)
+			goto out;
+		for (i = 0; i < a->dims[0]; i++)
+			SLang_get_array_element(a, &i, &nodes[i]);
+
+		*len = a->dims[0];
+		ret = 0;
+	}
+
+out:
+	if (a)
+		SLang_free_array(a);
+	if (ret == 0) {
+		*nodelist = nodes;
+	} else {
+		if (nodes)
+			free(nodes);
+	}
+	
+	return ret;
+}
+
+
+/**
+  get_service_property(service_name, property)
+ */
+char *
+sl_service_property(char *svcName, char *prop)
+{
+	char buf[96];
+
+	if (get_service_property(svcName, prop, buf, sizeof(buf)) < 0)
+		return NULL;
+
+	/* does this work or do I have to push a malloce'd string? */
+	return strdup(buf);
+}
+
+
+/**
+  usage:
+
+  stop_service(name, disable_flag);
+ */
+int
+sl_stop_service(void)
+{
+	char *svcname = NULL;
+	int nargs, t, ret = -1;
+	int do_disable = 0;
+
+	nargs = SLang_Num_Function_Args;
+
+	/* Takes one or two args */
+	if (nargs <= 0 || nargs > 2) {
+		SLang_verror(SL_Syntax_Error,
+		     "%s: Wrong # of args (%d), must be 1 or 2\n",
+		     __FUNCTION__,
+		     nargs);
+		return -1;
+	}
+
+	if (nargs == 2) {
+		t = SLang_peek_at_stack();
+		if (t != SLANG_INT_TYPE) {
+			SLang_verror(SL_Syntax_Error,
+				     "%s: expected type %d got %d\n",
+				     __FUNCTION__, SLANG_INT_TYPE, t);
+			goto out;
+		}
+
+		if (SLang_pop_integer(&do_disable) < 0) {
+			SLang_verror(SL_Syntax_Error,
+			    "%s: Failed to pop integer from stack!\n",
+			    __FUNCTION__);
+			goto out;
+		}
+
+		--nargs;
+	}
+
+	if (nargs == 1) {
+		t = SLang_peek_at_stack();
+		if (t != SLANG_STRING_TYPE) {
+			SLang_verror(SL_Syntax_Error,
+				     "%s: expected type %d got %d\n",
+				     __FUNCTION__,
+				     SLANG_STRING_TYPE, t);
+			goto out;
+		}
+
+		if (SLpop_string(&svcname) < 0) {
+			SLang_verror(SL_Syntax_Error,
+			    "%s: Failed to pop string from stack!\n",
+			    __FUNCTION__);
+			goto out;
+		}
+	}
+
+	/* TODO: Meat of function goes here */
+	ret = service_op_stop(svcname, do_disable, _event_type);
+out:
+	if (svcname)
+		free(svcname);
+	_user_return = ret;
+	return ret;
+}
+
+
+/**
+  usage:
+
+  start_service(name, <array>ordered_node_list_allowed,
+  		      <array>node_list_illegal)
+ */
+int
+sl_start_service(void)
+{
+	char *svcname = NULL;
+	int *pref_list = NULL, pref_list_len = 0;
+	int *illegal_list = NULL, illegal_list_len = 0;
+	int nargs, t, newowner = 0, ret = -1;
+
+	nargs = SLang_Num_Function_Args;
+
+	/* Takes one, two, or three */
+	if (nargs <= 0 || nargs > 3) {
+		SLang_verror(SL_Syntax_Error,
+		     "%s: Wrong # of args (%d), must be 1 or 2\n",
+		     __FUNCTION__, nargs);
+		return -1;
+	}
+
+	if (nargs == 3) {
+		if (get_int_array(&illegal_list, &illegal_list_len) < 0)
+			goto out;
+		--nargs;
+	}
+
+	if (nargs == 2) {
+		if (get_int_array(&pref_list, &pref_list_len) < 0)
+			goto out;
+		--nargs;
+	}
+
+	if (nargs == 1) {
+		/* Just get the service name */
+		t = SLang_peek_at_stack();
+		if (t != SLANG_STRING_TYPE) {
+			SLang_verror(SL_Syntax_Error,
+				     "%s: expected type %d got %d\n",
+				     __FUNCTION__,
+				     SLANG_STRING_TYPE, t);
+			goto out;
+		}
+
+		if (SLpop_string(&svcname) < 0)
+			goto out;
+	}
+
+	/* TODO: Meat of function goes here */
+	ret = service_op_start(svcname, pref_list,
+			       pref_list_len, &newowner);
+
+	if (ret == 0 && newowner > 0)
+		ret = newowner;
+out:
+	if (svcname)
+		free(svcname);
+	if (illegal_list)
+		free(illegal_list);
+	if (pref_list)
+		free(pref_list);
+	_user_return = ret;
+	return ret;
+}
+
+
+/* Take an array of integers given its length and
+   push it on to the S/Lang stack */
+void
+push_int_array(int *stuff, int len)
+{
+	SLindex_Type arrlen, x;
+	SLang_Array_Type *arr;
+	int i;
+
+	arrlen = len;
+	arr = SLang_create_array(SLANG_INT_TYPE, 0, NULL, &arrlen, 1);
+	if (!arr)
+		return;
+
+	x = 0;
+	for (x = 0; x < len; x++) {
+		i = stuff[x];
+		SLang_set_array_element(arr, &x, &i);
+	}
+	SLang_push_array(arr, 1);
+}
+
+
+/*
+   Returns an array of rgmanager-visible nodes online.  How cool is that?
+ */
+void
+sl_nodes_online(void)
+{
+	int i, *nodes, nodecount = 0;
+
+	cluster_member_list_t *membership = member_list();
+	if (!membership)
+		return;
+	nodes = malloc(sizeof(int) * membership->cml_count);
+	if (!nodes)
+		return;
+
+	nodecount = 0;
+	for (i = 0; i < membership->cml_count; i++) {
+		if (membership->cml_members[i].cn_member &&
+		    membership->cml_members[i].cn_nodeid != 0) {
+			nodes[nodecount] = membership->cml_members[i].cn_nodeid;
+			++nodecount;
+		}
+	}
+	free_member_list(membership);
+	push_int_array(nodes, nodecount);
+	free(nodes);
+}
+
+
+/*
+   Returns an array of rgmanager-defined services, in type:name format
+   We allocate/kill this list *once* per event to ensure we don't leak
+   memory
+ */
+void
+sl_service_list(void)
+{
+	SLindex_Type svccount = _service_list_len, x = 0;
+	SLang_Array_Type *svcarray;
+
+	svcarray = SLang_create_array(SLANG_STRING_TYPE, 0, NULL, &svccount, 1);
+	if (!svcarray)
+		return;
+
+	for (; x < _service_list_len; x++) 
+		SLang_set_array_element(svcarray, &x, &_service_list[x]);
+
+	SLang_push_array(svcarray, 1);
+}
+
+
+/* s_union hook (see sets.c) */
+void
+sl_union(void)
+{
+	int *arr1 = NULL, a1len = 0;
+	int *arr2 = NULL, a2len = 0;
+	int *ret = NULL, retlen = 0;
+	int nargs = SLang_Num_Function_Args;
+
+	if (nargs != 2)
+		return;
+		
+	/* Remember: args on the stack are reversed */
+	get_int_array(&arr2, &a2len);
+	get_int_array(&arr1, &a1len);
+	s_union(arr1, a1len, arr2, a2len, &ret, &retlen);
+	push_int_array(ret, retlen);
+	if (arr1)
+		free(arr1);
+	if (arr2)
+		free(arr2);
+	if (ret)
+		free(ret);
+	return;
+}
+
+
+/* s_intersection hook (see sets.c) */
+void
+sl_intersection(void)
+{
+	int *arr1 = NULL, a1len = 0;
+	int *arr2 = NULL, a2len = 0;
+	int *ret = NULL, retlen = 0;
+	int nargs = SLang_Num_Function_Args;
+
+	if (nargs != 2)
+		return;
+		
+	/* Remember: args on the stack are reversed */
+	get_int_array(&arr2, &a2len);
+	get_int_array(&arr1, &a1len);
+	s_intersection(arr1, a1len, arr2, a2len, &ret, &retlen);
+	push_int_array(ret, retlen);
+	if (arr1)
+		free(arr1);
+	if (arr2)
+		free(arr2);
+	if (ret)
+		free(ret);
+	return;
+}
+
+
+/* s_delta hook (see sets.c) */
+void
+sl_delta(void)
+{
+	int *arr1 = NULL, a1len = 0;
+	int *arr2 = NULL, a2len = 0;
+	int *ret = NULL, retlen = 0;
+	int nargs = SLang_Num_Function_Args;
+
+	if (nargs != 2)
+		return;
+		
+	/* Remember: args on the stack are reversed */
+	get_int_array(&arr2, &a2len);
+	get_int_array(&arr1, &a1len);
+	s_delta(arr1, a1len, arr2, a2len, &ret, &retlen);
+	push_int_array(ret, retlen);
+	if (arr1)
+		free(arr1);
+	if (arr2)
+		free(arr2);
+	if (ret)
+		free(ret);
+	return;
+}
+
+
+/* s_subtract hook (see sets.c) */
+void
+sl_subtract(void)
+{
+	int *arr1 = NULL, a1len = 0;
+	int *arr2 = NULL, a2len = 0;
+	int *ret = NULL, retlen = 0;
+	int nargs = SLang_Num_Function_Args;
+
+	if (nargs != 2)
+		return;
+		
+	/* Remember: args on the stack are reversed */
+	get_int_array(&arr2, &a2len);
+	get_int_array(&arr1, &a1len);
+	s_subtract(arr1, a1len, arr2, a2len, &ret, &retlen);
+	push_int_array(ret, retlen);
+	if (arr1)
+		free(arr1);
+	if (arr2)
+		free(arr2);
+	if (ret)
+		free(ret);
+	return;
+}
+
+
+/* Shuffle array (see sets.c) */
+void
+sl_shuffle(void)
+{
+	int *arr1 = NULL, a1len = 0;
+	int nargs = SLang_Num_Function_Args;
+
+	if (nargs != 1)
+		return;
+		
+	/* Remember: args on the stack are reversed */
+	get_int_array(&arr1, &a1len);
+	s_shuffle(arr1, a1len);
+	push_int_array(arr1, a1len);
+	if (arr1)
+		free(arr1);
+	return;
+}
+
+
+/* Converts an int array to a string so we can log it in one shot */
+static int
+array_to_string(char *buf, int buflen, int *array, int arraylen)
+{
+	char intbuf[16];
+	int x, len, remain = buflen;
+
+	memset(intbuf, 0, sizeof(intbuf));
+	memset(buf, 0, buflen);
+	len = snprintf(buf, buflen - 1, "[ ");
+	if (len == buflen)
+		return -1;
+
+	remain -= len;
+	for (x = 0; x < arraylen; x++) {
+		len = snprintf(intbuf, sizeof(intbuf) - 1, "%d ", array[x]);
+		remain -= len;
+		if (remain > 0) {
+			strncat(buf, intbuf, len);
+		} else {
+			return -1;
+		}
+	}
+
+	len = snprintf(intbuf, sizeof(intbuf) - 1 ,  "]");
+	remain -= len;
+	if (remain > 0) {
+		strncat(buf, intbuf, len);
+	} else {
+		return -1;
+	}
+	return (buflen - remain);
+}
+
+
+/**
+  Start at the end of the arg list and work backwards, prepending a string.
+  This does not support standard clulog / printf formattting; rather, we 
+  just allow integers / strings to be mixed on the stack, figure out the
+  type, convert it to the right type, and prepend it on to our log message
+
+  The last must be a log level, as specified above:
+     LOG_DEBUG
+     ...
+     LOG_EMERG
+
+  This matches up with clulog / syslog mappings in the var table; the above
+  are constants in the S/Lang interpreter.  Any number of arguments may
+  be provided.  Examples are:
+
+    log(LOG_INFO, "String", 1, "string2");
+
+  Result:  String1string2
+
+    log(LOG_INFO, "String ", 1, " string2");
+
+  Result:  String 1 string2
+
+ */
+void
+sl_clulog(int level)
+{
+	int t, nargs, len;
+	//int level;
+	int s_intval;
+	char *s_strval;
+	int *nodes = 0, nlen = 0;
+	char logbuf[512];
+	char tmp[256];
+	int need_free;
+	int remain = sizeof(logbuf)-2;
+
+	nargs = SLang_Num_Function_Args;
+	if (nargs < 1)
+		return;
+
+	memset(logbuf, 0, sizeof(logbuf));
+	memset(tmp, 0, sizeof(tmp));
+	logbuf[sizeof(logbuf)-1] = 0;
+	logbuf[sizeof(logbuf)-2] = '\n';
+
+	while (nargs && (t = SLang_peek_at_stack()) >= 0 && remain) {
+		switch(t) {
+		case SLANG_ARRAY_TYPE:
+			if (get_int_array(&nodes, &nlen) < 0)
+				return;
+			len = array_to_string(tmp, sizeof(tmp),
+					      nodes, nlen);
+			if (len < 0) {
+				free(nodes);
+				return;
+			}
+			free(nodes);
+			break;
+		case SLANG_INT_TYPE:
+			if (SLang_pop_integer(&s_intval) < 0)
+				return;
+			len=snprintf(tmp, sizeof(tmp) - 1, "%d", s_intval);
+			break;
+		case SLANG_STRING_TYPE:
+			need_free = 0;
+			if (SLpop_string(&s_strval) < 0)
+				return;
+			len=snprintf(tmp, sizeof(tmp) - 1, "%s", s_strval);
+			SLfree(s_strval);
+			break;
+		default:
+			need_free = 0;
+			len=snprintf(tmp, sizeof(tmp) - 1,
+				     "{UnknownType %d}", t);
+			break;
+		}
+
+		--nargs;
+
+		if (len > remain)
+			return;
+		remain -= len;
+
+		memcpy(&logbuf[remain], tmp, len);
+	}
+
+#if 0
+	printf("<%d> %s\n", level, &logbuf[remain]);
+#endif
+	clulog(level, &logbuf[remain]);
+	return;
+}
+
+
+/* Logging functions */
+void
+sl_log_debug(void)
+{
+	sl_clulog(LOG_DEBUG);
+}
+
+
+void
+sl_log_info(void)
+{
+	sl_clulog(LOG_INFO);
+}
+
+
+void
+sl_log_notice(void)
+{
+	sl_clulog(LOG_NOTICE);
+}
+
+
+void
+sl_log_warning(void)
+{
+	sl_clulog(LOG_WARNING);
+}
+
+
+void
+sl_log_err(void)
+{
+	sl_clulog(LOG_ERR);
+}
+
+
+void
+sl_log_crit(void)
+{
+	sl_clulog(LOG_CRIT);
+}
+
+
+void
+sl_log_alert(void)
+{
+	sl_clulog(LOG_ALERT);
+}
+
+
+void
+sl_log_emerg(void)
+{
+	sl_clulog(LOG_EMERG);
+}
+
+
+void
+sl_die(void)
+{
+	_stop_processing = 1;
+	return;
+}
+
+
+SLang_Intrin_Fun_Type rgmanager_slang[] =
+{
+	MAKE_INTRINSIC_0("nodes_online", sl_nodes_online, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("service_list", sl_service_list, SLANG_VOID_TYPE),
+
+	MAKE_INTRINSIC_SS("service_property", sl_service_property,
+			  SLANG_STRING_TYPE),
+	MAKE_INTRINSIC_S("service_domain_info", sl_domain_info, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("service_stop", sl_stop_service, SLANG_INT_TYPE),
+	MAKE_INTRINSIC_0("service_start", sl_start_service, SLANG_INT_TYPE),
+	MAKE_INTRINSIC_S("service_status", sl_service_status,
+			 SLANG_VOID_TYPE),
+
+	/* Node list manipulation */
+	MAKE_INTRINSIC_0("union", sl_union, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("intersection", sl_intersection, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("delta", sl_delta, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("subtract", sl_subtract, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("shuffle", sl_shuffle, SLANG_VOID_TYPE),
+
+	/* Logging */
+	MAKE_INTRINSIC_0("debug", sl_log_debug, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("info", sl_log_info, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("notice", sl_log_notice, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("warning", sl_log_warning, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("err", sl_log_err, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("crit", sl_log_crit, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("alert", sl_log_alert, SLANG_VOID_TYPE),
+	MAKE_INTRINSIC_0("emerg", sl_log_emerg, SLANG_VOID_TYPE),
+
+	MAKE_INTRINSIC_0("stop_processing", sl_die, SLANG_VOID_TYPE),
+
+	SLANG_END_INTRIN_FUN_TABLE
+};
+
+
+/* Hook for when we generate a script error */
+void
+rgmanager_slang_error_hook(char *errstr)
+{
+	/* Don't just send errstr, because it might contain
+	   "%s" for example which would result in a crash!
+	   plus, we like the newline :) */
+	clulog(LOG_ERR, "[S/Lang] %s\n", errstr);
+}
+
+
+
+/* ================================================================
+ * S/Lang initialization
+ * ================================================================ */
+int
+do_init_slang(void)
+{
+	SLang_init_slang();
+	SLang_init_slfile();
+
+	if (SLadd_intrin_fun_table(rgmanager_slang, NULL) < 0)
+		return 1;
+    	if (SLadd_intrin_var_table (rgmanager_vars, NULL) < 0)
+		return 1;
+
+	/* TODO: Make rgmanager S/Lang conformant.  Though, it
+	   might be a poor idea to provide access to all the 
+	   S/Lang libs */
+	SLpath_set_load_path(RESOURCE_ROOTDIR);
+
+	_my_node_id = my_id();
+	__sl_initialized = 1;
+
+	SLang_Error_Hook = rgmanager_slang_error_hook;
+
+	return 0;
+}
+
+
+/*
+   Execute a script / file and return the result to the caller
+   Log an error if we receive one.
+ */
+int
+do_slang_run(const char *file, const char *script)
+{
+	int ret = 0;
+
+	if (file) 
+		ret = SLang_load_file((char *)file);
+	else
+		ret = SLang_load_string((char *)script);
+
+	if (ret < 0) {
+		clulog(LOG_ERR, "[S/Lang] Script Execution Failure\n");
+		SLang_restart(1);
+	}
+
+	return ret;
+}
+
+
+int
+S_node_event(const char *file, const char *script, int nodeid,
+	     int state, int clean)
+{
+	int ret;
+	cluster_member_list_t *membership = member_list();
+
+	_node_name = strdup(memb_id_to_name(membership, nodeid));
+	_node_state = state;
+	_node_clean = clean;
+	_node_id = nodeid;
+	free_member_list(membership);
+
+	ret = do_slang_run(file, script);
+
+	_node_state = 0;
+	_node_clean = 0;
+	_node_id = 0;
+	if (_node_name)
+		free(_node_name);
+	_node_name = NULL;
+
+	return ret;
+}
+
+
+int
+S_service_event(const char *file, const char *script, char *name,
+	        int state, int owner, int last_owner)
+{
+	int ret;
+
+	_service_name = name;
+	_service_state = (char *)rg_state_str(state);
+	_service_owner = owner;
+	_service_last_owner = last_owner;
+	_service_restarts_exceeded = check_restart(name);
+
+	switch(state) {
+	case RG_STATE_DISABLED:
+	case RG_STATE_STOPPED:
+	case RG_STATE_FAILED:
+	case RG_STATE_RECOVER:
+	case RG_STATE_ERROR:
+		/* There is no owner for these states.  Ever.  */
+		_service_owner = -1;
+	}
+
+	ret = do_slang_run(file, script);
+
+	_service_name = NULL;
+	_service_state = 0;
+	_service_owner = 0;
+	_service_last_owner = 0;
+	_service_restarts_exceeded = 0;
+
+	return ret;
+}
+
+
+int
+S_user_event(const char *file, const char *script, char *name,
+	     int request, int arg1, int arg2, int target, msgctx_t *ctx)
+{
+	int ret = RG_SUCCESS;
+
+	_service_name = name;
+	_service_owner = target;
+	_user_request = request;
+	_user_arg1 = arg1;
+	_user_arg2 = arg2;
+	_user_return = 0;
+
+	ret = do_slang_run(file, script);
+	if (ret < 0) {
+		_user_return = RG_ESCRIPT;
+	}
+
+	_service_name = NULL;
+	_service_owner = 0;
+	_user_request = 0;
+	_user_arg1 = 0;
+	_user_arg2 = 0;
+
+	/* XXX Send response code to caller - that 0 should be the
+	   new service owner, if there is one  */
+	if (ctx) {
+		if (_user_return > 0) {
+			/* sl_start_service() squashes return code and
+			   node ID into one value.  <0 = error, >0 =
+			   success, return-value == node id running
+			   service */
+			send_ret(ctx, name, 0, request, _user_return);
+		} else {
+			/* return value < 0 ... pass directly back;
+			   don't transpose */
+			send_ret(ctx, name, _user_return, request, 0);
+		}
+		msg_close(ctx);
+		msg_free_ctx(ctx);
+	}
+	_user_return = 0;
+	return ret;
+}
+
+
+int
+slang_do_script(event_t *pattern, event_t *ev)
+{
+	_event_type = ev->ev_type;
+	int ret = 0;
+
+	switch(ev->ev_type) {
+	case EVENT_NODE:
+		ret = S_node_event(
+				pattern->ev_script_file,
+				pattern->ev_script,
+				ev->ev.node.ne_nodeid,
+				ev->ev.node.ne_state,
+				ev->ev.node.ne_clean);
+		break;
+	case EVENT_RG:
+		ret = S_service_event(
+				pattern->ev_script_file,
+				pattern->ev_script,
+				ev->ev.group.rg_name,
+				ev->ev.group.rg_state,
+				ev->ev.group.rg_owner,
+				ev->ev.group.rg_last_owner);
+		break;
+	case EVENT_USER:
+		ret = S_user_event(
+				pattern->ev_script_file,
+				pattern->ev_script,
+				ev->ev.user.u_name,
+				ev->ev.user.u_request,
+				ev->ev.user.u_arg1,
+				ev->ev.user.u_arg2,
+				ev->ev.user.u_target,
+				ev->ev.user.u_ctx);
+		break;
+	default:
+		break;
+	}
+
+	_event_type = EVENT_NONE;
+	return ret;
+}
+
+
+
+/**
+  Process an event given our event table and the event that
+  occurred.  Note that the caller is responsible for freeing the
+  event - do not free (ev) ...
+ */
+int
+slang_process_event(event_table_t *event_table, event_t *ev)
+{
+	int x, y;
+	event_t *pattern;
+
+	if (!__sl_initialized)
+		do_init_slang();
+
+	/* Get the service list once before processing events */
+	if (!_service_list || !_service_list_len)
+		_service_list = get_service_names(&_service_list_len);
+
+	_stop_processing = 0;
+	for (x = 1; x <= event_table->max_prio; x++) {
+		list_for(&event_table->entries[x], pattern, y) {
+			if (event_match(pattern, ev))
+				slang_do_script(pattern, ev);
+			if (_stop_processing)
+				goto out;
+		}
+	}
+
+	/* Default level = 0 */
+	list_for(&event_table->entries[0], pattern, y) {
+		if (event_match(pattern, ev))
+			slang_do_script(pattern, ev);
+		if (_stop_processing)
+			goto out;
+	}
+
+out:
+	/* Free the service list */
+	if (_service_list) {
+		for(x = 0; x < _service_list_len; x++) {
+			free(_service_list[x]);
+		}
+		free(_service_list);
+		_service_list = NULL;
+		_service_list_len = 0;
+	}
+
+	return 0;
+}
diff --git a/rgmanager/src/daemons/test.c b/rgmanager/src/daemons/test.c
index fa77edd..55dc63d 100644
--- a/rgmanager/src/daemons/test.c
+++ b/rgmanager/src/daemons/test.c
@@ -1,3 +1,21 @@
+/*
+  Copyright Red Hat, Inc. 2004-2006
+
+  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, or (at your option) any
+  later version.
+
+  This program 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 program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
 #include <libxml/parser.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/xpath.h>
@@ -66,20 +84,22 @@ test_func(int argc, char **argv)
 	fod_t *domains = NULL;
 	resource_rule_t *rulelist = NULL, *currule;
 	resource_t *reslist = NULL, *curres;
-	resource_node_t *tree = NULL;
+	resource_node_t *tree = NULL, *tmp, *rn = NULL;
 	int ccsfd, ret = 0, rules = 0;
+	event_table_t *events = NULL;
 
 	fprintf(stderr,"Running in test mode.\n");
 
 	conf_setconfig(argv[1]);
        	ccsfd = ccs_lock();
-	if (ccsfd == FAIL) {
+	if (ccsfd < 0) {
 		printf("Error parsing %s\n", argv[1]);
 		goto out;
 	}
 
 	load_resource_rules(agentpath, &rulelist);
 	construct_domains(ccsfd, &domains);
+	construct_events(ccsfd, &events);
 	load_resources(ccsfd, &reslist, &rulelist);
 	build_resource_tree(ccsfd, &tree, &rulelist, &reslist);
 
@@ -114,6 +134,11 @@ test_func(int argc, char **argv)
 			printf("=== Failover Domains ===\n");
 			print_domains(&domains);
 		}
+
+		if (events) {
+			printf("=== Event Triggers ===\n");
+			print_events(events);
+		}
 	}
 
 	ccs_unlock(ccsfd);
@@ -128,6 +153,13 @@ test_func(int argc, char **argv)
 		goto out;
 	}
 
+	list_do(&tree, tmp) {
+		if (tmp->rn_resource == curres) {
+			rn = tmp;
+			break;
+		}
+	} while (!list_done(&tree, tmp));
+
 	if (!strcmp(argv[1], "start")) {
 		printf("Starting %s...\n", argv[3]);
 
@@ -147,12 +179,29 @@ test_func(int argc, char **argv)
 		}
 		printf("Stop of %s complete\n", argv[3]);
 		goto out;
+	} else if (!strcmp(argv[1], "migrate")) {
+		printf("Migrating %s to %s...\n", argv[3], argv[4]);
+
+	#if 0
+		if (!group_migratory(curres)) {
+			printf("No can do\n");
+			ret = -1;
+			goto out;
+		}
+	#endif
+
+		if (res_exec(rn, RS_MIGRATE, argv[4], 0)) {
+			ret = -1;
+			goto out;
+		}
+		printf("Migration of %s complete\n", argv[3]);
+		goto out;
 	} else if (!strcmp(argv[1], "status")) {
 		printf("Checking status of %s...\n", argv[3]);
 
-		if (res_status(&tree, curres, NULL)) {
+		ret = res_status(&tree, curres, NULL);
+		if (ret) {
 			printf("Status check of %s failed\n", argv[3]);
-			ret = -1;
 			goto out;
 		}
 		printf("Status of %s is good\n", argv[3]);
@@ -160,6 +209,7 @@ test_func(int argc, char **argv)
 	}
 
 out:
+	deconstruct_events(&events);
 	deconstruct_domains(&domains);
 	destroy_resource_tree(&tree);
 	destroy_resources(&reslist);
@@ -175,7 +225,9 @@ tree_delta_test(int argc, char **argv)
 	resource_rule_t *rulelist = NULL, *currule, *rulelist2 = NULL;
 	resource_t *reslist = NULL, *curres, *reslist2 = NULL;
 	resource_node_t *tree = NULL, *tree2 = NULL;
-	int ccsfd, ret = 0;
+	resource_node_t *tn;
+	int ccsfd, ret = 0, need_init, need_kill;
+	char rg[64];
 
 	if (argc < 2) {
 		printf("Operation requires two arguments\n");
@@ -234,6 +286,57 @@ tree_delta_test(int argc, char **argv)
 	print_resource_tree(&tree);
 	printf("=== New Resource Tree ===\n");
 	print_resource_tree(&tree2);
+	printf("=== Operations (down-phase) ===\n");
+	list_do(&tree, tn) {
+		res_build_name(rg, sizeof(rg), tn->rn_resource);
+		/* Set state to uninitialized if we're killing a RG */
+		need_init = 0;
+
+		/* Set state to uninitialized if we're killing a RG */
+		need_kill = 0;
+		if (tn->rn_resource->r_flags & RF_NEEDSTOP) {
+			need_kill = 1;
+			printf("[kill] ");
+		}
+
+		if (!tn->rn_child && ((tn->rn_resource->r_rule->rr_flags &
+		    RF_DESTROY) == 0) && group_migratory(&reslist, &tree, rg) &&
+		    need_kill == 1) {
+			/* Do something smart here: flip state? */
+			printf("[no-op] %s was removed from the config, but I am not stopping it.\n",
+			       rg);
+			continue;
+		}
+
+		res_condstop(&tn, tn->rn_resource, NULL);
+	} while (!list_done(&tree, tn));
+	printf("=== Operations (up-phase) ===\n");
+	list_do(&tree2, tn) {
+		res_build_name(rg, sizeof(rg), tn->rn_resource);
+		/* New RG.  We'll need to initialize it. */
+		need_init = 0;
+		if (!(tn->rn_resource->r_flags & RF_RECONFIG) &&
+		    (tn->rn_resource->r_flags & RF_NEEDSTART))
+			need_init = 1;
+
+		if (need_init) {
+			printf("[init] ");
+		}
+
+		if (!tn->rn_child && ((tn->rn_resource->r_rule->rr_flags &
+		    RF_INIT) == 0) && group_migratory(&reslist2, &tree2, rg) &&
+		    need_init == 1) {
+			/* Do something smart here? */
+			printf("[noop] %s was added, but I am not initializing it\n", rg);
+			continue;
+		}
+
+		if (need_init) {
+			res_stop(&tn, tn->rn_resource, NULL);
+		} else {
+			res_condstart(&tn, tn->rn_resource, NULL);
+		}
+	} while (!list_done(&tree2, tn));
 
 out:
 	destroy_resource_tree(&tree2);
@@ -289,6 +392,7 @@ main(int argc, char **argv)
 			goto out;
 		} else if (!strcmp(argv[1], "delta")) {
 			shift();
+			_no_op_mode(1);
 			ret = tree_delta_test(argc, argv);
 			goto out;
 		} else {
@@ -312,5 +416,5 @@ main(int argc, char **argv)
 out:
 	xmlCleanupParser();
 	malloc_dump_table();
-	return 0;
+	return ret;
 }
diff --git a/rgmanager/src/resources/Makefile b/rgmanager/src/resources/Makefile
index d543468..c1f4a71 100644
--- a/rgmanager/src/resources/Makefile
+++ b/rgmanager/src/resources/Makefile
@@ -21,11 +21,12 @@ RESOURCES=fs.sh service.sh ip.sh nfsclient.sh nfsexport.sh \
 	script.sh netfs.sh clusterfs.sh smb.sh \
 	apache.sh openldap.sh samba.sh mysql.sh \
 	postgres-8.sh tomcat-5.sh lvm.sh lvm_by_lv.sh lvm_by_vg.sh \
-	SAPInstance SAPDatabase oracledb.sh
+	SAPInstance SAPDatabase named.sh \
+	oracledb.sh
 
 METADATA=apache.metadata openldap.metadata samba.metadata \
 	mysql.metadata postgres-8.metadata tomcat-5.metadata \
-	lvm.metadata
+	named.metadata lvm.metadata
 
 TARGETS=${RESOURCES} ocf-shellfuncs svclib_nfslock
 
@@ -34,6 +35,9 @@ UTIL_TARGETS= \
 	utils/httpd-parse-config.pl utils/tomcat-parse-config.pl \
 	utils/member_util.sh
 
+EVENT_TARGETS= \
+	default_event_script.sl
+
 all:
 
 install: all
@@ -44,6 +48,7 @@ install: all
 	install $(TARGETS) ${sharedir}
 	install $(UTIL_TARGETS) ${sharedir}/utils
 	install -m 644 $(METADATA) ${sharedir}
+	install -m 644 $(EVENT_TARGETS) ${sharedir}
 
 uninstall:
 	${UNINSTALL} ${UTIL_TARGETS} ${sharedir}/utils
diff --git a/rgmanager/src/resources/clusterfs.sh b/rgmanager/src/resources/clusterfs.sh
index 0bf14f0..34a5590 100755
--- a/rgmanager/src/resources/clusterfs.sh
+++ b/rgmanager/src/resources/clusterfs.sh
@@ -366,7 +366,7 @@ mountInUse () {
 	typeset junk
 
 	if [ $# -ne 2 ]; then
-		logAndPrint $LOG_ERR "Usage: mountInUse device mount_point".
+		ocf_log err "Usage: mountInUse device mount_point".
 		return $FAIL
 	fi
 
@@ -444,14 +444,14 @@ isAlive()
 	declare rw
 	
 	if [ $# -ne 1 ]; then
-	        logAndPrint $LOG_ERR "Usage: isAlive mount_point"
+	        ocf_log err "Usage: isAlive mount_point"
 		return $FAIL
 	fi
 	mount_point=$1
 	
 	test -d $mount_point
 	if [ $? -ne 0 ]; then
-		logAndPrint $LOG_ERR "$mount_point is not a directory"
+		ocf_log err "$mount_point is not a directory"
 		return $FAIL
 	fi
 	
@@ -738,7 +738,7 @@ Cannot mount $dev on $mp, the device or mount point is already in use!"
 	#
 	# Mount the device
 	#
-	logAndPrint $LOG_DEBUG "mount $fstype_option $mount_options $dev $mp"
+	ocf_log debug "mount $fstype_option $mount_options $dev $mp"
 	mount $fstype_option $mount_options $dev $mp
 	ret_val=$?
 	if [ $ret_val -ne 0 ]; then
@@ -761,6 +761,7 @@ stopFilesystem() {
 	typeset -i try=1
 	typeset -i max_tries=3		# how many times to try umount
 	typeset -i sleep_time=2		# time between each umount failure
+	typeset -i refs=0
 	typeset done=""
 	typeset umount_failed=""
 	typeset force_umount=""
@@ -815,6 +816,18 @@ stop: Could not match $OCF_RESKEY_device with a real device"
 		esac
 	fi
 
+	#
+	# Check the rgmanager-supplied reference count if one exists.
+	# If the reference count is <= 1, we can safely proceed
+	#
+	if [ -n "$OCF_RESKEY_RGMANAGER_meta_refcnt" ]; then
+		refs=$OCF_RESKEY_RGMANAGER_meta_refcnt
+		if [ $refs -gt 1 ]; then
+			((refs--))
+			ocf_log debug "Not unmounting $OCF_RESOURCE_INSTANCE - still in use by $refs other service(s)"
+			return $OCF_SUCCESS
+		fi
+	fi
 
 	#
 	# Always do this hackery on clustered file systems.
@@ -825,10 +838,10 @@ stop: Could not match $OCF_RESKEY_device with a real device"
 		mkdir -p $mp/.clumanager/statd
 		pkill -KILL -x lockd
 		# Copy out the notify list; our 
-			# IPs are already torn down
-			if notify_list_store $mp/.clumanager/statd; then
-				notify_list_broadcast $mp/.clumanager/statd
-			fi
+		# IPs are already torn down
+		if notify_list_store $mp/.clumanager/statd; then
+			notify_list_broadcast $mp/.clumanager/statd
+		fi
 	fi
 
 	# Always invalidate buffers on clusterfs resources
@@ -857,7 +870,7 @@ stop: Could not match $OCF_RESKEY_device with a real device"
 			sync; sync; sync
 			ocf_log info "unmounting $dev ($mp)"
 
-			umount $dev
+			umount $mp
 			if  [ $? -eq 0 ]; then
 				umount_failed=
 				done=$YES
@@ -907,8 +920,20 @@ stop: Could not match $OCF_RESKEY_device with a real device"
 
 case $1 in
 start)
-	startFilesystem
-	exit $?
+	declare tries=0
+	declare rv
+
+	while [ $tries -lt 3 ]; do
+		startFilesystem
+		rv=$?
+		if [ $rv -eq 0 ]; then
+			exit 0
+		fi
+
+		((tries++))
+		sleep 3
+	done
+	exit $rv
 	;;
 stop)
 	stopFilesystem
@@ -916,16 +941,12 @@ stop)
 	;;
 status|monitor)
   	isMounted ${OCF_RESKEY_device} ${OCF_RESKEY_mountpoint}
- 	if [ $? -ne $YES ]; then
-		ocf_log err "fs:${OCF_RESKEY_name}: ${OCF_RESKEY_device} is not mounted on ${OCF_RESKEY_mountpoint}"
-		exit $OCF_ERR_GENERIC
-	fi
+ 	[ $? -ne $YES ] && exit $OCF_ERR_GENERIC
 
  	isAlive ${OCF_RESKEY_mountpoint}
- 	[ $? -eq $YES ] && exit 0
-
-	ocf_log err "clusterfs:${OCF_RESKEY_name}: Mount point is not accessible!"
-	exit $OCF_ERR_GENERIC
+ 	[ $? -ne $YES ] && exit $OCF_ERR_GENERIC
+ 	
+	exit 0
 	;;
 restart)
 	stopFilesystem
diff --git a/rgmanager/src/resources/default_event_script.sl b/rgmanager/src/resources/default_event_script.sl
new file mode 100644
index 0000000..e961266
--- /dev/null
+++ b/rgmanager/src/resources/default_event_script.sl
@@ -0,0 +1,314 @@
+define node_in_set(node_list, node)
+{
+	variable x, len;
+
+	len = length(node_list);
+	for (x = 0; x < len; x++) {
+		if (node_list[x] == node)
+			return 1;
+	}
+
+	return 0;
+}
+
+define move_or_start(service, node_list)
+{
+	variable len;
+	variable state, owner;
+	variable depends;
+
+	depends = service_property(service, "depend");
+	if (depends != "") {
+		(owner, state) = service_status(depends);
+		if (owner < 0) {
+			debug(service, " is not runnable; dependency not met");
+			return ERR_DEPEND;
+		}
+	}
+
+	(owner, state) = service_status(service);
+	debug("Evaluating ", service, " state=", state, " owner=", owner);
+
+	len = length(node_list);
+	if (len == 0) {
+		debug(service, " is not runnable");
+		return ERR_DOMAIN;
+	}
+
+	if (((event_type != EVENT_USER) and (state == "disabled")) or (state == "failed")) {
+		%
+		% Commenting out this block will -not- allow you to
+		% recover failed services from event scripts.  Sorry.
+		% All it will get you is a false log message about
+		% starting this service.
+		%
+		% You may enable disabled services, but I recommend
+		% against it.
+		%
+		debug(service, " is not runnable");
+		return -1;
+	}
+
+	if (node_list[0] == owner) {
+		debug(service, " is already running on best node");
+		return ERR_RUNNING;
+	}
+
+	if ((owner >= 0) and (node_in_set(node_list, owner) == 1)) {
+		notice("Moving ", service, " from ", owner,
+		       " to ", node_list);
+		if (service_stop(service) < 0) {
+			return ERR_ABORT;
+		}
+	} else {
+		notice("Starting ", service, " on ", node_list);
+	}
+
+	return service_start(service, node_list);
+}
+
+
+%
+% Returns the set of online nodes in preferred/shuffled order which
+% are allowed to run this service.  Gives highest preference to current
+% owner if nofailback is specified.
+% 
+define allowed_nodes(service)
+{
+	variable anodes;
+	variable online;
+	variable nodes_domain;
+	variable ordered, restricted, nofailback;
+	variable state, owner;
+	variable depends;
+
+	(nofailback, restricted, ordered, nodes_domain) =
+			service_domain_info(service);
+
+	(owner, state) = service_status(service);
+
+	anodes = nodes_online();
+
+	% Shuffle the array so we don't start all services on the same
+	% node.  TODO - add RR, Least-services, placement policies...
+	online = shuffle(anodes);
+
+	if (restricted == 1) {
+		anodes = intersection(nodes_domain, online);
+	} else {
+		% Ordered failover domains (nodes_domain) unioned with the
+		% online nodes basically just reorders the online node list
+		% according to failover domain priority rules.
+		anodes = union(intersection(nodes_domain, online),
+			       online);
+	}
+
+	if ((nofailback == 1) or (ordered == 0)) {
+		
+		if ((owner < 0) or (node_in_set(anodes, owner) == 0)) {
+			return anodes;
+		}
+		
+		% Because union takes left as priority, we can
+		% return the union of the current owner with the
+		% allowed node list.  This means the service will
+		% remain on the same node it's currently on.
+		return union(owner, anodes);
+	}
+
+	return anodes;
+}
+
+
+define default_node_event_handler()
+{
+	variable services = service_list();
+	variable x;
+	variable nodes;
+
+	% debug("Executing default node event handler");
+	for (x = 0; x < length(services); x++) {
+		nodes = allowed_nodes(services[x]);
+		()=move_or_start(services[x], nodes);
+	}
+}
+
+
+define default_service_event_handler()
+{
+	variable services = service_list();
+	variable x;
+	variable depends;
+	variable depend_mode;
+	variable policy;
+	variable nodes;
+	variable tmp;
+	variable owner;
+	variable state;
+
+	% debug("Executing default service event handler");
+
+	if (service_state == "recovering") {
+
+		policy = service_property(service_name, "recovery");
+		debug("Recovering",
+		      " Service: ", service_name,
+		      " Last owner: ", service_last_owner,
+		      " Policy: ", policy,
+		      " RTE: ", service_restarts_exceeded);
+
+		if (policy == "disable") {
+			() = service_stop(service_name, 1);
+			return;
+		}
+
+		nodes = allowed_nodes(service_name);
+		if (policy == "restart" and service_restarts_exceeded == 0) {
+			nodes = union(service_last_owner, nodes);
+		} else {
+			% relocate 
+			tmp = subtract(nodes, service_last_owner);
+			if (length(tmp) == 0) {
+				() = service_stop(service_name,0);
+				return;
+			}
+
+			nodes = union(tmp, service_last_owner);
+		}
+
+		()=move_or_start(service_name, nodes);
+
+		return;
+	}
+
+	for (x = 0; x < length(services); x++) {
+		if (service_name == services[x]) {
+			% don't do anything to ourself! 
+			continue;
+		}
+
+		%
+		% Simplistic dependency handling
+		%
+		depends = service_property(services[x], "depend");
+		depend_mode = service_property(services[x], "depend_mode");
+
+		% No dependency; do nothing
+		if (depends != service_name) {
+			continue;
+		}
+
+		(owner, state) = service_status(services[x]);
+		if ((service_state == "started") and (owner < 0) and
+		    (state == "stopped")) {
+			info("Dependency met; starting ", services[x]);
+			nodes = allowed_nodes(services[x]);
+			()=move_or_start(services[x], nodes);
+		}
+
+		% service died - stop service(s) that depend on the dead
+		if ((service_owner < 0) and (owner >= 0) and
+		    (depend_mode != "soft")) {
+			info("Dependency lost; stopping ", services[x]);
+			()=service_stop(services[x]);
+		}
+	}
+}
+
+define default_config_event_handler()
+{
+	% debug("Executing default config event handler");
+}
+
+define default_user_event_handler()
+{
+	variable ret;
+	variable nodes;
+	variable reordered;
+	variable x;
+	variable target = user_target;
+	variable found = 0;
+	variable owner, state;
+
+	nodes = allowed_nodes(service_name);
+	(owner, state) = service_status(service_name);
+
+	if (user_request == USER_RESTART) {
+
+		if (owner >= 0) {
+			reordered = union(owner, nodes);
+			nodes = reordered;
+		}
+
+		notice("Stopping ", service_name, " for relocate to ", nodes);
+
+		found = service_stop(service_name);
+		if (found < 0) {
+			return ERR_ABORT;
+		}
+
+		ret = move_or_start(service_name, nodes);
+
+	} else if ((user_request == USER_RELOCATE) or 
+		   (user_request == USER_ENABLE)) {
+
+		if (user_target > 0) {
+			for (x = 0; x < length(nodes); x++) {
+				%
+				% Put the preferred node at the front of the 
+				% list for a user-relocate operation
+				%
+				if (nodes[x] == user_target) {
+					reordered = union(user_target, nodes);
+					nodes = reordered;
+					found = 1;
+				}
+			}
+	
+			if (found == 0) {
+				warning("User specified node ", user_target,
+					" is offline");
+			}
+		}
+
+		if ((owner >= 0) and (user_request == USER_RELOCATE)) {
+			if (service_stop(service_name) < 0) {
+				return ERR_ABORT;
+			}
+
+			%
+			% The current owner shouldn't be the default
+			% for a relocate operation
+			%
+			reordered = subtract(nodes, owner);
+			nodes = union(reordered, owner);
+		}
+
+		ret = move_or_start(service_name, nodes);
+
+	} else if (user_request == USER_DISABLE) {
+
+		ret = service_stop(service_name, 1);
+
+	} else if (user_request == USER_STOP) {
+
+		ret = service_stop(service_name);
+
+	} 
+
+	%
+	% todo - migrate
+	%
+
+	return ret;
+}
+
+if (event_type == EVENT_NODE)
+	default_node_event_handler();
+if (event_type == EVENT_SERVICE)
+	default_service_event_handler();
+if (event_type == EVENT_CONFIG)
+	default_config_event_handler();
+if (event_type == EVENT_USER)
+	user_return=default_user_event_handler();
+
diff --git a/rgmanager/src/resources/ip.sh b/rgmanager/src/resources/ip.sh
index 517c4fd..b76bafc 100755
--- a/rgmanager/src/resources/ip.sh
+++ b/rgmanager/src/resources/ip.sh
@@ -444,22 +444,14 @@ findSlaves()
 		return $OCF_ERR_GENERIC
 	fi
 
-       ## Strip possible VLAN (802.1q) suffixes 
-       ##  - Roland Gadinger <roland gadinger beko at> 
-       mastif=${mastif%%.*} 
+	## Strip possible VLAN (802.1q) suffixes 
+	##  - Roland Gadinger <roland gadinger beko at> 
+	mastif=${mastif%%.*} 
 
 	while read line; do
 		set - $line
-		while [ $# -gt 0 ]; do
-			case $1 in
-			eth*:)
-				interfaces="${1/:/} $interfaces"
-				continue 2
-				;;
-			esac
-			shift
-		done
-	done < <( /sbin/ip link list | grep "master $mastif" )
+		interfaces="${2/:/} $interfaces"
+	done < <( /sbin/ip -o link list | grep "master $mastif" )
 
 	echo $interfaces
 }
diff --git a/rgmanager/src/resources/ocf-shellfuncs b/rgmanager/src/resources/ocf-shellfuncs
index eb0147f..98156c0 100755
--- a/rgmanager/src/resources/ocf-shellfuncs
+++ b/rgmanager/src/resources/ocf-shellfuncs
@@ -174,6 +174,10 @@ ocf_log() {
 	esac
 
 	pretty_echo $__OCF_PRIO "$__OCF_MSG"
+
+	if [ -z "`which clulog 2> /dev/null`" ]; then
+		return 0
+	fi
 	clulog -p $__LOG_PID -n $__LOG_NAME \
 		-s $__OCF_PRIO_N "$__OCF_MSG"
 }
diff --git a/rgmanager/src/resources/script.sh b/rgmanager/src/resources/script.sh
index 9a9455c..0141c80 100755
--- a/rgmanager/src/resources/script.sh
+++ b/rgmanager/src/resources/script.sh
@@ -115,5 +115,5 @@ ${OCF_RESKEY_file} $1
 declare -i rv=$?
 if [ $rv -ne 0 ]; then
 	ocf_log err "script:$OCF_RESKEY_name: $1 of $OCF_RESKEY_file failed (returned $rv)"
-	return $OCF_ERR_GENERIC
+	exit $OCF_ERR_GENERIC
 fi
diff --git a/rgmanager/src/resources/service.sh b/rgmanager/src/resources/service.sh
index da89301..339657d 100755
--- a/rgmanager/src/resources/service.sh
+++ b/rgmanager/src/resources/service.sh
@@ -1,8 +1,26 @@
 #!/bin/bash
 
 #
-# Dummy OCF script for resource group; the OCF spec doesn't support abstract
-# resources. ;(
+#  Copyright Red Hat, Inc. 2004-2006
+#
+#  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, or (at your option) any
+#  later version.
+#
+#  This program 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 program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+#
+
+#
+# Dummy OCF script for resource group
 #
 
 # Grab nfs lock tricks if available
@@ -38,7 +56,7 @@ meta_data()
             <content type="string"/>
         </parameter>
     
-        <parameter name="domain">
+        <parameter name="domain" reconfig="1">
             <longdesc lang="en">
                 Fail over domains define lists of cluster members
                 to try in the event that a resource group fails.
@@ -49,7 +67,7 @@ meta_data()
             <content type="string"/>
         </parameter>
 
-        <parameter name="autostart">
+        <parameter name="autostart" reconfig="1">
             <longdesc lang="en">
 	    	If set to yes, this resource group will automatically be started
 		after the cluster forms a quorum.  If set to no, this resource
@@ -59,10 +77,10 @@ meta_data()
             <shortdesc lang="en">
 	    	Automatic start after quorum formation
             </shortdesc>
-            <content type="boolean"/>
+            <content type="boolean" default="1"/>
         </parameter>
 
-        <parameter name="hardrecovery">
+        <parameter name="hardrecovery" reconfig="1">
             <longdesc lang="en">
 	    	If set to yes, the last owner will reboot if this resource
 		group fails to stop cleanly, thus allowing the resource
@@ -74,10 +92,10 @@ meta_data()
             <shortdesc lang="en">
 	    	Reboot if stop phase fails
             </shortdesc>
-            <content type="boolean"/>
+            <content type="boolean" default="0"/>
         </parameter>
 
-        <parameter name="exclusive">
+        <parameter name="exclusive" reconfig="1">
             <longdesc lang="en">
 	    	If set, this resource group will only relocate to
 		nodes which have no other resource groups running in the
@@ -91,7 +109,7 @@ meta_data()
             <shortdesc lang="en">
 	        Exclusive resource group
             </shortdesc>
-            <content type="boolean"/>
+            <content type="boolean" default="0"/>
         </parameter>
 
 	<parameter name="nfslock">
@@ -107,10 +125,10 @@ meta_data()
 	    <shortdesc lang="en">
 	        Enable NFS lock workarounds
 	    </shortdesc>
-	    <content type="boolean"/>
+	    <content type="boolean" default="0"/>
 	</parameter>
                 
-        <parameter name="recovery">
+        <parameter name="recovery" reconfig="1">
             <longdesc lang="en">
 	        This currently has three possible options: "restart" tries
 		to restart failed parts of this resource group locally before
@@ -123,8 +141,60 @@ meta_data()
             <shortdesc lang="en">
 	    	Failure recovery policy
             </shortdesc>
+            <content type="string" default="restart"/>
+        </parameter>
+
+        <parameter name="depend">
+            <longdesc lang="en">
+		Top-level service this depends on, in "service:name"; format.
+            </longdesc>
+            <shortdesc lang="en">
+		Service dependency; will not start without the specified
+		service running.
+            </shortdesc>
             <content type="string"/>
         </parameter>
+
+        <parameter name="depend_mode">
+            <longdesc lang="en">
+	    	Dependency mode
+            </longdesc>
+            <shortdesc lang="en">
+		Service dependency mode.
+		hard - This service is stopped/started if its dependency
+		       is stopped/started
+		soft - This service only depends on the other service for
+		       initial startip.  If the other service stops, this
+		       service is not stopped.
+            </shortdesc>
+            <content type="string" default="hard"/>
+        </parameter>
+
+        <parameter name="max_restarts">
+            <longdesc lang="en">
+	    	Maximum restarts for this service.
+            </longdesc>
+            <shortdesc lang="en">
+	    	Maximum restarts for this service.
+            </shortdesc>
+            <content type="string" default="0"/>
+        </parameter>
+
+        <parameter name="restart_expire_time">
+            <longdesc lang="en">
+	    	Restart expiration time
+            </longdesc>
+            <shortdesc lang="en">
+	    	Restart expiration time.  A restart is forgotten
+		after this time.  When combined with the max_restarts
+		option, this lets administrators specify a threshold
+		for when to fail over services.  If max_restarts
+		is exceeded in this given expiration time, the service
+		is relocated instead of restarted again.
+            </shortdesc>
+            <content type="string" default="0"/>
+        </parameter>
+
     </parameters>
 
     <actions>
@@ -135,10 +205,11 @@ meta_data()
         <action name="status" timeout="5" interval="1h"/>
         <action name="monitor" timeout="5" interval="1h"/>
 
+        <action name="reconfig" timeout="5"/>
         <action name="recover" timeout="5"/>
         <action name="reload" timeout="5"/>
         <action name="meta-data" timeout="5"/>
-        <action name="verify-all" timeout="5"/>
+        <action name="validate-all" timeout="5"/>
     </actions>
     
     <special tag="rgmanager">
@@ -149,7 +220,7 @@ meta_data()
         <child type="netfs" start="4" stop="6"/>
 	<child type="nfsexport" start="5" stop="5"/>
 
-	<child type="nfsclient" start="6" stop=""/>
+	<child type="nfsclient" start="6" stop="4"/>
 
         <child type="ip" start="7" stop="2"/>
         <child type="smb" start="8" stop="3"/>
@@ -166,6 +237,7 @@ EOT
 #
 case $1 in
 	start)
+		[ -d "/var/run/cluster/rgmanager" ] && touch "/var/run/cluster/rgmanager/$OCF_RESOURCE_INSTANCE"
 		#
 		# XXX If this is set, we kill lockd.  If there is no
 		# child IP address, then clients will NOT get the reclaim
@@ -180,6 +252,7 @@ case $1 in
 		exit 0
 		;;
 	stop)
+		[ -d "/var/run/cluster/rgmanager" ] && rm -f "/var/run/cluster/rgmanager/$OCF_RESOURCE_INSTANCE"
 		exit 0
 		;;
 	recover|restart)
@@ -195,7 +268,10 @@ case $1 in
 		meta_data
 		exit 0
 		;;
-	verify-all)
+	validate-all)
+		exit 0
+		;;
+	reconfig)
 		exit 0
 		;;
 	*)
diff --git a/rgmanager/src/resources/svclib_nfslock b/rgmanager/src/resources/svclib_nfslock
index de996b1..2101e1e 100644
--- a/rgmanager/src/resources/svclib_nfslock
+++ b/rgmanager/src/resources/svclib_nfslock
@@ -1,5 +1,22 @@
 #!/bin/bash
 #
+#  Copyright Red Hat Inc., 2006
+#
+#  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, or (at your option) any
+#  later version.
+#
+#  This program 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 program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+#
 # Do reclaim-broadcasts when we kill lockd during shutdown/startup
 # of a cluster service.
 #
@@ -163,6 +180,17 @@ notify_list_broadcast()
 	declare lockd_pid=$(pidof lockd)
 	declare nl_dir=$1
 
+	# First of all, send lockd a SIGKILL.  We hope nfsd is running.
+	# If it is, this will cause lockd to reset the grace period for
+	# lock reclaiming.
+	if [ -n "$lockd_pid" ]; then
+		ocf_log info "Asking lockd to drop locks (pid $lockd_pid)"
+		kill -9 $lockd_pid
+	else
+		ocf_log warning "lockd not running; cannot notify clients"
+		return 1
+	fi
+	
         while read dev family addr maskbits; do
 		if [ "$family" != "inet" ]; then
 			continue
diff --git a/rgmanager/src/resources/utils/named-parse-config.pl b/rgmanager/src/resources/utils/named-parse-config.pl
new file mode 100644
index 0000000..4ac39c3
--- /dev/null
+++ b/rgmanager/src/resources/utils/named-parse-config.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+
+##
+##  parse named.conf (from stdin) and add options from cluster.conf
+##  
+##  ./named-parse-config.pl "directory" "pid-file" "listen-on"
+##
+use strict;
+
+if ($#argv < 2) {
+	die ("Not enough arguments");
+}
+
+while (my $line = <STDIN>) {
+	chomp($line);
+	$line =~ s/(.*?)\s*$/$1/;
+	if ($line =~ /^\s*options\s+\{/) {
+		print $line, "\n";
+		print "\tdirectory \"$ARGV[0]\";\n";
+		print "\tpid-file \"$ARGV[1]\";\n";
+		print "\tlisten-on { $ARGV[2] };\n";
+	} else {
+		print $line, "\n";
+	}
+}
+
diff --git a/rgmanager/src/resources/utils/ra-skelet.sh b/rgmanager/src/resources/utils/ra-skelet.sh
index 67630e2..5530ae6 100644
--- a/rgmanager/src/resources/utils/ra-skelet.sh
+++ b/rgmanager/src/resources/utils/ra-skelet.sh
@@ -65,7 +65,7 @@ stop_generic()
 	if [ ! -d "/proc/$pid" ]; then
 		return 0;
 	fi
-                                
+
 	kill -TERM "$pid"
 
 	if [ $? -ne 0 ]; then


hooks/post-receive
--
Cluster Project


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]