diff --git a/cib/callbacks.c b/cib/callbacks.c
index c9fa8b7d45..bf945d2b4c 100644
--- a/cib/callbacks.c
+++ b/cib/callbacks.c
@@ -1,1599 +1,1599 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/msg_xml.h>
 #include <crm/cluster/internal.h>
 
 #include <crm/common/xml.h>
 
 #include <cibio.h>
 #include <callbacks.h>
 #include <cibmessages.h>
 #include <notify.h>
 #include "common.h"
 
 static unsigned long cib_local_bcast_num = 0;
 
 typedef struct cib_local_notify_s {
     xmlNode *notify_src;
     char *client_id;
     gboolean from_peer;
     gboolean sync_reply;
 } cib_local_notify_t;
 
 int next_client_id = 0;
 
 gboolean legacy_mode = FALSE;
 
 qb_ipcs_service_t *ipcs_ro = NULL;
 qb_ipcs_service_t *ipcs_rw = NULL;
 qb_ipcs_service_t *ipcs_shm = NULL;
 
 gint cib_GCompareFunc(gconstpointer a, gconstpointer b);
 gboolean can_write(int flags);
 void send_cib_replace(const xmlNode * sync_request, const char *host);
 void cib_process_request(xmlNode * request, gboolean privileged, gboolean force_synchronous,
                          gboolean from_peer, crm_client_t * cib_client);
 
 
 int cib_process_command(xmlNode * request, xmlNode ** reply,
                         xmlNode ** cib_diff, gboolean privileged);
 
 gboolean cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size,
                              gboolean privileged);
 
 gboolean cib_legacy_mode(void)
 {
     return legacy_mode;
 }
 
 
 static int32_t
 cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
 {
     crm_trace("Connection %p", c);
     if (cib_shutdown_flag) {
         crm_info("Ignoring new client [%d] during shutdown", crm_ipcs_client_pid(c));
         return -EPERM;
     }
 
     if (crm_client_new(c, uid, gid) == NULL) {
         return -EIO;
     }
     return 0;
 }
 
 static void
 cib_ipc_created(qb_ipcs_connection_t * c)
 {
     crm_trace("Connection %p", c);
 }
 
 static int32_t
 cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size)
 {
     crm_client_t *client = crm_client_get(c);
 
     crm_trace("%p message from %s", c, client->id);
     return cib_common_callback(c, data, size, TRUE);
 }
 
 static int32_t
 cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size)
 {
     crm_client_t *client = crm_client_get(c);
 
     crm_trace("%p message from %s", c, client->id);
     return cib_common_callback(c, data, size, FALSE);
 }
 
 /* Error code means? */
 static int32_t
 cib_ipc_closed(qb_ipcs_connection_t * c)
 {
     crm_client_t *client = crm_client_get(c);
 
     if (client == NULL) {
         return 0;
     }
     crm_trace("Connection %p", c);
     crm_client_destroy(client);
     return 0;
 }
 
 static void
 cib_ipc_destroy(qb_ipcs_connection_t * c)
 {
     crm_trace("Connection %p", c);
     cib_ipc_closed(c);
     if (cib_shutdown_flag) {
         cib_shutdown(0);
     }
 }
 
 struct qb_ipcs_service_handlers ipc_ro_callbacks = {
     .connection_accept = cib_ipc_accept,
     .connection_created = cib_ipc_created,
     .msg_process = cib_ipc_dispatch_ro,
     .connection_closed = cib_ipc_closed,
     .connection_destroyed = cib_ipc_destroy
 };
 
 struct qb_ipcs_service_handlers ipc_rw_callbacks = {
     .connection_accept = cib_ipc_accept,
     .connection_created = cib_ipc_created,
     .msg_process = cib_ipc_dispatch_rw,
     .connection_closed = cib_ipc_closed,
     .connection_destroyed = cib_ipc_destroy
 };
 
 void
 cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
                            crm_client_t * cib_client, gboolean privileged)
 {
     const char *op = crm_element_value(op_request, F_CIB_OPERATION);
 
     if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) {
         if (flags & crm_ipc_client_response) {
             xmlNode *ack = create_xml_node(NULL, __FUNCTION__);
 
             crm_xml_add(ack, F_CIB_OPERATION, CRM_OP_REGISTER);
             crm_xml_add(ack, F_CIB_CLIENTID, cib_client->id);
             crm_ipcs_send(cib_client, id, ack, flags);
             cib_client->request_id = 0;
             free_xml(ack);
         }
         return;
 
     } else if (crm_str_eq(op, T_CIB_NOTIFY, TRUE)) {
         /* Update the notify filters for this client */
         int on_off = 0;
         long long bit = 0;
         const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);
 
         crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
 
         crm_debug("Setting %s callbacks for %s (%s): %s",
                   type, cib_client->name, cib_client->id, on_off ? "on" : "off");
 
         if (safe_str_eq(type, T_CIB_POST_NOTIFY)) {
             bit = cib_notify_post;
 
         } else if (safe_str_eq(type, T_CIB_PRE_NOTIFY)) {
             bit = cib_notify_pre;
 
         } else if (safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) {
             bit = cib_notify_confirm;
 
         } else if (safe_str_eq(type, T_CIB_DIFF_NOTIFY)) {
             bit = cib_notify_diff;
 
         } else if (safe_str_eq(type, T_CIB_REPLACE_NOTIFY)) {
             bit = cib_notify_replace;
         }
 
         if (on_off) {
             set_bit(cib_client->options, bit);
         } else {
             clear_bit(cib_client->options, bit);
         }
 
         if (flags & crm_ipc_client_response) {
             /* TODO - include rc */
             crm_ipcs_send_ack(cib_client, id, flags, "ack", __FUNCTION__, __LINE__);
         }
         return;
     }
 
     cib_process_request(op_request, FALSE, privileged, FALSE, cib_client);
 }
 
 int32_t
 cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean privileged)
 {
     uint32_t id = 0;
     uint32_t flags = 0;
     int call_options = 0;
     crm_client_t *cib_client = crm_client_get(c);
     xmlNode *op_request = crm_ipcs_recv(cib_client, data, size, &id, &flags);
 
     if (op_request) {
         crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
     }
 
     if (op_request == NULL) {
         crm_trace("Invalid message from %p", c);
         crm_ipcs_send_ack(cib_client, id, flags, "nack", __FUNCTION__, __LINE__);
         return 0;
 
     } else if(cib_client == NULL) {
         crm_trace("Invalid client %p", c);
         return 0;
     }
 
     if (is_set(call_options, cib_sync_call)) {
         CRM_LOG_ASSERT(flags & crm_ipc_client_response);
         CRM_LOG_ASSERT(cib_client->request_id == 0);    /* This means the client has two synchronous events in-flight */
         cib_client->request_id = id;    /* Reply only to the last one */
     }
 
     if (cib_client->name == NULL) {
         const char *value = crm_element_value(op_request, F_CIB_CLIENTNAME);
 
         if (value == NULL) {
             cib_client->name = crm_itoa(cib_client->pid);
         } else {
             cib_client->name = strdup(value);
             if (crm_is_daemon_name(value)) {
                 set_bit(cib_client->options, cib_is_daemon);
             }
         }
     }
 
     /* Allow cluster daemons more leeway before being evicted */
     if (is_set(cib_client->options, cib_is_daemon)) {
         const char *qmax = cib_config_lookup("cluster-ipc-limit");
 
         if (crm_set_client_queue_max(cib_client, qmax)) {
             crm_trace("IPC threshold for %s[%u] is now %u",
                       cib_client->name, cib_client->pid, cib_client->queue_max);
         }
     }
 
     crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
     crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
 
 #if ENABLE_ACL
     CRM_LOG_ASSERT(cib_client->user != NULL);
     crm_acl_get_set_user(op_request, F_CIB_USER, cib_client->user);
 #endif
 
     crm_log_xml_trace(op_request, "Client[inbound]");
 
     cib_common_callback_worker(id, flags, op_request, cib_client, privileged);
     free_xml(op_request);
 
     return 0;
 }
 
 static uint64_t ping_seq = 0;
 static char *ping_digest = NULL;
 static bool ping_modified_since = FALSE;
 int sync_our_cib(xmlNode * request, gboolean all);
 
 static gboolean
 cib_digester_cb(gpointer data)
 {
     if (cib_is_master) {
         char buffer[32];
         xmlNode *ping = create_xml_node(NULL, "ping");
 
         ping_seq++;
         free(ping_digest);
         ping_digest = NULL;
         ping_modified_since = FALSE;
         snprintf(buffer, 32, U64T, ping_seq);
         crm_trace("Requesting peer digests (%s)", buffer);
 
         crm_xml_add(ping, F_TYPE, "cib");
         crm_xml_add(ping, F_CIB_OPERATION, CRM_OP_PING);
         crm_xml_add(ping, F_CIB_PING_ID, buffer);
 
         crm_xml_add(ping, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
         send_cluster_message(NULL, crm_msg_cib, ping, TRUE);
 
         free_xml(ping);
     }
     return FALSE;
 }
 
 static void
 process_ping_reply(xmlNode *reply) 
 {
     uint64_t seq = 0;
     const char *host = crm_element_value(reply, F_ORIG);
 
     xmlNode *pong = get_message_xml(reply, F_CIB_CALLDATA);
     const char *seq_s = crm_element_value(pong, F_CIB_PING_ID);
     const char *digest = crm_element_value(pong, XML_ATTR_DIGEST);
 
     if (seq_s) {
         seq = crm_int_helper(seq_s, NULL);
     }
 
     if(digest == NULL) {
         crm_trace("Ignoring ping reply %s from %s with no digest", seq_s, host);
 
     } else if(seq != ping_seq) {
         crm_trace("Ignoring out of sequence ping reply %s from %s", seq_s, host);
 
     } else if(ping_modified_since) {
         crm_trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host);
 
     } else {
         const char *version = crm_element_value(pong, XML_ATTR_CRM_VERSION);
 
         if(ping_digest == NULL) {
             crm_trace("Calculating new digest");
             ping_digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, version);
         }
 
         crm_trace("Processing ping reply %s from %s (%s)", seq_s, host, digest);
         if(safe_str_eq(ping_digest, digest) == FALSE) {
             xmlNode *remote_cib = get_message_xml(pong, F_CIB_CALLDATA);
 
             crm_notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s %p",
                        crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN),
                        crm_element_value(the_cib, XML_ATTR_GENERATION),
                        crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
                        ping_digest, host,
                        remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION_ADMIN):"_",
                        remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION):"_",
                        remote_cib?crm_element_value(remote_cib, XML_ATTR_NUMUPDATES):"_",
                        digest, remote_cib);
 
             if(remote_cib && remote_cib->children) {
                 /* Additional debug */
                 xml_calculate_changes(the_cib, remote_cib);
                 xml_log_changes(LOG_INFO, __FUNCTION__, remote_cib);
                 crm_trace("End of differences");
             }
 
             free_xml(remote_cib);
             sync_our_cib(reply, FALSE);
         }
     }
 }
 
 static void
 do_local_notify(xmlNode * notify_src, const char *client_id,
                 gboolean sync_reply, gboolean from_peer)
 {
     /* send callback to originating child */
     crm_client_t *client_obj = NULL;
     int local_rc = pcmk_ok;
     int call_id = 0;
 
     crm_element_value_int(notify_src, F_CIB_CALLID, &call_id);
 
     if (client_id != NULL) {
         client_obj = crm_client_get_by_id(client_id);
     }
 
     if (client_obj == NULL) {
         local_rc = -ECONNRESET;
         crm_trace("No client to sent response %d to, F_CIB_CLIENTID not set.", call_id);
 
     } else {
         int rid = 0;
 
         if (sync_reply) {
             if (client_obj->ipcs) {
                 CRM_LOG_ASSERT(client_obj->request_id);
 
                 rid = client_obj->request_id;
                 client_obj->request_id = 0;
 
                 crm_trace("Sending response %d to %s %s",
                           rid, client_obj->name,
                           from_peer ? "(originator of delegated request)" : "");
             } else {
                 crm_trace("Sending response [call %d] to %s %s",
                           call_id, client_obj->name, from_peer ? "(originator of delegated request)" : "");
             }
 
         } else {
             crm_trace("Sending event %d to %s %s",
                       call_id, client_obj->name, from_peer ? "(originator of delegated request)" : "");
         }
 
         switch (client_obj->kind) {
             case CRM_CLIENT_IPC:
                 if (crm_ipcs_send(client_obj, rid, notify_src, sync_reply?crm_ipc_flags_none:crm_ipc_server_event) < 0) {
                     local_rc = -ENOMSG;
                 }
                 break;
 #ifdef HAVE_GNUTLS_GNUTLS_H
             case CRM_CLIENT_TLS:
 #endif
             case CRM_CLIENT_TCP:
                 crm_remote_send(client_obj->remote, notify_src);
                 break;
             default:
                 crm_err("Unknown transport %d for %s", client_obj->kind, client_obj->name);
         }
     }
 
     if (local_rc != pcmk_ok && client_obj != NULL) {
         crm_warn("%sSync reply to %s failed: %s",
                  sync_reply ? "" : "A-",
                  client_obj ? client_obj->name : "<unknown>", pcmk_strerror(local_rc));
     }
 }
 
 static void
 local_notify_destroy_callback(gpointer data)
 {
     cib_local_notify_t *notify = data;
 
     free_xml(notify->notify_src);
     free(notify->client_id);
     free(notify);
 }
 
 static void
 check_local_notify(int bcast_id)
 {
     cib_local_notify_t *notify = NULL;
 
     if (!local_notify_queue) {
         return;
     }
 
     notify = g_hash_table_lookup(local_notify_queue, GINT_TO_POINTER(bcast_id));
 
     if (notify) {
         do_local_notify(notify->notify_src, notify->client_id, notify->sync_reply,
                         notify->from_peer);
         g_hash_table_remove(local_notify_queue, GINT_TO_POINTER(bcast_id));
     }
 }
 
 static void
 queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_reply,
                    gboolean from_peer)
 {
     cib_local_notify_t *notify = calloc(1, sizeof(cib_local_notify_t));
 
     notify->notify_src = notify_src;
     notify->client_id = strdup(client_id);
     notify->sync_reply = sync_reply;
     notify->from_peer = from_peer;
 
     if (!local_notify_queue) {
         local_notify_queue = g_hash_table_new_full(g_direct_hash,
                                                    g_direct_equal, NULL,
                                                    local_notify_destroy_callback);
     }
 
     g_hash_table_insert(local_notify_queue, GINT_TO_POINTER(cib_local_bcast_num), notify);
 }
 
 static void
 parse_local_options_v1(crm_client_t * cib_client, int call_type, int call_options, const char *host,
                     const char *op, gboolean * local_notify, gboolean * needs_reply,
                     gboolean * process, gboolean * needs_forward)
 {
     if (cib_op_modifies(call_type)
         && !(call_options & cib_inhibit_bcast)) {
         /* we need to send an update anyway */
         *needs_reply = TRUE;
     } else {
         *needs_reply = FALSE;
     }
 
     if (host == NULL && (call_options & cib_scope_local)) {
         crm_trace("Processing locally scoped %s op from %s", op, cib_client->name);
         *local_notify = TRUE;
 
     } else if (host == NULL && cib_is_master) {
         crm_trace("Processing master %s op locally from %s", op, cib_client->name);
         *local_notify = TRUE;
 
     } else if (safe_str_eq(host, cib_our_uname)) {
         crm_trace("Processing locally addressed %s op from %s", op, cib_client->name);
         *local_notify = TRUE;
 
     } else if (stand_alone) {
         *needs_forward = FALSE;
         *local_notify = TRUE;
         *process = TRUE;
 
     } else {
         crm_trace("%s op from %s needs to be forwarded to %s",
                   op, cib_client->name, host ? host : "the master instance");
         *needs_forward = TRUE;
         *process = FALSE;
     }
 }
 
 static void
 parse_local_options_v2(crm_client_t * cib_client, int call_type, int call_options, const char *host,
                     const char *op, gboolean * local_notify, gboolean * needs_reply,
                     gboolean * process, gboolean * needs_forward)
 {
     if (cib_op_modifies(call_type)) {
         if(safe_str_eq(op, CIB_OP_MASTER) || safe_str_eq(op, CIB_OP_SLAVE)) {
             /* Always handle these locally */
             *process = TRUE;
             *needs_reply = FALSE;
             *local_notify = TRUE;
             *needs_forward = FALSE;
             return;
 
         } else {
             /* Redirect all other updates via CPG */
             *needs_reply = TRUE;
             *needs_forward = TRUE;
             *process = FALSE;
             crm_trace("%s op from %s needs to be forwarded to %s",
                       op, cib_client->name, host ? host : "the master instance");
             return;
         }
     }
 
 
     *process = TRUE;
     *needs_reply = FALSE;
     *local_notify = TRUE;
     *needs_forward = FALSE;
 
     if (stand_alone) {
         crm_trace("Processing %s op from %s (stand-alone)", op, cib_client->name);
 
     } else if (host == NULL) {
         crm_trace("Processing unaddressed %s op from %s", op, cib_client->name);
 
     } else if (safe_str_eq(host, cib_our_uname)) {
         crm_trace("Processing locally addressed %s op from %s", op, cib_client->name);
 
     } else {
         crm_trace("%s op from %s needs to be forwarded to %s", op, cib_client->name, host);
         *needs_forward = TRUE;
         *process = FALSE;
     }
 }
 
 static void
 parse_local_options(crm_client_t * cib_client, int call_type, int call_options, const char *host,
                     const char *op, gboolean * local_notify, gboolean * needs_reply,
                     gboolean * process, gboolean * needs_forward)
 {
     if(cib_legacy_mode()) {
         parse_local_options_v1(cib_client, call_type, call_options, host,
                                op, local_notify, needs_reply, process, needs_forward);
     } else {
         parse_local_options_v2(cib_client, call_type, call_options, host,
                                op, local_notify, needs_reply, process, needs_forward);
     }
 }
 
 static gboolean
 parse_peer_options_v1(int call_type, xmlNode * request,
                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
                    gboolean * needs_forward)
 {
     const char *op = NULL;
     const char *host = NULL;
     const char *delegated = NULL;
     const char *originator = crm_element_value(request, F_ORIG);
     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
     const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
 
     gboolean is_reply = safe_str_eq(reply_to, cib_our_uname);
 
     if (crm_is_true(update)) {
         *needs_reply = FALSE;
         if (is_reply) {
             *local_notify = TRUE;
             crm_trace("Processing global/peer update from %s"
                       " that originated from us", originator);
         } else {
             crm_trace("Processing global/peer update from %s", originator);
         }
         return TRUE;
     }
 
     crm_trace("Processing %s request sent by %s", op, originator);
     op = crm_element_value(request, F_CIB_OPERATION);
     if (safe_str_eq(op, "cib_shutdown_req")) {
         /* Always process these */
         *local_notify = FALSE;
         if (reply_to == NULL || is_reply) {
             *process = TRUE;
         }
         if (is_reply) {
             *needs_reply = FALSE;
         }
         return *process;
     }
 
     if (is_reply && safe_str_eq(op, CRM_OP_PING)) {
         process_ping_reply(request);
         return FALSE;
     }
 
     if (is_reply) {
         crm_trace("Forward reply sent from %s to local clients", originator);
         *process = FALSE;
         *needs_reply = FALSE;
         *local_notify = TRUE;
         return TRUE;
     }
 
     host = crm_element_value(request, F_CIB_HOST);
     if (host != NULL && safe_str_eq(host, cib_our_uname)) {
         crm_trace("Processing %s request sent to us from %s", op, originator);
         return TRUE;
 
     } else if(is_reply == FALSE && safe_str_eq(op, CRM_OP_PING)) {
         crm_trace("Processing %s request sent to %s by %s", op, host?host:"everyone", originator);
         *needs_reply = TRUE;
         return TRUE;
 
     } else if (host == NULL && cib_is_master == TRUE) {
         crm_trace("Processing %s request sent to master instance from %s", op, originator);
         return TRUE;
     }
 
     delegated = crm_element_value(request, F_CIB_DELEGATED);
     if (delegated != NULL) {
         crm_trace("Ignoring msg for master instance");
 
     } else if (host != NULL) {
         /* this is for a specific instance and we're not it */
         crm_trace("Ignoring msg for instance on %s", crm_str(host));
 
     } else if (reply_to == NULL && cib_is_master == FALSE) {
         /* this is for the master instance and we're not it */
         crm_trace("Ignoring reply to %s", crm_str(reply_to));
 
     } else if (safe_str_eq(op, "cib_shutdown_req")) {
         if (reply_to != NULL) {
             crm_debug("Processing %s from %s", op, host);
             *needs_reply = FALSE;
 
         } else {
             crm_debug("Processing %s reply from %s", op, host);
         }
         return TRUE;
 
     } else {
         crm_err("Nothing for us to do?");
         crm_log_xml_err(request, "Peer[inbound]");
     }
 
     return FALSE;
 }
 
 static gboolean
 parse_peer_options_v2(int call_type, xmlNode * request,
                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
                    gboolean * needs_forward)
 {
     const char *host = NULL;
     const char *delegated = crm_element_value(request, F_CIB_DELEGATED);
     const char *op = crm_element_value(request, F_CIB_OPERATION);
     const char *originator = crm_element_value(request, F_ORIG);
     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
     const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
 
     gboolean is_reply = safe_str_eq(reply_to, cib_our_uname);
 
     if(safe_str_eq(op, CIB_OP_REPLACE)) {
         /* sync_our_cib() sets F_CIB_ISREPLY */
         if (reply_to) {
             delegated = reply_to;
         }
         goto skip_is_reply;
 
     } else if(safe_str_eq(op, CIB_OP_SYNC)) {
 
     } else if (is_reply && safe_str_eq(op, CRM_OP_PING)) {
         process_ping_reply(request);
         return FALSE;
 
     } else if (safe_str_eq(op, CIB_OP_UPGRADE)) {
         /* Only the DC (node with the oldest software) should process
          * this operation if F_CIB_SCHEMA_MAX is unset
          *
          * If the DC is happy it will then send out another
          * CIB_OP_UPGRADE which will tell all nodes to do the actual
          * upgrade.
          *
          * Except this time F_CIB_SCHEMA_MAX will be set which puts a
          * limit on how far newer nodes will go
          */
         const char *max = crm_element_value(request, F_CIB_SCHEMA_MAX);
 
         crm_trace("Parsing %s operation%s for %s with max=%s",
                   op, is_reply?" reply":"", cib_is_master?"master":"slave", max);
         if(max == NULL && cib_is_master) {
             /* We are the DC, check if this upgrade is allowed */
             goto skip_is_reply;
 
         } else if(max) {
             /* Ok, go ahead and upgrade to 'max' */
             goto skip_is_reply;
 
         } else {
             return FALSE; /* Ignore */
         }
 
     } else if (crm_is_true(update)) {
         crm_info("Detected legacy %s global update from %s", op, originator);
         send_sync_request(NULL);
         legacy_mode = TRUE;
         return FALSE;
 
     } else if (is_reply && cib_op_modifies(call_type)) {
         crm_trace("Ignoring legacy %s reply sent from %s to local clients", op, originator);
         return FALSE;
 
     } else if (safe_str_eq(op, "cib_shutdown_req")) {
         /* Legacy handling */
         crm_debug("Legacy handling of %s message from %s", op, originator);
         *local_notify = FALSE;
         if (reply_to == NULL) {
             *process = TRUE;
         }
         return *process;
     }
 
     if(is_reply) {
         crm_trace("Handling %s reply sent from %s to local clients", op, originator);
         *process = FALSE;
         *needs_reply = FALSE;
         *local_notify = TRUE;
         return TRUE;
     }
 
   skip_is_reply:
     *process = TRUE;
     *needs_reply = FALSE;
 
     if(safe_str_eq(delegated, cib_our_uname)) {
         *local_notify = TRUE;
     } else {
         *local_notify = FALSE;
     }
 
     host = crm_element_value(request, F_CIB_HOST);
     if (host != NULL && safe_str_eq(host, cib_our_uname)) {
         crm_trace("Processing %s request sent to us from %s", op, originator);
         *needs_reply = TRUE;
         return TRUE;
 
     } else if (host != NULL) {
         /* this is for a specific instance and we're not it */
         crm_trace("Ignoring %s operation for instance on %s", op, crm_str(host));
         return FALSE;
 
     } else if(is_reply == FALSE && safe_str_eq(op, CRM_OP_PING)) {
         *needs_reply = TRUE;
     }
 
     crm_trace("Processing %s request sent to everyone by %s/%s on %s %s", op,
               crm_element_value(request, F_CIB_CLIENTNAME),
               crm_element_value(request, F_CIB_CALLID),
               originator, (*local_notify)?"(notify)":"");
     return TRUE;
 }
 
 static gboolean
 parse_peer_options(int call_type, xmlNode * request,
                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
                    gboolean * needs_forward)
 {
     /* TODO: What happens when an update comes in after node A
      * requests the CIB from node B, but before it gets the reply (and
      * sends out the replace operation)
      */
     if(cib_legacy_mode()) {
         return parse_peer_options_v1(
             call_type, request, local_notify, needs_reply, process, needs_forward);
     } else {
         return parse_peer_options_v2(
             call_type, request, local_notify, needs_reply, process, needs_forward);
     }
 }
 
 static void
 forward_request(xmlNode * request, crm_client_t * cib_client, int call_options)
 {
     const char *op = crm_element_value(request, F_CIB_OPERATION);
     const char *host = crm_element_value(request, F_CIB_HOST);
 
     crm_xml_add(request, F_CIB_DELEGATED, cib_our_uname);
 
     if (host != NULL) {
         crm_trace("Forwarding %s op to %s", op, host);
         send_cluster_message(crm_get_peer(0, host), crm_msg_cib, request, FALSE);
 
     } else {
         crm_trace("Forwarding %s op to master instance", op);
         send_cluster_message(NULL, crm_msg_cib, request, FALSE);
     }
 
     /* Return the request to its original state */
     xml_remove_prop(request, F_CIB_DELEGATED);
 
     if (call_options & cib_discard_reply) {
         crm_trace("Client not interested in reply");
     }
 }
 
 static gboolean
 send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gboolean broadcast)
 {
     CRM_ASSERT(msg != NULL);
 
     if (broadcast) {
         /* this (successful) call modified the CIB _and_ the
          * change needs to be broadcast...
          *   send via HA to other nodes
          */
         int diff_add_updates = 0;
         int diff_add_epoch = 0;
         int diff_add_admin_epoch = 0;
 
         int diff_del_updates = 0;
         int diff_del_epoch = 0;
         int diff_del_admin_epoch = 0;
 
         const char *digest = NULL;
         int format = 1;
 
         CRM_LOG_ASSERT(result_diff != NULL);
         digest = crm_element_value(result_diff, XML_ATTR_DIGEST);
         crm_element_value_int(result_diff, "format", &format);
 
         cib_diff_version_details(result_diff,
                                  &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
                                  &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
 
         crm_trace("Sending update diff %d.%d.%d -> %d.%d.%d %s",
                   diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
                   diff_add_admin_epoch, diff_add_epoch, diff_add_updates, digest);
 
         crm_xml_add(msg, F_CIB_ISREPLY, originator);
         crm_xml_add(msg, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE);
         crm_xml_add(msg, F_CIB_OPERATION, CIB_OP_APPLY_DIFF);
         crm_xml_add(msg, F_CIB_USER, CRM_DAEMON_USER);
 
         if (format == 1) {
             CRM_ASSERT(digest != NULL);
         }
 
         add_message_xml(msg, F_CIB_UPDATE_DIFF, result_diff);
         crm_log_xml_explicit(msg, "copy");
         return send_cluster_message(NULL, crm_msg_cib, msg, TRUE);
 
     } else if (originator != NULL) {
         /* send reply via HA to originating node */
         crm_trace("Sending request result to %s only", originator);
         crm_xml_add(msg, F_CIB_ISREPLY, originator);
         return send_cluster_message(crm_get_peer(0, originator), crm_msg_cib, msg, FALSE);
     }
 
     return FALSE;
 }
 
 void
 cib_process_request(xmlNode * request, gboolean force_synchronous, gboolean privileged,
                     gboolean unused, crm_client_t * cib_client)
 {
     int call_type = 0;
     int call_options = 0;
 
     gboolean process = TRUE;
     gboolean is_update = TRUE;
     gboolean from_peer = TRUE;
     gboolean needs_reply = TRUE;
     gboolean local_notify = FALSE;
     gboolean needs_forward = FALSE;
     gboolean global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE));
 
     xmlNode *op_reply = NULL;
     xmlNode *result_diff = NULL;
 
     int rc = pcmk_ok;
     const char *op = crm_element_value(request, F_CIB_OPERATION);
     const char *originator = crm_element_value(request, F_ORIG);
     const char *host = crm_element_value(request, F_CIB_HOST);
     const char *target = NULL;
     const char *call_id = crm_element_value(request, F_CIB_CALLID);
     const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
     const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
 
     if (cib_client) {
         from_peer = FALSE;
     }
 
     cib_num_ops++;
     if (cib_num_ops == 0) {
         cib_num_fail = 0;
         cib_num_local = 0;
         cib_num_updates = 0;
         crm_info("Stats wrapped around");
     }
 
     crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
     if (force_synchronous) {
         call_options |= cib_sync_call;
     }
 
     if (host != NULL && strlen(host) == 0) {
         host = NULL;
     }
 
     if (host) {
         target = host;
 
     } else if (call_options & cib_scope_local) {
         target = "local host";
 
     } else {
         target = "master";
     }
 
     if (from_peer) {
         crm_trace("Processing peer %s operation from %s/%s on %s intended for %s (reply=%s)",
                   op, client_name, call_id, originator, target, reply_to);
     } else {
         crm_xml_add(request, F_ORIG, cib_our_uname);
         crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target);
     }
 
     rc = cib_get_operation_id(op, &call_type);
     if (rc != pcmk_ok) {
         /* TODO: construct error reply? */
         crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc));
         return;
     }
 
     if (from_peer == FALSE) {
         parse_local_options(cib_client, call_type, call_options, host, op,
                             &local_notify, &needs_reply, &process, &needs_forward);
 
     } else if (parse_peer_options(call_type, request, &local_notify,
                                   &needs_reply, &process, &needs_forward) == FALSE) {
         return;
     }
 
     is_update = cib_op_modifies(call_type);
     if (is_update) {
         cib_num_updates++;
     }
 
     if (call_options & cib_discard_reply) {
         needs_reply = is_update;
         local_notify = FALSE;
     }
 
     if (needs_forward) {
         const char *host = crm_element_value(request, F_CIB_HOST);
         const char *section = crm_element_value(request, F_CIB_SECTION);
         int log_level = LOG_INFO;
 
         if (safe_str_eq(op, CRM_OP_NOOP)) {
             log_level = LOG_DEBUG;
         }
 
         do_crm_log(log_level,
                    "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)",
                    op,
                    section ? section : "'all'",
                    host ? host : cib_legacy_mode() ? "master" : "all",
                    originator ? originator : "local",
                    client_name, call_id);
 
         forward_request(request, cib_client, call_options);
         return;
     }
 
     if (cib_status != pcmk_ok) {
         const char *call = crm_element_value(request, F_CIB_CALLID);
 
         rc = cib_status;
         crm_err("Operation ignored, cluster configuration is invalid."
                 " Please repair and restart: %s", pcmk_strerror(cib_status));
 
         op_reply = create_xml_node(NULL, "cib-reply");
         crm_xml_add(op_reply, F_TYPE, T_CIB);
         crm_xml_add(op_reply, F_CIB_OPERATION, op);
         crm_xml_add(op_reply, F_CIB_CALLID, call);
         crm_xml_add(op_reply, F_CIB_CLIENTID, client_id);
         crm_xml_add_int(op_reply, F_CIB_CALLOPTS, call_options);
         crm_xml_add_int(op_reply, F_CIB_RC, rc);
 
         crm_trace("Attaching reply output");
         add_message_xml(op_reply, F_CIB_CALLDATA, the_cib);
 
         crm_log_xml_explicit(op_reply, "cib:reply");
 
     } else if (process) {
         time_t finished = 0;
 
         int now = time(NULL);
         int level = LOG_INFO;
         const char *section = crm_element_value(request, F_CIB_SECTION);
 
         cib_num_local++;
         rc = cib_process_command(request, &op_reply, &result_diff, privileged);
 
         if (is_update == FALSE) {
             level = LOG_TRACE;
 
         } else if (global_update) {
             switch (rc) {
                 case pcmk_ok:
                     level = LOG_INFO;
                     break;
                 case -pcmk_err_old_data:
                 case -pcmk_err_diff_resync:
                 case -pcmk_err_diff_failed:
                     level = LOG_TRACE;
                     break;
                 default:
                     level = LOG_ERR;
             }
 
         } else if (rc != pcmk_ok && is_update) {
             cib_num_fail++;
             level = LOG_WARNING;
         }
 
         do_crm_log(level,
                    "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)",
                    op, section ? section : "'all'", pcmk_strerror(rc), rc,
                    originator ? originator : "local", client_name, call_id,
                    the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN) : "0",
                    the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION) : "0",
                    the_cib ? crm_element_value(the_cib, XML_ATTR_NUMUPDATES) : "0");
 
         finished = time(NULL);
         if (finished - now > 3) {
             crm_trace("%s operation took %lds to complete", op, (long)(finished - now));
             crm_write_blackbox(0, NULL);
         }
 
         if (op_reply == NULL && (needs_reply || local_notify)) {
             crm_err("Unexpected NULL reply to message");
             crm_log_xml_err(request, "null reply");
             needs_reply = FALSE;
             local_notify = FALSE;
         }
     }
 
     /* from now on we are the server */
     if(is_update && cib_legacy_mode() == FALSE) {
         crm_trace("Completed pre-sync update from %s/%s/%s%s",
                   originator ? originator : "local", client_name, call_id,
                   local_notify?" with local notification":"");
 
     } else if (needs_reply == FALSE || stand_alone) {
         /* nothing more to do...
          * this was a non-originating slave update
          */
         crm_trace("Completed slave update");
 
     } else if (cib_legacy_mode() &&
                rc == pcmk_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) {
         gboolean broadcast = FALSE;
 
         cib_local_bcast_num++;
         crm_xml_add_int(request, F_CIB_LOCAL_NOTIFY_ID, cib_local_bcast_num);
         broadcast = send_peer_reply(request, result_diff, originator, TRUE);
 
         if (broadcast && client_id && local_notify && op_reply) {
 
             /* If we have been asked to sync the reply,
              * and a bcast msg has gone out, we queue the local notify
              * until we know the bcast message has been received */
             local_notify = FALSE;
             crm_trace("Queuing local %ssync notification for %s",
                       (call_options & cib_sync_call) ? "" : "a-", client_id);
 
             queue_local_notify(op_reply, client_id, (call_options & cib_sync_call), from_peer);
             op_reply = NULL;    /* the reply is queued, so don't free here */
         }
 
     } else if (call_options & cib_discard_reply) {
         crm_trace("Caller isn't interested in reply");
 
     } else if (from_peer) {
         if (is_update == FALSE || result_diff == NULL) {
             crm_trace("Request not broadcast: R/O call");
 
         } else if (call_options & cib_inhibit_bcast) {
             crm_trace("Request not broadcast: inhibited");
 
         } else if (rc != pcmk_ok) {
             crm_trace("Request not broadcast: call failed: %s", pcmk_strerror(rc));
 
         } else {
             crm_trace("Directing reply to %s", originator);
         }
 
         send_peer_reply(op_reply, result_diff, originator, FALSE);
     }
 
     if (local_notify && client_id) {
         crm_trace("Performing local %ssync notification for %s",
                   (call_options & cib_sync_call) ? "" : "a-", client_id);
         if (process == FALSE) {
             do_local_notify(request, client_id, call_options & cib_sync_call, from_peer);
         } else {
             do_local_notify(op_reply, client_id, call_options & cib_sync_call, from_peer);
         }
     }
 
     free_xml(op_reply);
     free_xml(result_diff);
 
     return;
 }
 
 int
 cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gboolean privileged)
 {
     xmlNode *input = NULL;
     xmlNode *output = NULL;
     xmlNode *result_cib = NULL;
     xmlNode *current_cib = NULL;
 
     int call_type = 0;
     int call_options = 0;
 
     const char *op = NULL;
     const char *section = NULL;
     const char *call_id = crm_element_value(request, F_CIB_CALLID);
 
     int rc = pcmk_ok;
     int rc2 = pcmk_ok;
 
     gboolean send_r_notify = FALSE;
     gboolean global_update = FALSE;
     gboolean config_changed = FALSE;
     gboolean manage_counters = TRUE;
 
     static mainloop_timer_t *digest_timer = NULL;
 
     CRM_ASSERT(cib_status == pcmk_ok);
 
     if(digest_timer == NULL) {
         digest_timer = mainloop_timer_add("digester", 5000, FALSE, cib_digester_cb, NULL);
     }
 
     *reply = NULL;
     *cib_diff = NULL;
     current_cib = the_cib;
 
     /* Start processing the request... */
     op = crm_element_value(request, F_CIB_OPERATION);
     crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
     rc = cib_get_operation_id(op, &call_type);
 
     if (rc == pcmk_ok && privileged == FALSE) {
         rc = cib_op_can_run(call_type, call_options, privileged, global_update);
     }
 
     rc2 = cib_op_prepare(call_type, request, &input, &section);
     if (rc == pcmk_ok) {
         rc = rc2;
     }
 
     if (rc != pcmk_ok) {
         crm_trace("Call setup failed: %s", pcmk_strerror(rc));
         goto done;
 
     } else if (cib_op_modifies(call_type) == FALSE) {
         rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE,
                             section, request, input, FALSE, &config_changed,
                             current_cib, &result_cib, NULL, &output);
 
         CRM_CHECK(result_cib == NULL, free_xml(result_cib));
         goto done;
     }
 
     /* Handle a valid write action */
     global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE));
     if (global_update) {
         /* legacy code */
         manage_counters = FALSE;
         call_options |= cib_force_diff;
         crm_trace("Global update detected");
 
         CRM_CHECK(call_type == 3 || call_type == 4, crm_err("Call type: %d", call_type);
                   crm_log_xml_err(request, "bad op"));
     }
 
     if (rc == pcmk_ok) {
         ping_modified_since = TRUE;
         if (call_options & cib_inhibit_bcast) {
             /* skip */
             crm_trace("Skipping update: inhibit broadcast");
             manage_counters = FALSE;
         }
 
         if (is_not_set(call_options, cib_dryrun) && safe_str_eq(section, XML_CIB_TAG_STATUS)) {
             /* Copying large CIBs accounts for a huge percentage of our CIB usage */
             call_options |= cib_zero_copy;
         } else {
             clear_bit(call_options, cib_zero_copy);
         }
 
         /* result_cib must not be modified after cib_perform_op() returns */
         rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE,
                             section, request, input, manage_counters, &config_changed,
                             current_cib, &result_cib, cib_diff, &output);
 
         if (manage_counters == FALSE) {
             int format = 1;
             /* Legacy code
              * If the diff is NULL at this point, it's because nothing changed
              */
             if (*cib_diff) {
                 crm_element_value_int(*cib_diff, "format", &format);
             }
 
             if (format == 1) {
                 config_changed = cib_config_changed(NULL, NULL, cib_diff);
             }
         }
 
         /* Always write to disk for replace ops,
          * this also negates the need to detect ordering changes
          */
         if (crm_str_eq(CIB_OP_REPLACE, op, TRUE)) {
             config_changed = TRUE;
         }
     }
 
     if (rc == pcmk_ok && is_not_set(call_options, cib_dryrun)) {
         crm_trace("Activating %s->%s%s%s",
                   crm_element_value(current_cib, XML_ATTR_NUMUPDATES),
                   crm_element_value(result_cib, XML_ATTR_NUMUPDATES),
                   (is_set(call_options, cib_zero_copy)? " zero-copy" : ""),
                   (config_changed? " changed" : ""));
         if(is_not_set(call_options, cib_zero_copy)) {
             rc = activateCibXml(result_cib, config_changed, op);
             crm_trace("Activated %s (%d)",
                       crm_element_value(current_cib, XML_ATTR_NUMUPDATES), rc);
         }
 
         if (rc == pcmk_ok && cib_internal_config_changed(*cib_diff)) {
             cib_read_config(config_hash, result_cib);
         }
 
         if (crm_str_eq(CIB_OP_REPLACE, op, TRUE)) {
             if (section == NULL) {
                 send_r_notify = TRUE;
 
             } else if (safe_str_eq(section, XML_TAG_CIB)) {
                 send_r_notify = TRUE;
 
             } else if (safe_str_eq(section, XML_CIB_TAG_NODES)) {
                 send_r_notify = TRUE;
 
             } else if (safe_str_eq(section, XML_CIB_TAG_STATUS)) {
                 send_r_notify = TRUE;
             }
 
         } else if (crm_str_eq(CIB_OP_ERASE, op, TRUE)) {
             send_r_notify = TRUE;
         }
 
         mainloop_timer_stop(digest_timer);
         mainloop_timer_start(digest_timer);
 
     } else if (rc == -pcmk_err_schema_validation) {
         CRM_ASSERT(is_not_set(call_options, cib_zero_copy));
 
         if (output != NULL) {
             crm_log_xml_info(output, "cib:output");
             free_xml(output);
         }
 
         output = result_cib;
 
     } else {
         crm_trace("Not activating %d %d %s", rc, is_set(call_options, cib_dryrun), crm_element_value(result_cib, XML_ATTR_NUMUPDATES));
         if(is_not_set(call_options, cib_zero_copy)) {
             free_xml(result_cib);
         }
     }
 
     if ((call_options & (cib_inhibit_notify|cib_dryrun)) == 0) {
         const char *client = crm_element_value(request, F_CIB_CLIENTNAME);
 
         crm_trace("Sending notifications %d", is_set(call_options, cib_dryrun));
         cib_diff_notify(call_options, client, call_id, op, input, rc, *cib_diff);
     }
 
     if (send_r_notify) {
         const char *origin = crm_element_value(request, F_ORIG);
 
         cib_replace_notify(origin, the_cib, rc, *cib_diff);
     }
 
     xml_log_patchset(LOG_TRACE, "cib:diff", *cib_diff);
   done:
     if ((call_options & cib_discard_reply) == 0) {
         const char *caller = crm_element_value(request, F_CIB_CLIENTID);
 
         *reply = create_xml_node(NULL, "cib-reply");
         crm_xml_add(*reply, F_TYPE, T_CIB);
         crm_xml_add(*reply, F_CIB_OPERATION, op);
         crm_xml_add(*reply, F_CIB_CALLID, call_id);
         crm_xml_add(*reply, F_CIB_CLIENTID, caller);
         crm_xml_add_int(*reply, F_CIB_CALLOPTS, call_options);
         crm_xml_add_int(*reply, F_CIB_RC, rc);
 
         if (output != NULL) {
             crm_trace("Attaching reply output");
             add_message_xml(*reply, F_CIB_CALLDATA, output);
         }
 
         crm_log_xml_explicit(*reply, "cib:reply");
     }
 
     crm_trace("cleanup");
 
     if (cib_op_modifies(call_type) == FALSE && output != current_cib) {
         free_xml(output);
         output = NULL;
     }
 
     if (call_type >= 0) {
         cib_op_cleanup(call_type, call_options, &input, &output);
     }
 
     crm_trace("done");
     return rc;
 }
 
 gint
 cib_GCompareFunc(gconstpointer a, gconstpointer b)
 {
     const xmlNode *a_msg = a;
     const xmlNode *b_msg = b;
 
     int msg_a_id = 0;
     int msg_b_id = 0;
     const char *value = NULL;
 
-    value = crm_element_value_const(a_msg, F_CIB_CALLID);
+    value = crm_element_value(a_msg, F_CIB_CALLID);
     msg_a_id = crm_parse_int(value, NULL);
 
-    value = crm_element_value_const(b_msg, F_CIB_CALLID);
+    value = crm_element_value(b_msg, F_CIB_CALLID);
     msg_b_id = crm_parse_int(value, NULL);
 
     if (msg_a_id == msg_b_id) {
         return 0;
     } else if (msg_a_id < msg_b_id) {
         return -1;
     }
     return 1;
 }
 
 void
 cib_peer_callback(xmlNode * msg, void *private_data)
 {
     const char *reason = NULL;
     const char *originator = crm_element_value(msg, F_ORIG);
 
     if (cib_legacy_mode() && (originator == NULL || crm_str_eq(originator, cib_our_uname, TRUE))) {
         /* message is from ourselves */
         int bcast_id = 0;
 
         if (!(crm_element_value_int(msg, F_CIB_LOCAL_NOTIFY_ID, &bcast_id))) {
             check_local_notify(bcast_id);
         }
         return;
 
     } else if (crm_peer_cache == NULL) {
         reason = "membership not established";
         goto bail;
     }
 
     if (crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) {
         crm_xml_add(msg, F_CIB_CLIENTNAME, originator);
     }
 
     /* crm_log_xml_trace("Peer[inbound]", msg); */
     cib_process_request(msg, FALSE, TRUE, TRUE, NULL);
     return;
 
   bail:
     if (reason) {
         const char *seq = crm_element_value(msg, F_SEQ);
         const char *op = crm_element_value(msg, F_CIB_OPERATION);
 
         crm_warn("Discarding %s message (%s) from %s: %s", op, seq, originator, reason);
     }
 }
 
 gboolean
 can_write(int flags)
 {
     return TRUE;
 }
 
 static gboolean
 cib_force_exit(gpointer data)
 {
     crm_notice("Forcing exit!");
     terminate_cib(__FUNCTION__, CRM_EX_ERROR);
     return FALSE;
 }
 
 static void
 disconnect_remote_client(gpointer key, gpointer value, gpointer user_data)
 {
     crm_client_t *a_client = value;
 
     crm_err("Disconnecting %s... Not implemented", crm_str(a_client->name));
 }
 
 void
 cib_shutdown(int nsig)
 {
     struct qb_ipcs_stats srv_stats;
 
     if (cib_shutdown_flag == FALSE) {
         int disconnects = 0;
         qb_ipcs_connection_t *c = NULL;
 
         cib_shutdown_flag = TRUE;
 
         c = qb_ipcs_connection_first_get(ipcs_rw);
         while (c != NULL) {
             qb_ipcs_connection_t *last = c;
 
             c = qb_ipcs_connection_next_get(ipcs_rw, last);
 
             crm_debug("Disconnecting r/w client %p...", last);
             qb_ipcs_disconnect(last);
             qb_ipcs_connection_unref(last);
             disconnects++;
         }
 
         c = qb_ipcs_connection_first_get(ipcs_ro);
         while (c != NULL) {
             qb_ipcs_connection_t *last = c;
 
             c = qb_ipcs_connection_next_get(ipcs_ro, last);
 
             crm_debug("Disconnecting r/o client %p...", last);
             qb_ipcs_disconnect(last);
             qb_ipcs_connection_unref(last);
             disconnects++;
         }
 
         c = qb_ipcs_connection_first_get(ipcs_shm);
         while (c != NULL) {
             qb_ipcs_connection_t *last = c;
 
             c = qb_ipcs_connection_next_get(ipcs_shm, last);
 
             crm_debug("Disconnecting non-blocking r/w client %p...", last);
             qb_ipcs_disconnect(last);
             qb_ipcs_connection_unref(last);
             disconnects++;
         }
 
         disconnects += crm_hash_table_size(client_connections);
 
         crm_debug("Disconnecting %d remote clients", crm_hash_table_size(client_connections));
         g_hash_table_foreach(client_connections, disconnect_remote_client, NULL);
         crm_info("Disconnected %d clients", disconnects);
     }
 
     qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE);
 
     if (crm_hash_table_size(client_connections) == 0) {
         crm_info("All clients disconnected (%d)", srv_stats.active_connections);
         initiate_exit();
 
     } else {
         crm_info("Waiting on %d clients to disconnect (%d)",
                  crm_hash_table_size(client_connections), srv_stats.active_connections);
     }
 }
 
 void
 initiate_exit(void)
 {
     int active = 0;
     xmlNode *leaving = NULL;
 
     active = crm_active_peers();
     if (active < 2) {
         terminate_cib(__FUNCTION__, 0);
         return;
     }
 
     crm_info("Sending disconnect notification to %d peers...", active);
 
     leaving = create_xml_node(NULL, "exit-notification");
     crm_xml_add(leaving, F_TYPE, "cib");
     crm_xml_add(leaving, F_CIB_OPERATION, "cib_shutdown_req");
 
     send_cluster_message(NULL, crm_msg_cib, leaving, TRUE);
     free_xml(leaving);
 
     g_timeout_add(crm_get_msec("5s"), cib_force_exit, NULL);
 }
 
 extern int remote_fd;
 extern int remote_tls_fd;
 
 /*!
  * \internal
  * \brief Close remote sockets, free the global CIB and quit
  *
  * \param[in] caller           Name of calling function (for log message)
  * \param[in] fast             If -1, skip disconnect; if positive, exit that
  */
 void
 terminate_cib(const char *caller, int fast)
 {
     crm_info("%s: Exiting%s...", caller,
              (fast > 0)? " fast" : mainloop ? " from mainloop" : "");
 
     if (remote_fd > 0) {
         close(remote_fd);
         remote_fd = 0;
     }
     if (remote_tls_fd > 0) {
         close(remote_tls_fd);
         remote_tls_fd = 0;
     }
 
     uninitializeCib();
 
     if (fast > 0) {
         /* Quit fast on error */
         cib_ipc_servers_destroy(ipcs_ro, ipcs_rw, ipcs_shm);
         crm_exit(fast);
 
     } else if ((mainloop != NULL) && g_main_is_running(mainloop)) {
         /* Quit via returning from the main loop. If fast == -1, we skip the
          * disconnect here, and it will be done when the main loop returns
          * (this allows the peer status callback to avoid messing with the
          * peer caches).
          */
         if (fast == 0) {
             crm_cluster_disconnect(&crm_cluster);
         }
         g_main_loop_quit(mainloop);
 
     } else {
         /* Quit via clean exit. Even the peer status callback can disconnect
          * here, because we're not returning control to the caller. */
         crm_cluster_disconnect(&crm_cluster);
         cib_ipc_servers_destroy(ipcs_ro, ipcs_rw, ipcs_shm);
         crm_exit(CRM_EX_OK);
     }
 }
diff --git a/crmd/te_callbacks.c b/crmd/te_callbacks.c
index 9c1897a66c..fd75368cb9 100644
--- a/crmd/te_callbacks.c
+++ b/crmd/te_callbacks.c
@@ -1,920 +1,920 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/stat.h>
 
 #include <crm/crm.h>
 #include <crm/common/xml.h>
 #include <crm/msg_xml.h>
 
 #include <tengine.h>
 #include <te_callbacks.h>
 #include <crmd_fsa.h>
 
 #include <crm/cluster.h>        /* For ONLINESTATUS etc */
 
 void te_update_confirm(const char *event, xmlNode * msg);
 
 extern char *te_uuid;
 gboolean shuttingdown = FALSE;
 crm_graph_t *transition_graph;
 crm_trigger_t *transition_trigger = NULL;
 
 static unsigned long int stonith_max_attempts = 10;
 
 /* #define rsc_op_template "//"XML_TAG_DIFF_ADDED"//"XML_TAG_CIB"//"XML_CIB_TAG_STATE"[@uname='%s']"//"XML_LRM_TAG_RSC_OP"[@id='%s]" */
 #define rsc_op_template "//"XML_TAG_DIFF_ADDED"//"XML_TAG_CIB"//"XML_LRM_TAG_RSC_OP"[@id='%s']"
 
 static const char *
 get_node_id(xmlNode * rsc_op)
 {
     xmlNode *node = rsc_op;
 
     while (node != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(node))) {
         node = node->parent;
     }
 
     CRM_CHECK(node != NULL, return NULL);
     return ID(node);
 }
 
 void
 update_stonith_max_attempts(const char* value)
 {
     if (safe_str_eq(value, CRM_INFINITY_S)) {
        stonith_max_attempts = CRM_SCORE_INFINITY;
     }
     else {
        stonith_max_attempts = crm_int_helper(value, NULL);
     }
 }
 static void
 te_legacy_update_diff(const char *event, xmlNode * diff)
 {
     int lpc, max;
     xmlXPathObject *xpathObj = NULL;
 
     CRM_CHECK(diff != NULL, return);
 
     xml_log_patchset(LOG_TRACE, __FUNCTION__, diff);
     if (cib_config_changed(NULL, NULL, &diff)) {
         abort_transition(INFINITY, tg_restart, "Non-status change", diff);
         goto bail;              /* configuration changed */
     }
 
     /* Tickets Attributes - Added/Updated */
     xpathObj =
         xpath_search(diff,
                      "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_TICKETS);
     if (numXpathResults(xpathObj) > 0) {
         xmlNode *aborted = getXpathResult(xpathObj, 0);
 
         abort_transition(INFINITY, tg_restart, "Ticket attribute: update", aborted);
         goto bail;
 
     }
     freeXpathObject(xpathObj);
 
     /* Tickets Attributes - Removed */
     xpathObj =
         xpath_search(diff,
                      "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_TICKETS);
     if (numXpathResults(xpathObj) > 0) {
         xmlNode *aborted = getXpathResult(xpathObj, 0);
 
         abort_transition(INFINITY, tg_restart, "Ticket attribute: removal", aborted);
         goto bail;
     }
     freeXpathObject(xpathObj);
 
     /* Transient Attributes - Added/Updated */
     xpathObj =
         xpath_search(diff,
                      "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                      XML_TAG_TRANSIENT_NODEATTRS "//" XML_CIB_TAG_NVPAIR);
     max = numXpathResults(xpathObj);
 
     for (lpc = 0; lpc < max; lpc++) {
         xmlNode *attr = getXpathResult(xpathObj, lpc);
         const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
         const char *value = NULL;
 
         if (safe_str_eq(CRM_OP_PROBED, name)) {
             value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
         }
 
         if (crm_is_true(value) == FALSE) {
             abort_transition(INFINITY, tg_restart, "Transient attribute: update", attr);
             crm_log_xml_trace(attr, "Abort");
             goto bail;
         }
     }
 
     freeXpathObject(xpathObj);
 
     /* Transient Attributes - Removed */
     xpathObj =
         xpath_search(diff,
                      "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//"
                      XML_TAG_TRANSIENT_NODEATTRS);
     if (numXpathResults(xpathObj) > 0) {
         xmlNode *aborted = getXpathResult(xpathObj, 0);
 
         abort_transition(INFINITY, tg_restart, "Transient attribute: removal", aborted);
         goto bail;
 
     }
     freeXpathObject(xpathObj);
 
     /*
      * Check for and fast-track the processing of LRM refreshes
      * In large clusters this can result in _huge_ speedups
      *
      * Unfortunately we can only do so when there are no pending actions
      * Otherwise we could miss updates we're waiting for and stall
      *
      */
     xpathObj = NULL;
     if (transition_graph->pending == 0) {
         xpathObj =
             xpath_search(diff,
                          "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//"
                          XML_LRM_TAG_RESOURCE);
     }
 
     max = numXpathResults(xpathObj);
     if (max > 1) {
         /* Updates by, or in response to, TE actions will never contain updates
          * for more than one resource at a time
          */
         crm_debug("Detected LRM refresh - %d resources updated: Skipping all resource events", max);
         crm_log_xml_trace(diff, "lrm-refresh");
         abort_transition(INFINITY, tg_restart, "LRM Refresh", NULL);
         goto bail;
     }
     freeXpathObject(xpathObj);
 
     /* Process operation updates */
     xpathObj =
         xpath_search(diff,
                      "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_LRM_TAG_RSC_OP);
     if (numXpathResults(xpathObj)) {
 /*
     <status>
        <node_state id="node1" state=CRMD_JOINSTATE_MEMBER exp_state="active">
           <lrm>
              <lrm_resources>
         	<rsc_state id="" rsc_id="rsc4" node_id="node1" rsc_state="stopped"/>
 */
         int lpc = 0, max = numXpathResults(xpathObj);
 
         for (lpc = 0; lpc < max; lpc++) {
             xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
             const char *node = get_node_id(rsc_op);
 
             process_graph_event(rsc_op, node);
         }
     }
     freeXpathObject(xpathObj);
 
     /* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */
     xpathObj = xpath_search(diff, "//" XML_TAG_DIFF_REMOVED "//" XML_LRM_TAG_RSC_OP);
     max = numXpathResults(xpathObj);
     for (lpc = 0; lpc < max; lpc++) {
         int path_max = 0;
         const char *op_id = NULL;
         char *rsc_op_xpath = NULL;
         xmlXPathObject *op_match = NULL;
         xmlNode *match = getXpathResult(xpathObj, lpc);
 
         CRM_LOG_ASSERT(match != NULL);
         if(match == NULL) { continue; };
 
         op_id = ID(match);
 
         path_max = strlen(rsc_op_template) + strlen(op_id) + 1;
         rsc_op_xpath = calloc(1, path_max);
         snprintf(rsc_op_xpath, path_max, rsc_op_template, op_id);
 
         op_match = xpath_search(diff, rsc_op_xpath);
         if (numXpathResults(op_match) == 0) {
             /* Prevent false positives by matching cancelations too */
             const char *node = get_node_id(match);
             crm_action_t *cancelled = get_cancel_action(op_id, node);
 
             if (cancelled == NULL) {
                 crm_debug("No match for deleted action %s (%s on %s)", rsc_op_xpath, op_id,
                           node);
                 abort_transition(INFINITY, tg_restart, "Resource op removal", match);
                 freeXpathObject(op_match);
                 free(rsc_op_xpath);
                 goto bail;
 
             } else {
                 crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
                           op_id, node, cancelled->id);
             }
         }
 
         freeXpathObject(op_match);
         free(rsc_op_xpath);
     }
 
   bail:
     freeXpathObject(xpathObj);
 }
 
 static void process_resource_updates(
     const char *node, xmlNode *xml, xmlNode *change, const char *op, const char *xpath) 
 {
     xmlNode *cIter = NULL;
     xmlNode *rsc = NULL;
     xmlNode *rsc_op = NULL;
     int num_resources = 0;
 
     if(xml == NULL) {
         return;
 
     } else if(strcmp((const char*)xml->name, XML_CIB_TAG_LRM) == 0) {
         xml = first_named_child(xml, XML_LRM_TAG_RESOURCES);
         crm_trace("Got %p in %s", xml, XML_CIB_TAG_LRM);
     }
 
     CRM_ASSERT(strcmp((const char*)xml->name, XML_LRM_TAG_RESOURCES) == 0);
 
     for(cIter = xml->children; cIter; cIter = cIter->next) {
         num_resources++;
     }
 
     if(num_resources > 1) {
         /*
          * Check for and fast-track the processing of LRM refreshes
          * In large clusters this can result in _huge_ speedups
          *
          * Unfortunately we can only do so when there are no pending actions
          * Otherwise we could miss updates we're waiting for and stall
          *
          */
 
         crm_debug("Detected LRM refresh - %d resources updated", num_resources);
         crm_log_xml_trace(change, "lrm-refresh");
         abort_transition(INFINITY, tg_restart, "LRM Refresh", NULL);
         return;
     }
 
     for (rsc = __xml_first_child(xml); rsc != NULL; rsc = __xml_next(rsc)) {
         crm_trace("Processing %s", ID(rsc));
         for (rsc_op = __xml_first_child(rsc); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
             crm_trace("Processing %s", ID(rsc_op));
             process_graph_event(rsc_op, node);
         }
     }
 }
 
 #define NODE_PATT "/lrm[@id="
 static char *get_node_from_xpath(const char *xpath) 
 {
     char *nodeid = NULL;
     char *tmp = strstr(xpath, NODE_PATT);
 
     if(tmp) {
         tmp += strlen(NODE_PATT);
         tmp += 1;
 
         nodeid = strdup(tmp);
         tmp = strstr(nodeid, "\'");
         CRM_ASSERT(tmp);
         tmp[0] = 0;
     }
     return nodeid;
 }
 
 static char *extract_node_uuid(const char *xpath) 
 {
     char *mutable_path = strdup(xpath);
     char *node_uuid = NULL;
     char *search = NULL;
     char *match = NULL;
 
     match = strstr(mutable_path, "node_state[@id=\'");
     if (match == NULL) {
         free(mutable_path);
         return NULL;
     }
     match += strlen("node_state[@id=\'");
 
     search = strchr(match, '\'');
     if (search == NULL) {
         free(mutable_path);
         return NULL;
     }
     search[0] = 0;
 
     node_uuid = strdup(match);
     free(mutable_path);
     return node_uuid;
 }
 
 static void
 abort_unless_down(const char *xpath, const char *op, xmlNode *change,
                   const char *reason)
 {
     char *node_uuid = NULL;
     crm_action_t *down = NULL;
 
     if(safe_str_neq(op, "delete")) {
         abort_transition(INFINITY, tg_restart, reason, change);
         return;
     }
 
     node_uuid = extract_node_uuid(xpath);
     if(node_uuid == NULL) {
         crm_err("Could not extract node ID from %s", xpath);
         abort_transition(INFINITY, tg_restart, reason, change);
         return;
     }
 
     down = match_down_event(node_uuid, TRUE);
     if(down == NULL || down->executed == false) {
         crm_trace("Not expecting %s to be down (%s)", node_uuid, xpath);
         abort_transition(INFINITY, tg_restart, reason, change);
     } else {
         crm_trace("Expecting changes to %s (%s)", node_uuid, xpath);
     }
     free(node_uuid);
 }
 
 void
 te_update_diff(const char *event, xmlNode * msg)
 {
     int rc = -EINVAL;
     int format = 1;
     xmlNode *change = NULL;
     const char *op = NULL;
 
     xmlNode *diff = NULL;
 
     int p_add[] = { 0, 0, 0 };
     int p_del[] = { 0, 0, 0 };
 
     CRM_CHECK(msg != NULL, return);
     crm_element_value_int(msg, F_CIB_RC, &rc);
 
     if (transition_graph == NULL) {
         crm_trace("No graph");
         return;
 
     } else if (rc < pcmk_ok) {
         crm_trace("Filter rc=%d (%s)", rc, pcmk_strerror(rc));
         return;
 
     } else if (transition_graph->complete == TRUE
                && fsa_state != S_IDLE
                && fsa_state != S_TRANSITION_ENGINE && fsa_state != S_POLICY_ENGINE) {
         crm_trace("Filter state=%s, complete=%d", fsa_state2string(fsa_state),
                   transition_graph->complete);
         return;
     }
 
     op = crm_element_value(msg, F_CIB_OPERATION);
     diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 
     xml_patch_versions(diff, p_add, p_del);
     crm_debug("Processing (%s) diff: %d.%d.%d -> %d.%d.%d (%s)", op,
               p_del[0], p_del[1], p_del[2], p_add[0], p_add[1], p_add[2],
               fsa_state2string(fsa_state));
 
     crm_element_value_int(diff, "format", &format);
     switch(format) {
         case 1:
             te_legacy_update_diff(event, diff);
             return;
         case 2:
             /* Cool, we know what to do here */
             crm_log_xml_trace(diff, "Patch:Raw");
             break;
         default:
             crm_warn("Unknown patch format: %d", format);
             return;
     }
 
     for (change = __xml_first_child(diff); change != NULL; change = __xml_next(change)) {
         const char *name = NULL;
         const char *op = crm_element_value(change, XML_DIFF_OP);
         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
         xmlNode *match = NULL;
         const char *node = NULL;
 
         if(op == NULL) {
             continue;
 
         } else if(strcmp(op, "create") == 0) {
             match = change->children;
 
         } else if(strcmp(op, "move") == 0) {
             continue;
 
         } else if(strcmp(op, "modify") == 0) {
             match = first_named_child(change, XML_DIFF_RESULT);
             if(match) {
                 match = match->children;
             }
         }
 
         if(match) {
             if (match->type == XML_COMMENT_NODE) {
                 crm_trace("Ignoring %s operation for comment at %s", op, xpath);
                 continue;
             }
             name = (const char *)match->name;
         }
 
         crm_trace("Handling %s operation for %s%s%s",
                   op, (xpath? xpath : "CIB"),
                   (name? " matched by " : ""), (name? name : ""));
         if(xpath == NULL) {
             /* Version field, ignore */
 
         } else if(strstr(xpath, "/cib/configuration")) {
             abort_transition(INFINITY, tg_restart, "Configuration change", change);
             break; /* Won't be packaged with any resource operations we may be waiting for */
 
         } else if(strstr(xpath, "/"XML_CIB_TAG_TICKETS) || safe_str_eq(name, XML_CIB_TAG_TICKETS)) {
             abort_transition(INFINITY, tg_restart, "Ticket attribute change", change);
             break; /* Won't be packaged with any resource operations we may be waiting for */
 
         } else if(strstr(xpath, "/"XML_TAG_TRANSIENT_NODEATTRS"[") || safe_str_eq(name, XML_TAG_TRANSIENT_NODEATTRS)) {
             abort_unless_down(xpath, op, change, "Transient attribute change");
             break; /* Won't be packaged with any resource operations we may be waiting for */
 
         } else if(strstr(xpath, "/"XML_LRM_TAG_RSC_OP"[") && safe_str_eq(op, "delete")) {
             crm_action_t *cancel = NULL;
             char *mutable_key = strdup(xpath);
             char *key, *node_uuid;
 
             /* Extract the part of xpath between last pair of single quotes */
             key = strrchr(mutable_key, '\'');
             if (key != NULL) {
                 *key = '\0';
                 key = strrchr(mutable_key, '\'');
             }
             if (key == NULL) {
                 crm_warn("Ignoring malformed CIB update (resource deletion)");
                 free(mutable_key);
                 continue;
             }
             ++key;
 
             node_uuid = extract_node_uuid(xpath);
             cancel = get_cancel_action(key, node_uuid);
             if (cancel == NULL) {
                 abort_transition(INFINITY, tg_restart, "Resource operation removal", change);
 
             } else {
                 crm_info("Cancellation of %s on %s confirmed (%d)", key, node_uuid, cancel->id);
                 stop_te_timer(cancel->timer);
                 te_action_confirmed(cancel);
 
                 update_graph(transition_graph, cancel);
                 trigger_graph();
 
             }
             free(mutable_key);
             free(node_uuid);
 
         } else if(strstr(xpath, "/"XML_CIB_TAG_LRM"[") && safe_str_eq(op, "delete")) {
             abort_unless_down(xpath, op, change, "Resource state removal");
 
         } else if(strstr(xpath, "/"XML_CIB_TAG_STATE"[") && safe_str_eq(op, "delete")) {
             abort_unless_down(xpath, op, change, "Node state removal");
 
         } else if(name == NULL) {
             crm_debug("No result for %s operation to %s", op, xpath);
             CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
 
         } else if(strcmp(name, XML_TAG_CIB) == 0) {
             xmlNode *state = NULL;
             xmlNode *status = first_named_child(match, XML_CIB_TAG_STATUS);
             xmlNode *config = first_named_child(match, XML_CIB_TAG_CONFIGURATION);
 
             for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
                 xmlNode *lrm = first_named_child(state, XML_CIB_TAG_LRM);
 
                 node = ID(state);
                 process_resource_updates(node, lrm, change, op, xpath);
             }
 
             if(config) {
                 abort_transition(INFINITY, tg_restart, "Non-status-only change", change);
             }
 
         } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
             xmlNode *state = NULL;
 
             for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
                 xmlNode *lrm = first_named_child(state, XML_CIB_TAG_LRM);
 
                 node = ID(state);
                 process_resource_updates(node, lrm, change, op, xpath);
             }
 
         } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
             xmlNode *lrm = first_named_child(match, XML_CIB_TAG_LRM);
 
             node = ID(match);
             process_resource_updates(node, lrm, change, op, xpath);
 
         } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
             node = ID(match);
             process_resource_updates(node, match, change, op, xpath);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
             char *local_node = get_node_from_xpath(xpath);
 
             process_resource_updates(local_node, match, change, op, xpath);
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
 
             xmlNode *rsc_op;
             char *local_node = get_node_from_xpath(xpath);
 
             for (rsc_op = __xml_first_child(match); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
                 process_graph_event(rsc_op, local_node);
             }
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
             char *local_node = get_node_from_xpath(xpath);
 
             process_graph_event(match, local_node);
             free(local_node);
 
         } else {
             crm_err("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
         }
     }
 }
 
 
 gboolean
 process_te_message(xmlNode * msg, xmlNode * xml_data)
 {
     const char *from = crm_element_value(msg, F_ORIG);
     const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
     const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM);
     const char *ref = crm_element_value(msg, F_CRM_REFERENCE);
     const char *op = crm_element_value(msg, F_CRM_TASK);
     const char *type = crm_element_value(msg, F_CRM_MSG_TYPE);
 
     crm_trace("Processing %s (%s) message", op, ref);
     crm_log_xml_trace(msg, "ipc");
 
     if (op == NULL) {
         /* error */
 
     } else if (sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_TENGINE) != 0) {
         crm_trace("Bad sys-to %s", crm_str(sys_to));
         return FALSE;
 
     } else if (safe_str_eq(op, CRM_OP_INVOKE_LRM)
                && safe_str_eq(sys_from, CRM_SYSTEM_LRMD)
 /* 		  && safe_str_eq(type, XML_ATTR_RESPONSE) */
         ) {
         xmlXPathObject *xpathObj = NULL;
 
         crm_log_xml_trace(msg, "Processing (N)ACK");
         crm_debug("Processing (N)ACK %s from %s", crm_element_value(msg, F_CRM_REFERENCE), from);
 
         xpathObj = xpath_search(xml_data, "//" XML_LRM_TAG_RSC_OP);
         if (numXpathResults(xpathObj)) {
             int lpc = 0, max = numXpathResults(xpathObj);
 
             for (lpc = 0; lpc < max; lpc++) {
                 xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
                 const char *node = get_node_id(rsc_op);
 
                 process_graph_event(rsc_op, node);
             }
             freeXpathObject(xpathObj);
 
         } else {
             crm_log_xml_err(msg, "Invalid (N)ACK");
             freeXpathObject(xpathObj);
             return FALSE;
         }
 
     } else {
         crm_err("Unknown command: %s::%s from %s", type, op, sys_from);
     }
 
     crm_trace("finished processing message");
 
     return TRUE;
 }
 
 GHashTable *stonith_failures = NULL;
 struct st_fail_rec {
     int count;
 };
 
 static gboolean
 too_many_st_failures(const char *target)
 {
     GHashTableIter iter;
     const char *key = NULL;
     struct st_fail_rec *value = NULL;
 
     if (stonith_failures == NULL) {
         return FALSE;
     }
 
     if (target == NULL) {
         g_hash_table_iter_init(&iter, stonith_failures);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             if (value->count >= stonith_max_attempts) {
                 target = (const char*)key;
                 goto too_many;
             }
         }
     } else {
         value = g_hash_table_lookup(stonith_failures, target);
         if ((value != NULL) && (value->count >= stonith_max_attempts)) {
             goto too_many;
         }
     }
     return FALSE;
 
 too_many:
     crm_warn("Too many failures (%d) to fence %s, giving up",
              value->count, target);
     return TRUE;
 }
 
 /*!
  * \internal
  * \brief Reset a stonith fail count
  *
  * \param[in] target  Name of node to reset, or NULL for all
  */
 void
 st_fail_count_reset(const char *target)
 {
     if (stonith_failures == NULL) {
         return;
     }
 
     if (target) {
         struct st_fail_rec *rec = NULL;
 
         rec = g_hash_table_lookup(stonith_failures, target);
         if (rec) {
             rec->count = 0;
         }
     } else {
         GHashTableIter iter;
         const char *key = NULL;
         struct st_fail_rec *rec = NULL;
 
         g_hash_table_iter_init(&iter, stonith_failures);
         while (g_hash_table_iter_next(&iter, (gpointer *) &key,
                                       (gpointer *) &rec)) {
             rec->count = 0;
         }
     }
 }
 
 void
 st_fail_count_increment(const char *target)
 {
     struct st_fail_rec *rec = NULL;
 
     if (stonith_failures == NULL) {
         stonith_failures = crm_str_table_new();
     }
 
     rec = g_hash_table_lookup(stonith_failures, target);
     if (rec) {
         rec->count++;
     } else {
         rec = malloc(sizeof(struct st_fail_rec));
         if(rec == NULL) {
             return;
         }
 
         rec->count = 1;
         g_hash_table_insert(stonith_failures, strdup(target), rec);
     }
 }
 
 /*!
  * \internal
  * \brief Abort transition due to stonith failure
  *
  * \param[in] abort_action  Whether to restart or stop transition
  * \param[in] target  Don't restart if this (NULL for any) has too many failures
  * \param[in] reason  Log this stonith action XML as abort reason (or NULL)
  */
 void
 abort_for_stonith_failure(enum transition_action abort_action,
                           const char *target, xmlNode *reason)
 {
     /* If stonith repeatedly fails, we eventually give up on starting a new
      * transition for that reason.
      */
     if ((abort_action != tg_stop) && too_many_st_failures(target)) {
         abort_action = tg_stop;
     }
     abort_transition(INFINITY, abort_action, "Stonith failed", reason);
 }
 
 void
 tengine_stonith_callback(stonith_t * stonith, stonith_callback_data_t * data)
 {
     char *uuid = NULL;
     int target_rc = -1;
     int stonith_id = -1;
     int transition_id = -1;
     crm_action_t *action = NULL;
     int call_id = data->call_id;
     int rc = data->rc;
     char *userdata = data->userdata;
 
     CRM_CHECK(userdata != NULL, return);
     crm_notice("Stonith operation %d/%s: %s (%d)", call_id, (char *)userdata,
                pcmk_strerror(rc), rc);
 
     if (AM_I_DC == FALSE) {
         return;
     }
 
     /* crm_info("call=%d, optype=%d, node_name=%s, result=%d, node_list=%s, action=%s", */
     /*       op->call_id, op->optype, op->node_name, op->op_result, */
     /*       (char *)op->node_list, op->private_data); */
 
     /* filter out old STONITH actions */
     CRM_CHECK(decode_transition_key(userdata, &uuid, &transition_id, &stonith_id, &target_rc),
               crm_err("Invalid event detected");
               goto bail;
         );
 
     if (transition_graph->complete || stonith_id < 0 || safe_str_neq(uuid, te_uuid)
         || transition_graph->id != transition_id) {
         crm_info("Ignoring STONITH action initiated outside of the current transition");
         goto bail;
     }
 
     action = get_action(stonith_id, FALSE);
     if (action == NULL) {
         crm_err("Stonith action not matched");
         goto bail;
     }
 
     stop_te_timer(action->timer);
     if (rc == pcmk_ok) {
         const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
         const char *uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
         const char *op = crm_meta_value(action->params, "stonith_action"); 
 
         crm_info("Stonith operation %d for %s passed", call_id, target);
         if (action->confirmed == FALSE) {
             te_action_confirmed(action);
             if (safe_str_eq("on", op)) {
                 const char *value = NULL;
                 char *now = crm_itoa(time(NULL));
 
                 update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE);
                 free(now);
 
                 value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
                 update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE);
 
                 value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
                 update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE);
 
             } else if (action->sent_update == FALSE) {
                 send_stonith_update(action, target, uuid);
                 action->sent_update = TRUE;
             }
         }
         st_fail_count_reset(target);
 
     } else {
-        const char *target = crm_element_value_const(action->xml, XML_LRM_ATTR_TARGET);
+        const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
         enum transition_action abort_action = tg_restart;
 
         action->failed = TRUE;
         crm_notice("Stonith operation %d for %s failed (%s): aborting transition.",
                    call_id, target, pcmk_strerror(rc));
 
         /* If no fence devices were available, there's no use in immediately
          * checking again, so don't start a new transition in that case.
          */
         if (rc == -ENODEV) {
             crm_warn("No devices found in cluster to fence %s, giving up",
                      target);
             abort_action = tg_stop;
         }
 
         /* Increment the fail count now, so abort_for_stonith_failure() can
          * check it. Non-DC nodes will increment it in tengine_stonith_notify().
          */
         st_fail_count_increment(target);
         abort_for_stonith_failure(abort_action, target, NULL);
     }
 
     update_graph(transition_graph, action);
     trigger_graph();
 
   bail:
     free(userdata);
     free(uuid);
     return;
 }
 
 void
 cib_fencing_updated(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     if (rc < pcmk_ok) {
         crm_err("Fencing update %d for %s: failed - %s (%d)",
                 call_id, (char *)user_data, pcmk_strerror(rc), rc);
         crm_log_xml_warn(msg, "Failed update");
         abort_transition(INFINITY, tg_shutdown, "CIB update failed", NULL);
 
     } else {
         crm_info("Fencing update %d for %s: complete", call_id, (char *)user_data);
     }
 }
 
 void
 cib_action_updated(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     if (rc < pcmk_ok) {
         crm_err("Update %d FAILED: %s", call_id, pcmk_strerror(rc));
     }
 }
 
 gboolean
 action_timer_callback(gpointer data)
 {
     crm_action_timer_t *timer = NULL;
 
     CRM_CHECK(data != NULL, return FALSE);
 
     timer = (crm_action_timer_t *) data;
     stop_te_timer(timer);
 
     crm_warn("Timer popped (timeout=%d, abort_level=%d, complete=%s)",
              timer->timeout,
              transition_graph->abort_priority, transition_graph->complete ? "true" : "false");
 
     CRM_CHECK(timer->action != NULL, return FALSE);
 
     if (transition_graph->complete) {
         crm_warn("Ignoring timeout while not in transition");
 
     } else {
         /* fail the action */
         gboolean send_update = TRUE;
         const char *task = crm_element_value(timer->action->xml, XML_LRM_ATTR_TASK);
 
         print_action(LOG_ERR, "Aborting transition, action lost: ", timer->action);
 
         timer->action->failed = TRUE;
         te_action_confirmed(timer->action);
         abort_transition(INFINITY, tg_restart, "Action lost", NULL);
 
         update_graph(transition_graph, timer->action);
         trigger_graph();
 
         if (timer->action->type != action_type_rsc) {
             send_update = FALSE;
         } else if (safe_str_eq(task, RSC_CANCEL)) {
             /* we don't need to update the CIB with these */
             send_update = FALSE;
         }
 
         if (send_update) {
             cib_action_update(timer->action, PCMK_LRM_OP_TIMEOUT, PCMK_OCF_UNKNOWN_ERROR);
         }
     }
 
     return FALSE;
 }
diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h
index 6acb8799ea..f6db7e8cf1 100644
--- a/include/crm/common/xml.h
+++ b/include/crm/common/xml.h
@@ -1,405 +1,391 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef CRM_COMMON_XML__H
 #  define CRM_COMMON_XML__H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
  * \file
  * \brief Wrappers for and extensions to libxml2
  * \ingroup core
  */
 
 #  include <stdio.h>
 #  include <sys/types.h>
 #  include <unistd.h>
 
 #  include <stdlib.h>
 #  include <errno.h>
 #  include <fcntl.h>
 
 #  include <crm/crm.h>
 
 #  include <libxml/tree.h>
 #  include <libxml/xpath.h>
 
 /* Define compression parameters for IPC messages
  *
  * Compression costs a LOT, so we don't want to do it unless we're hitting
  * message limits. Currently, we use 128KB as the threshold, because higher
  * values don't play well with the heartbeat stack. With an earlier limit of
  * 10KB, compressing 184 of 1071 messages accounted for 23% of the total CPU
  * used by the cib.
  */
 #  define CRM_BZ2_BLOCKS		4
 #  define CRM_BZ2_WORK		20
 #  define CRM_BZ2_THRESHOLD	128 * 1024
 
 #  define XML_PARANOIA_CHECKS 0
 
 gboolean add_message_xml(xmlNode * msg, const char *field, xmlNode * xml);
 xmlNode *get_message_xml(xmlNode * msg, const char *field);
 GHashTable *xml2list(xmlNode * parent);
 
 xmlNode *crm_create_nvpair_xml(xmlNode *parent, const char *id,
                                const char *name, const char *value);
 
 void hash2nvpair(gpointer key, gpointer value, gpointer user_data);
 void hash2field(gpointer key, gpointer value, gpointer user_data);
 void hash2metafield(gpointer key, gpointer value, gpointer user_data);
 void hash2smartfield(gpointer key, gpointer value, gpointer user_data);
 
 xmlDoc *getDocPtr(xmlNode * node);
 
 /*
  * Replacement function for xmlCopyPropList which at the very least,
  * doesn't work the way *I* would expect it to.
  *
  * Copy all the attributes/properties from src into target.
  *
  * Not recursive, does not return anything.
  *
  */
 void copy_in_properties(xmlNode * target, xmlNode * src);
 void expand_plus_plus(xmlNode * target, const char *name, const char *value);
 void fix_plus_plus_recursive(xmlNode * target);
 
 /*
  * Create a node named "name" as a child of "parent"
  * If parent is NULL, creates an unconnected node.
  *
  * Returns the created node
  *
  */
 xmlNode *create_xml_node(xmlNode * parent, const char *name);
 
 /*
  * Make a copy of name and value and use the copied memory to create
  * an attribute for node.
  *
  * If node, name or value are NULL, nothing is done.
  *
  * If name or value are an empty string, nothing is done.
  *
  * Returns FALSE on failure and TRUE on success.
  *
  */
 const char *crm_xml_add(xmlNode * node, const char *name, const char *value);
 
 const char *crm_xml_replace(xmlNode * node, const char *name, const char *value);
 
 const char *crm_xml_add_int(xmlNode * node, const char *name, int value);
 
 /*!
  * \brief Add a boolean attribute to an XML object
  *
  * Add an attribute with the value XML_BOOLEAN_TRUE or XML_BOOLEAN_FALSE
  * as appropriate to an XML object.
  *
  * \param[in,out] node   XML object to add attribute to
  * \param[in]     name   Name of attribute to add
  * \param[in]     value  Boolean whose value will be tested
  *
  * \return Pointer to newly created XML attribute's content, or NULL on error
  */
 static inline const char *
 crm_xml_add_boolean(xmlNode *node, const char *name, gboolean value)
 {
     return crm_xml_add(node, name, (value? "true" : "false"));
 }
 
 /*
  *
  */
 void purge_diff_markers(xmlNode * a_node);
 
 /*
  * Returns a deep copy of src_node
  *
  */
 xmlNode *copy_xml(xmlNode * src_node);
 
 /*
  * Add a copy of xml_node to new_parent
  */
 xmlNode *add_node_copy(xmlNode * new_parent, xmlNode * xml_node);
 
 int add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child);
 
 /*
  * XML I/O Functions
  *
  * Whitespace between tags is discarded.
  */
 xmlNode *filename2xml(const char *filename);
 
 xmlNode *stdin2xml(void);
 
 xmlNode *string2xml(const char *input);
 
 int write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress);
 int write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress);
 
 char *dump_xml_formatted(xmlNode * msg);
 /* Also dump the text node with xml_log_option_text enabled */ 
 char *dump_xml_formatted_with_text(xmlNode * msg);
 
 char *dump_xml_unformatted(xmlNode * msg);
 
 /*
  * Diff related Functions
  */
 xmlNode *diff_xml_object(xmlNode * left, xmlNode * right, gboolean suppress);
 
 xmlNode *subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
                              gboolean full, gboolean * changed, const char *marker);
 
 gboolean can_prune_leaf(xmlNode * xml_node);
 
 gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml);
 
 /*
  * Searching & Modifying
  */
 xmlNode *find_xml_node(xmlNode * cib, const char *node_path, gboolean must_find);
 
 xmlNode *find_entity(xmlNode * parent, const char *node_name, const char *id);
 
 void xml_remove_prop(xmlNode * obj, const char *name);
 
 gboolean replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update,
                            gboolean delete_only);
 
 gboolean update_xml_child(xmlNode * child, xmlNode * to_update);
 
 int find_xml_children(xmlNode ** children, xmlNode * root,
                       const char *tag, const char *field, const char *value,
                       gboolean search_matches);
 
-int crm_element_value_int(xmlNode * data, const char *name, int *dest);
-char *crm_element_value_copy(xmlNode * data, const char *name);
-int crm_element_value_const_int(const xmlNode * data, const char *name, int *dest);
-const char *crm_element_value_const(const xmlNode * data, const char *name);
+int crm_element_value_int(const xmlNode *data, const char *name, int *dest);
+char *crm_element_value_copy(const xmlNode *data, const char *name);
 xmlNode *get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level);
 xmlNode *get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level);
 
 static inline const char *
-crm_element_name(xmlNode *xml)
+crm_element_name(const xmlNode *xml)
 {
     return xml? (const char *)(xml->name) : NULL;
 }
 
-const char *crm_element_value(xmlNode * data, const char *name);
+const char *crm_element_value(const xmlNode *data, const char *name);
 
 /*!
  * \brief Copy an element from one XML object to another
  *
  * \param[in]     obj1     Source XML
  * \param[in,out] obj2     Destination XML
  * \param[in]     element  Name of element to copy
  *
  * \return Pointer to copied value (from source)
  */
 static inline const char *
 crm_copy_xml_element(xmlNode *obj1, xmlNode *obj2, const char *element)
 {
     const char *value = crm_element_value(obj1, element);
 
     crm_xml_add(obj2, element, value);
     return value;
 }
 
 gboolean xml_has_children(const xmlNode * root);
 
 char *calculate_on_disk_digest(xmlNode * local_cib);
 char *calculate_operation_digest(xmlNode * local_cib, const char *version);
 char *calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter,
                                      const char *version);
 
 /* schema-related functions (from schemas.c) */
 gboolean validate_xml(xmlNode * xml_blob, const char *validation, gboolean to_logs);
 gboolean validate_xml_verbose(xmlNode * xml_blob);
 
 /*!
  * \brief Update CIB XML to most recent schema version
  *
  * "Update" means either actively employ XSLT-based transformation(s)
  * (if intermediate product to transform valid per its declared schema version,
  * transformation available, proceeded successfully with a result valid per
  * expectated newer schema version), or just try to bump the marked validating
  * schema until all gradually rising schema versions attested or the first
  * such attempt subsequently fails to validate.   Which of the two styles will
  * be used depends on \p transform parameter (positive/negative, respectively).
  *
  * \param[in,out] xml_blob   XML tree representing CIB, may be swapped with
  *                           an "updated" one
  * \param[out]    best       The highest configuration version (per its index
  *                           in the global schemas table) it was possible to
  *                           reach during the update steps while ensuring
  *                           the validity of the result; if no validation
  *                           success was observed against possibly multiple
  *                           schemas, the value is less or equal the result
  *                           of <tt>get_schema_version</tt> applied on the
  *                           input \p xml_blob value (unless that function
  *                           maps it to -1, then 0 would be used instead)
  * \param[in]     max        When \p transform is positive, this allows to
  *                           set upper boundary schema (per its index in the
  *                           global schemas table) beyond which its forbidden
  *                           to update by the means of XSLT transformation
  * \param[in]     transform  Whether to employ XSLT-based transformation so
  *                           as allow overcoming possible incompatibilities
  *                           between major schema versions (see above)
  * \param[in]     to_logs    If true, output notable progress info to
  *                           internal log streams; if false, to stderr
  *
  * \return <tt>pcmk_ok</tt> if no non-recoverable error encountered (up to
  *         caller to evaluate if the update satisfies the requirements
  *         per returned \p best value), negative value carrying the reason
  *         otherwise
  */
 int update_validation(xmlNode **xml_blob, int *best, int max,
                       gboolean transform, gboolean to_logs);
 
 int get_schema_version(const char *name);
 const char *get_schema_name(int version);
 const char *xml_latest_schema(void);
 gboolean cli_config_update(xmlNode ** xml, int *best_version, gboolean to_logs);
 
 void crm_xml_init(void);
 void crm_xml_cleanup(void);
 
 static inline xmlNode *
-__xml_first_child(xmlNode * parent)
+__xml_first_child(const xmlNode *parent)
 {
-    xmlNode *child = NULL;
+    xmlNode *child = parent? parent->children : NULL;
 
-    if (parent) {
-        child = parent->children;
-        while (child && child->type == XML_TEXT_NODE) {
-            child = child->next;
-        }
+    while (child && (child->type == XML_TEXT_NODE)) {
+        child = child->next;
     }
     return child;
 }
 
 static inline xmlNode *
-__xml_next(xmlNode * child)
+__xml_next(const xmlNode *child)
 {
-    if (child) {
-        child = child->next;
-        while (child && child->type == XML_TEXT_NODE) {
-            child = child->next;
-        }
+    xmlNode *next = child? child->next : NULL;
+
+    while (next && (next->type == XML_TEXT_NODE)) {
+        next = next->next;
     }
-    return child;
+    return next;
 }
 
 static inline xmlNode *
-__xml_first_child_element(xmlNode * parent)
+__xml_first_child_element(const xmlNode *parent)
 {
-    xmlNode *child = NULL;
+    xmlNode *child = parent? parent->children : NULL;
 
-    if (parent) {
-        child = parent->children;
-    }
-
-    while (child) {
-        if(child->type == XML_ELEMENT_NODE) {
-            return child;
-        }
+    while (child && (child->type != XML_ELEMENT_NODE)) {
         child = child->next;
     }
-    return NULL;
+    return child;
 }
 
 static inline xmlNode *
-__xml_next_element(xmlNode * child)
+__xml_next_element(const xmlNode *child)
 {
-    while (child) {
-        child = child->next;
-        if(child && child->type == XML_ELEMENT_NODE) {
-            return child;
-        }
+    xmlNode *next = child? child->next : NULL;
+
+    while (next && (next->type != XML_ELEMENT_NODE)) {
+        next = next->next;
     }
-    return NULL;
+    return next;
 }
 
 void free_xml(xmlNode * child);
 
-xmlNode *first_named_child(xmlNode * parent, const char *name);
-xmlNode *crm_next_same_xml(xmlNode *sibling);
+xmlNode *first_named_child(const xmlNode *parent, const char *name);
+xmlNode *crm_next_same_xml(const xmlNode *sibling);
 
 xmlNode *sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive);
 xmlXPathObjectPtr xpath_search(xmlNode * xml_top, const char *path);
 void crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
                               void (*helper)(xmlNode*, void*), void *user_data);
 xmlNode *expand_idref(xmlNode * input, xmlNode * top);
 
 void freeXpathObject(xmlXPathObjectPtr xpathObj);
 xmlNode *getXpathResult(xmlXPathObjectPtr xpathObj, int index);
 void dedupXpathResults(xmlXPathObjectPtr xpathObj);
 
 static inline int numXpathResults(xmlXPathObjectPtr xpathObj)
 {
     if(xpathObj == NULL || xpathObj->nodesetval == NULL) {
         return 0;
     }
     return xpathObj->nodesetval->nodeNr;
 }
 
 bool xml_acl_enabled(xmlNode *xml);
 void xml_acl_disable(xmlNode *xml);
 bool xml_acl_denied(xmlNode *xml); /* Part or all of a change was rejected */
 bool xml_acl_filtered_copy(const char *user, xmlNode* acl_source, xmlNode *xml, xmlNode ** result);
 
 bool xml_tracking_changes(xmlNode * xml);
 bool xml_document_dirty(xmlNode *xml);
 void xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls);
 void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml);
 void xml_accept_changes(xmlNode * xml);
 void xml_log_changes(uint8_t level, const char *function, xmlNode *xml);
 void xml_log_patchset(uint8_t level, const char *function, xmlNode *xml);
 bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3]);
 
 xmlNode *xml_create_patchset(
     int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version);
 int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version);
 
 void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest);
 
 void save_xml_to_file(xmlNode * xml, const char *desc, const char *filename);
 char *xml_get_path(xmlNode *xml);
 
 char * crm_xml_escape(const char *text);
 void crm_xml_sanitize_id(char *id);
 void crm_xml_set_id(xmlNode *xml, const char *format, ...)
     __attribute__ ((__format__ (__printf__, 2, 3)));
 
 /*!
  * \brief xmlNode destructor which can be used in glib collections
  */
 void crm_destroy_xml(gpointer data);
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif
diff --git a/include/crm/compatibility.h b/include/crm/compatibility.h
index f889102b81..ae33e004da 100644
--- a/include/crm/compatibility.h
+++ b/include/crm/compatibility.h
@@ -1,218 +1,220 @@
 /*
  * Copyright (C) 2012-2018 Andrew Beekhof <andrew@beekhof.net>
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 #ifndef CRM_COMPATIBILITY__H
 #  define CRM_COMPATIBILITY__H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #include <crm/msg_xml.h>
 
 /* Heartbeat-specific definitions. Support for heartbeat has been removed
  * entirely, so any code branches relying on these should be deleted.
  */
 #define ACTIVESTATUS "active"
 #define DEADSTATUS "dead"
 #define PINGSTATUS "ping"
 #define JOINSTATUS "join"
 #define LEAVESTATUS "leave"
 #define NORMALNODE "normal"
 #define CRM_NODE_EVICTED "evicted"
 #define CRM_LEGACY_CONFIG_DIR "/var/lib/heartbeat/crm"
 #define HA_VARLIBHBDIR "/var/lib/heartbeat"
 #define pcmk_cluster_heartbeat 0x0004
 
 /* Corosync-version-1-specific definitions */
 
 /* Support for corosync version 1 has been removed entirely, so any code
  * branches relying on these should be deleted.
  */
 #define PCMK_SERVICE_ID 9
 #define CRM_SERVICE PCMK_SERVICE_ID
 #define XML_ATTR_EXPECTED_VOTES "expected-quorum-votes"
 #define crm_class_members 1
 #define crm_class_notify 2
 #define crm_class_nodeid 3
 #define crm_class_rmpeer 4
 #define crm_class_quorum 5
 #define pcmk_cluster_classic_ais 0x0010
 #define pcmk_cluster_cman 0x0040
 static int ais_fd_sync = -1;
 
 // These are always true now
 #define CS_USES_LIBQB 1
 #define HAVE_CMAP 1
 #define SUPPORT_CS_QUORUM 1
 #define SUPPORT_AIS 1
 #define AIS_COROSYNC 1
 
 // These are always false now
 #define HAVE_CONFDB 0
 #define SUPPORT_CMAN 0
 #define SUPPORT_PLUGIN 0
 #define SUPPORT_STONITH_CONFIG 0
 #define is_classic_ais_cluster() 0
 #define is_cman_cluster() 0
 
 // These have newer names
 #define is_openais_cluster() is_corosync_cluster()
 #if SUPPORT_COROSYNC
 #define SUPPORT_CS
 #endif
 
 /* Isolation-specific definitions. Support for the resource isolation feature
  * has been removed * entirely, so any code branches relying on these should be
  * deleted.
  */
 #define XML_RSC_ATTR_ISOLATION_INSTANCE "isolation-instance"
 #define XML_RSC_ATTR_ISOLATION_WRAPPER "isolation-wrapper"
 #define XML_RSC_ATTR_ISOLATION_HOST "isolation-host"
 #define XML_RSC_ATTR_ISOLATION "isolation"
 
 /* Schema-related definitions */
 
 // This has been renamed
 #define CRM_DTD_DIRECTORY CRM_SCHEMA_DIRECTORY
 
 /* Exit-code-related definitions */
 
 #define DAEMON_RESPAWN_STOP CRM_EX_FATAL
 #define pcmk_err_panic      CRM_EX_PANIC
 
 // Deprecated symbols that were removed
 #define APPNAME_LEN         256
 #define CRM_NODE_ACTIVE             CRM_NODE_MEMBER
 #define CRM_OP_DIE          "die_no_respawn"
 #define CRM_OP_RETRIVE_CIB  "retrieve_cib"
 #define CRM_OP_HBEAT        "dc_beat"
 #define CRM_OP_ABORT        "abort"
 #define CRM_OP_DEBUG_UP     "debug_inc"
 #define CRM_OP_DEBUG_DOWN   "debug_dec"
 #define CRM_OP_EVENTCC      "event_cc"
 #define CRM_OP_TEABORT      "te_abort"
 #define CRM_OP_TEABORTED    "te_abort_confirmed"
 #define CRM_OP_TE_HALT      "te_halt"
 #define CRM_OP_TECOMPLETE   "te_complete"
 #define CRM_OP_TETIMEOUT    "te_timeout"
 #define CRM_OP_TRANSITION   "transition"
 #define CRM_OP_NODES_PROBED "probe_nodes_complete"
 #define DOT_ALL_FSA_INPUTS  1
 #define DOT_FSA_ACTIONS     1
 #define F_LRMD_CANCEL_CALLID        "lrmd_cancel_callid"
 #define F_LRMD_RSC_METADATA         "lrmd_rsc_metadata_res"
 #define F_LRMD_IPC_PROXY_NODE       "lrmd_ipc_proxy_node"
 #define LOG_DEBUG_2  LOG_TRACE
 #define LOG_DEBUG_3  LOG_TRACE
 #define LOG_DEBUG_4  LOG_TRACE
 #define LOG_DEBUG_5  LOG_TRACE
 #define LOG_DEBUG_6  LOG_TRACE
 #define LRMD_OP_RSC_CHK_REG         "lrmd_rsc_check_register"
 #define MAX_IPC_FAIL                5
 #define MSG_LOG                     1
 #define PE_OBJ_T_NATIVE             "native"
 #define PE_OBJ_T_GROUP              "group"
 #define PE_OBJ_T_INCARNATION        "clone"
 #define PE_OBJ_T_MASTER             "master"
 #define SERVICE_SCRIPT              "/sbin/service"
 #define SOCKET_LEN                  1024
 #define XML_ATTR_TAGNAME            F_XML_TAGNAME
 #define XML_ATTR_FILTER_TYPE        "type-filter"
 #define XML_ATTR_FILTER_ID          "id-filter"
 #define XML_ATTR_FILTER_PRIORITY    "priority-filter"
 #define XML_ATTR_DC                 "is_dc"
 #define XML_MSG_TAG                 "crm_message"
 #define XML_MSG_TAG_DATA            "msg_data"
 #define XML_FAIL_TAG_RESOURCE       "failed_resource"
 #define XML_FAILRES_ATTR_RESID      "resource_id"
 #define XML_FAILRES_ATTR_REASON     "reason"
 #define XML_FAILRES_ATTR_RESSTATUS  "resource_status"
 #define XML_ATTR_RESULT             "result"
 #define XML_ATTR_SECTION            "section"
 #define XML_CIB_TAG_DOMAIN          "domain"
 #define XML_CIB_TAG_CONSTRAINT      "constraint"
 #define XML_RSC_ATTR_STATE          "clone-state"
 #define XML_RSC_ATTR_PRIORITY       "priority"
 #define XML_OP_ATTR_DEPENDENT       "dependent-on"
 #define XML_LRM_TAG_AGENTS          "lrm_agents"
 #define XML_LRM_TAG_AGENT           "lrm_agent"
 #define XML_LRM_TAG_ATTRIBUTES      "attributes"
 #define XML_CIB_ATTR_HEALTH         "health"
 #define XML_CIB_ATTR_WEIGHT         "weight"
 #define XML_CIB_ATTR_CLEAR          "clear_on"
 #define XML_CIB_ATTR_STONITH        "stonith"
 #define XML_CIB_ATTR_STANDBY        "standby"
 #define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled"
 #define XML_RULE_ATTR_RESULT        "result"
 #define XML_NODE_ATTR_STATE         "state"
 #define XML_ATTR_LRM_PROBE          "lrm-is-probe"
 #define XML_ATTR_TE_ALLOWFAIL       "op_allow_fail"
 #define add_cib_op_callback(cib, id, flag, data, fn) do {                \
         cib->cmds->register_callback(cib, id, 120, flag, data, #fn, fn); \
     } while(0)
 #define cib_default_options = cib_none
 #define crm_remote_baremetal              0x0004
 #define crm_remote_container              0x0002
+#define crm_element_value_const           crm_element_value
+#define crm_element_value_const_int       crm_element_value_int
 #define n_object_classes 3
 #define pe_action_failure_is_fatal        0x00020
 #define pe_rsc_munging                    0x00000800ULL
 #define pe_rsc_try_reload                 0x00001000ULL
 #define pe_rsc_shutdown                   0x00020000ULL
 #define pe_rsc_migrating                  0x00400000ULL
 #define pe_rsc_unexpectedly_running       0x02000000ULL
 #define pe_rsc_have_unfencing             0x80000000ULL
 
 static int node_score_infinity = 1000000;
 
 /* Clone terminology definitions */
 
 // These can no longer be used in a switch together
 #define pe_master pe_clone
 
 static inline enum pe_obj_types
 get_resource_type(const char *name)
 {
     if (safe_str_eq(name, XML_CIB_TAG_RESOURCE)) {
         return pe_native;
 
     } else if (safe_str_eq(name, XML_CIB_TAG_GROUP)) {
         return pe_group;
 
     } else if (safe_str_eq(name, XML_CIB_TAG_INCARNATION)
                || safe_str_eq(name, XML_CIB_TAG_MASTER)) {
         return pe_clone;
 
     } else if (safe_str_eq(name, XML_CIB_TAG_CONTAINER)) {
         return pe_container;
     }
 
     return pe_unknown;
 }
 
 static inline const char *
 get_resource_typename(enum pe_obj_types type)
 {
     switch (type) {
         case pe_native:
             return XML_CIB_TAG_RESOURCE;
         case pe_group:
             return XML_CIB_TAG_GROUP;
         case pe_clone:
             return XML_CIB_TAG_INCARNATION;
         case pe_container:
             return XML_CIB_TAG_CONTAINER;
         case pe_unknown:
             return "unknown";
     }
     return "<unknown>";
 }
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif
diff --git a/lib/common/xml.c b/lib/common/xml.c
index 13a186ffe8..6852005fae 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -1,5208 +1,5198 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <time.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <stdarg.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>  /* CRM_XML_LOG_BASE */
 
 #if HAVE_BZLIB_H
 #  include <bzlib.h>
 #endif
 
 #if HAVE_LIBXML2
 #  include <libxml/parser.h>
 #  include <libxml/tree.h>
 #endif
 
 #define XML_BUFFER_SIZE	4096
 #define XML_PARSER_DEBUG 0
 
 static inline int
 __get_prefix(const char *prefix, xmlNode *xml, char *buffer, int offset);
 
 typedef struct {
     int found;
     const char *string;
 } filter_t;
 
 enum xml_private_flags {
      xpf_none        = 0x0000,
      xpf_dirty       = 0x0001,
      xpf_deleted     = 0x0002,
      xpf_created     = 0x0004,
      xpf_modified    = 0x0008,
 
      xpf_tracking    = 0x0010,
      xpf_processed   = 0x0020,
      xpf_skip        = 0x0040,
      xpf_moved       = 0x0080,
 
      xpf_acl_enabled = 0x0100,
      xpf_acl_read    = 0x0200,
      xpf_acl_write   = 0x0400,
      xpf_acl_deny    = 0x0800,
 
      xpf_acl_create  = 0x1000,
      xpf_acl_denied  = 0x2000,
 };
 
-typedef struct xml_private_s 
-{
+typedef struct xml_private_s {
         long check;
         uint32_t flags;
         char *user;
         GListPtr acls;
         GListPtr deleted_objs;
 } xml_private_t;
 
 typedef struct xml_acl_s {
         enum xml_private_flags mode;
         char *xpath;
 } xml_acl_t;
 
 typedef struct xml_deleted_obj_s {
         char *path;
         int position;
 } xml_deleted_obj_t;
 
 /* *INDENT-OFF* */
 
 static filter_t filter[] = {
     { 0, XML_ATTR_ORIGIN },
     { 0, XML_CIB_ATTR_WRITTEN },
     { 0, XML_ATTR_UPDATE_ORIG },
     { 0, XML_ATTR_UPDATE_CLIENT },
     { 0, XML_ATTR_UPDATE_USER },
 };
 /* *INDENT-ON* */
 
 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
 static int add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update);
 static bool __xml_acl_check(xmlNode *xml, const char *name, enum xml_private_flags mode);
 const char *__xml_acl_to_text(enum xml_private_flags flags);
 
 #define CHUNK_SIZE 1024
 static inline bool TRACKING_CHANGES(xmlNode *xml)
 {
     if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
         return FALSE;
     } else if(is_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
         return TRUE;
     }
     return FALSE;
 }
 
 #define buffer_print(buffer, max, offset, fmt, args...) do {            \
         int rc = (max);                                                 \
         if(buffer) {                                                    \
             rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
         }                                                               \
         if(buffer && rc < 0) {                                          \
             crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
             (buffer)[(offset)] = 0;                                     \
             break;                                                      \
         } else if(rc >= ((max) - (offset))) {                           \
             char *tmp = NULL;                                           \
             (max) = QB_MAX(CHUNK_SIZE, (max) * 2);                      \
             tmp = realloc_safe((buffer), (max));                        \
             CRM_ASSERT(tmp);                                            \
             (buffer) = tmp;                                             \
         } else {                                                        \
             offset += rc;                                               \
             break;                                                      \
         }                                                               \
     } while(1);
 
 static void
 insert_prefix(int options, char **buffer, int *offset, int *max, int depth)
 {
     if (options & xml_log_option_formatted) {
         size_t spaces = 2 * depth;
 
         if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
             (*max) = QB_MAX(CHUNK_SIZE, (*max) * 2);
             (*buffer) = realloc_safe((*buffer), (*max));
         }
         memset((*buffer) + (*offset), ' ', spaces);
         (*offset) += spaces;
     }
 }
 
 static void
 set_parent_flag(xmlNode *xml, long flag) 
 {
 
     for(; xml; xml = xml->parent) {
         xml_private_t *p = xml->_private;
 
         if(p == NULL) {
             /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
         } else {
             p->flags |= flag;
             /* crm_trace("Setting flag %x due to %s[@id=%s]", flag, xml->name, ID(xml)); */
         }
     }
 }
 
 static void
 set_doc_flag(xmlNode *xml, long flag) 
 {
 
     if(xml && xml->doc && xml->doc->_private){
         /* During calls to xmlDocCopyNode(), xml->doc may be unset */
         xml_private_t *p = xml->doc->_private;
 
         p->flags |= flag;
         /* crm_trace("Setting flag %x due to %s[@id=%s]", flag, xml->name, ID(xml)); */
     }
 }
 
 static void
 __xml_node_dirty(xmlNode *xml) 
 {
     set_doc_flag(xml, xpf_dirty);
     set_parent_flag(xml, xpf_dirty);
 }
 
 static void
 __xml_node_clean(xmlNode *xml) 
 {
     xmlNode *cIter = NULL;
     xml_private_t *p = xml->_private;
 
     if(p) {
         p->flags = 0;
     }
 
     for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
         __xml_node_clean(cIter);
     }
 }
 
 static void
 crm_node_created(xmlNode *xml) 
 {
     xmlNode *cIter = NULL;
     xml_private_t *p = xml->_private;
 
     if(p && TRACKING_CHANGES(xml)) {
         if(is_not_set(p->flags, xpf_created)) {
             p->flags |= xpf_created;
             __xml_node_dirty(xml);
         }
 
         for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
            crm_node_created(cIter);
         }
     }
 }
 
 static void
 crm_attr_dirty(xmlAttr *a) 
 {
     xmlNode *parent = a->parent;
     xml_private_t *p = NULL;
 
     p = a->_private;
     p->flags |= (xpf_dirty|xpf_modified);
     p->flags = (p->flags & ~xpf_deleted);
     /* crm_trace("Setting flag %x due to %s[@id=%s, @%s=%s]", */
     /*           xpf_dirty, parent?parent->name:NULL, ID(parent), a->name, a->children->content); */
 
     __xml_node_dirty(parent);
 }
 
 int get_tag_name(const char *input, size_t offset, size_t max);
 int get_attr_name(const char *input, size_t offset, size_t max);
 int get_attr_value(const char *input, size_t offset, size_t max);
 gboolean can_prune_leaf(xmlNode * xml_node);
 
 void diff_filter_context(int context, int upper_bound, int lower_bound,
                          xmlNode * xml_node, xmlNode * parent);
 int in_upper_context(int depth, int context, xmlNode * xml_node);
 int add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff);
 
 static inline const char *
-crm_attr_value(xmlAttr * attr)
+crm_attr_value(const xmlAttr *attr)
 {
     if (attr == NULL || attr->children == NULL) {
         return NULL;
     }
-    return (const char *)attr->children->content;
+    return (const char *) attr->children->content;
 }
 
 static inline xmlAttr *
-crm_first_attr(xmlNode * xml)
+crm_first_attr(const xmlNode *xml)
 {
     if (xml == NULL) {
         return NULL;
     }
     return xml->properties;
 }
 
 #define XML_PRIVATE_MAGIC (long) 0x81726354
 
 static void
 __xml_acl_free(void *data)
 {
     if(data) {
         xml_acl_t *acl = data;
 
         free(acl->xpath);
         free(acl);
     }
 }
 
 static void
 __xml_deleted_obj_free(void *data)
 {
     if(data) {
         xml_deleted_obj_t *deleted_obj = data;
 
         free(deleted_obj->path);
         free(deleted_obj);
     }
 }
 
 static void
 __xml_private_clean(xml_private_t *p)
 {
     if(p) {
         CRM_ASSERT(p->check == XML_PRIVATE_MAGIC);
 
         free(p->user);
         p->user = NULL;
 
         if(p->acls) {
             g_list_free_full(p->acls, __xml_acl_free);
             p->acls = NULL;
         }
 
         if(p->deleted_objs) {
             g_list_free_full(p->deleted_objs, __xml_deleted_obj_free);
             p->deleted_objs = NULL;
         }
     }
 }
 
 
 static void
 __xml_private_free(xml_private_t *p)
 {
     __xml_private_clean(p);
     free(p);
 }
 
 static void
 pcmkDeregisterNode(xmlNodePtr node)
 {
     __xml_private_free(node->_private);
 }
 
 static void
 pcmkRegisterNode(xmlNodePtr node)
 {
     xml_private_t *p = NULL;
 
     switch(node->type) {
         case XML_ELEMENT_NODE:
         case XML_DOCUMENT_NODE:
         case XML_ATTRIBUTE_NODE:
         case XML_COMMENT_NODE:
             p = calloc(1, sizeof(xml_private_t));
             p->check = XML_PRIVATE_MAGIC;
             /* Flags will be reset if necessary when tracking is enabled */
             p->flags |= (xpf_dirty|xpf_created);
             node->_private = p;
             break;
         case XML_TEXT_NODE:
         case XML_DTD_NODE:
         case XML_CDATA_SECTION_NODE:
             break;
         default:
             /* Ignore */
             crm_trace("Ignoring %p %d", node, node->type);
             CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
             break;
     }
 
     if(p && TRACKING_CHANGES(node)) {
         /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
          * not hooked up at the point we are called
          */
         set_doc_flag(node, xpf_dirty);
         __xml_node_dirty(node);
     }
 }
 
 static xml_acl_t *
 __xml_acl_create(xmlNode * xml, xmlNode *target, enum xml_private_flags mode)
 {
     xml_acl_t *acl = NULL;
 
     xml_private_t *p = NULL;
     const char *tag = crm_element_value(xml, XML_ACL_ATTR_TAG);
     const char *ref = crm_element_value(xml, XML_ACL_ATTR_REF);
     const char *xpath = crm_element_value(xml, XML_ACL_ATTR_XPATH);
 
     if(tag == NULL) {
         /* Compatibility handling for pacemaker < 1.1.12 */
         tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1);
     }
     if(ref == NULL) {
         /* Compatibility handling for pacemaker < 1.1.12 */
         ref = crm_element_value(xml, XML_ACL_ATTR_REFv1);
     }
 
     if(target == NULL || target->doc == NULL || target->doc->_private == NULL){
         CRM_ASSERT(target);
         CRM_ASSERT(target->doc);
         CRM_ASSERT(target->doc->_private);
         return NULL;
 
     } else if (tag == NULL && ref == NULL && xpath == NULL) {
         crm_trace("No criteria %p", xml);
         return NULL;
     }
 
     p = target->doc->_private;
     acl = calloc(1, sizeof(xml_acl_t));
     if (acl) {
         const char *attr = crm_element_value(xml, XML_ACL_ATTR_ATTRIBUTE);
 
         acl->mode = mode;
         if(xpath) {
             acl->xpath = strdup(xpath);
             crm_trace("Using xpath: %s", acl->xpath);
 
         } else {
             int offset = 0;
             char buffer[XML_BUFFER_SIZE];
 
             if(tag) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "//%s", tag);
             } else {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "//*");
             }
 
             if(ref || attr) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "[");
             }
 
             if(ref) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "@id='%s'", ref);
             }
 
             if(ref && attr) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, " and ");
             }
 
             if(attr) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "@%s", attr);
             }
 
             if(ref || attr) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "]");
             }
 
             CRM_LOG_ASSERT(offset > 0);
             acl->xpath = strdup(buffer);
             crm_trace("Built xpath: %s", acl->xpath);
         }
 
         p->acls = g_list_append(p->acls, acl);
     }
     return acl;
 }
 
 static gboolean
 __xml_acl_parse_entry(xmlNode * acl_top, xmlNode * acl_entry, xmlNode *target)
 {
     xmlNode *child = NULL;
 
     for (child = __xml_first_child(acl_entry); child; child = __xml_next(child)) {
         const char *tag = crm_element_name(child);
         const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND);
 
         if (strcmp(XML_ACL_TAG_PERMISSION, tag) == 0){
             tag = kind;
         }
 
         crm_trace("Processing %s %p", tag, child);
         if(tag == NULL) {
             CRM_ASSERT(tag != NULL);
 
         } else if (strcmp(XML_ACL_TAG_ROLE_REF, tag) == 0
                    || strcmp(XML_ACL_TAG_ROLE_REFv1, tag) == 0) {
             const char *ref_role = crm_element_value(child, XML_ATTR_ID);
 
             if (ref_role) {
                 xmlNode *role = NULL;
 
                 for (role = __xml_first_child(acl_top); role; role = __xml_next(role)) {
                     if (strcmp(XML_ACL_TAG_ROLE, (const char *)role->name) == 0) {
                         const char *role_id = crm_element_value(role, XML_ATTR_ID);
 
                         if (role_id && strcmp(ref_role, role_id) == 0) {
                             crm_debug("Unpacking referenced role: %s", role_id);
                             __xml_acl_parse_entry(acl_top, role, target);
                             break;
                         }
                     }
                 }
             }
 
         } else if (strcmp(XML_ACL_TAG_READ, tag) == 0) {
             __xml_acl_create(child, target, xpf_acl_read);
 
         } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
             __xml_acl_create(child, target, xpf_acl_write);
 
         } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
             __xml_acl_create(child, target, xpf_acl_deny);
 
         } else {
             crm_warn("Unknown ACL entry: %s/%s", tag, kind);
         }
     }
 
     return TRUE;
 }
 
 /*
     <acls>
       <acl_target id="l33t-haxor"><role id="auto-l33t-haxor"/></acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_target id="badidea"><role id="auto-badidea"/></acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
         <acl_permission id="badidea-resources-2" kind="deny" reference="dummy-meta_attributes"/>
       </acl_role>
     </acls>
 */
 
 const char *
 __xml_acl_to_text(enum xml_private_flags flags)
 {
     if(is_set(flags, xpf_acl_deny)) {
         return "deny";
     }
     if(is_set(flags, xpf_acl_write)) {
         return "read/write";
     }
     if(is_set(flags, xpf_acl_read)) {
         return "read";
     }
 
     return "none";
 }
 
 static void
 __xml_acl_apply(xmlNode *xml) 
 {
     GListPtr aIter = NULL;
     xml_private_t *p = NULL;
     xmlXPathObjectPtr xpathObj = NULL;
 
     if(xml_acl_enabled(xml) == FALSE) {
         p = xml->doc->_private;
         crm_trace("Not applying ACLs for %s", p->user);
         return;
     }
 
     p = xml->doc->_private;
     for(aIter = p->acls; aIter != NULL; aIter = aIter->next) {
         int max = 0, lpc = 0;
         xml_acl_t *acl = aIter->data;
 
         xpathObj = xpath_search(xml, acl->xpath);
         max = numXpathResults(xpathObj);
 
         for(lpc = 0; lpc < max; lpc++) {
             xmlNode *match = getXpathResult(xpathObj, lpc);
             char *path = xml_get_path(match);
 
             p = match->_private;
             crm_trace("Applying %x to %s for %s", acl->mode, path, acl->xpath);
 
 #ifdef SUSE_ACL_COMPAT
             if(is_not_set(p->flags, acl->mode)) {
                 if(is_set(p->flags, xpf_acl_read)
                    || is_set(p->flags, xpf_acl_write)
                    || is_set(p->flags, xpf_acl_deny)) {
                     crm_config_warn("Configuration element %s is matched by multiple ACL rules, only the first applies ('%s' wins over '%s')",
                                     path, __xml_acl_to_text(p->flags), __xml_acl_to_text(acl->mode));
                     free(path);
                     continue;
                 }
             }
 #endif
 
             p->flags |= acl->mode;
             free(path);
         }
         crm_trace("Now enforcing ACL: %s (%d matches)", acl->xpath, max);
         freeXpathObject(xpathObj);
     }
 
     p = xml->_private;
     if(is_not_set(p->flags, xpf_acl_read) && is_not_set(p->flags, xpf_acl_write)) {
         p->flags |= xpf_acl_deny;
         p = xml->doc->_private;
         crm_info("Enforcing default ACL for %s to %s", p->user, crm_element_name(xml));
     }
 
 }
 
 static void
 __xml_acl_unpack(xmlNode *source, xmlNode *target, const char *user)
 {
 #if ENABLE_ACL
     xml_private_t *p = NULL;
 
     if(target == NULL || target->doc == NULL || target->doc->_private == NULL) {
         return;
     }
 
     p = target->doc->_private;
     if(pcmk_acl_required(user) == FALSE) {
         crm_trace("no acls needed for '%s'", user);
 
     } else if(p->acls == NULL) {
         xmlNode *acls = get_xpath_object("//"XML_CIB_TAG_ACLS, source, LOG_TRACE);
 
         free(p->user);
         p->user = strdup(user);
 
         if(acls) {
             xmlNode *child = NULL;
 
             for (child = __xml_first_child(acls); child; child = __xml_next(child)) {
                 const char *tag = crm_element_name(child);
 
                 if (strcmp(tag, XML_ACL_TAG_USER) == 0 || strcmp(tag, XML_ACL_TAG_USERv1) == 0) {
                     const char *id = crm_element_value(child, XML_ATTR_ID);
 
                     if(id && strcmp(id, user) == 0) {
                         crm_debug("Unpacking ACLs for %s", id);
                         __xml_acl_parse_entry(acls, child, target);
                     }
                 }
             }
         }
     }
 #endif
 }
 
 static inline bool
 __xml_acl_mode_test(enum xml_private_flags allowed, enum xml_private_flags requested)
 {
     if(is_set(allowed, xpf_acl_deny)) {
         return FALSE;
 
     } else if(is_set(allowed, requested)) {
         return TRUE;
 
     } else if(is_set(requested, xpf_acl_read) && is_set(allowed, xpf_acl_write)) {
         return TRUE;
 
     } else if(is_set(requested, xpf_acl_create) && is_set(allowed, xpf_acl_write)) {
         return TRUE;
 
     } else if(is_set(requested, xpf_acl_create) && is_set(allowed, xpf_created)) {
         return TRUE;
     }
     return FALSE;
 }
 
 /* rc = TRUE if orig_cib has been filtered
  * That means '*result' rather than 'xml' should be exploited afterwards
  */
 static bool
 __xml_purge_attributes(xmlNode *xml)
 {
     xmlNode *child = NULL;
     xmlAttr *xIter = NULL;
     bool readable_children = FALSE;
     xml_private_t *p = xml->_private;
 
     if(__xml_acl_mode_test(p->flags, xpf_acl_read)) {
         crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
         return TRUE;
     }
 
     xIter = crm_first_attr(xml);
     while(xIter != NULL) {
         xmlAttr *tmp = xIter;
         const char *prop_name = (const char *)xIter->name;
 
         xIter = xIter->next;
         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
             continue;
         }
 
         xmlUnsetProp(xml, tmp->name);
     }
 
     child = __xml_first_child(xml);
     while ( child != NULL ) {
         xmlNode *tmp = child;
 
         child = __xml_next(child);
         readable_children |= __xml_purge_attributes(tmp);
     }
 
     if(readable_children == FALSE) {
         free_xml(xml); /* Nothing readable under here, purge completely */
     }
     return readable_children;
 }
 
 bool
 xml_acl_filtered_copy(const char *user, xmlNode* acl_source, xmlNode *xml, xmlNode ** result)
 {
     GListPtr aIter = NULL;
     xmlNode *target = NULL;
     xml_private_t *p = NULL;
     xml_private_t *doc = NULL;
 
     *result = NULL;
     if(xml == NULL || pcmk_acl_required(user) == FALSE) {
         crm_trace("no acls needed for '%s'", user);
         return FALSE;
     }
 
     crm_trace("filtering copy of %p for '%s'", xml, user);
     target = copy_xml(xml);
     if(target == NULL) {
         return TRUE;
     }
 
     __xml_acl_unpack(acl_source, target, user);
     set_doc_flag(target, xpf_acl_enabled);
     __xml_acl_apply(target);
 
     doc = target->doc->_private;
     for(aIter = doc->acls; aIter != NULL && target; aIter = aIter->next) {
         int max = 0;
         xml_acl_t *acl = aIter->data;
 
         if(acl->mode != xpf_acl_deny) {
             /* Nothing to do */
 
         } else if(acl->xpath) {
             int lpc = 0;
             xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
 
             max = numXpathResults(xpathObj);
             for(lpc = 0; lpc < max; lpc++) {
                 xmlNode *match = getXpathResult(xpathObj, lpc);
 
                 crm_trace("Purging attributes from %s", acl->xpath);
                 if(__xml_purge_attributes(match) == FALSE && match == target) {
                     crm_trace("No access to the entire document for %s", user);
                     freeXpathObject(xpathObj);
                     return TRUE;
                 }
             }
             crm_trace("Enforced ACL %s (%d matches)", acl->xpath, max);
             freeXpathObject(xpathObj);
         }
     }
 
     p = target->_private;
     if(is_set(p->flags, xpf_acl_deny) && __xml_purge_attributes(target) == FALSE) {
         crm_trace("No access to the entire document for %s", user);
         return TRUE;
     }
 
     if(doc->acls) {
         g_list_free_full(doc->acls, __xml_acl_free);
         doc->acls = NULL;
 
     } else {
         crm_trace("Ordinary user '%s' cannot access the CIB without any defined ACLs", doc->user);
         free_xml(target);
         target = NULL;
     }
 
     if(target) {
         *result = target;
     }
 
     return TRUE;
 }
 
 static void
 __xml_acl_post_process(xmlNode * xml)
 {
     xmlNode *cIter = __xml_first_child(xml);
     xml_private_t *p = xml->_private;
 
     if(is_set(p->flags, xpf_created)) {
         xmlAttr *xIter = NULL;
         char *path = xml_get_path(xml);
 
         /* Always allow new scaffolding (e.g. node with no attributes or only an
          * 'id'), except in the ACLs section
          */
 
         for (xIter = crm_first_attr(xml); xIter != NULL; xIter = xIter->next) {
             const char *prop_name = (const char *)xIter->name;
 
             if (strcmp(prop_name, XML_ATTR_ID) == 0 && strstr(path, "/"XML_CIB_TAG_ACLS"/") == NULL) {
                 /* Delay the acl check */
                 continue;
 
             } else if(__xml_acl_check(xml, NULL, xpf_acl_write)) {
                 crm_trace("Creation of %s=%s is allowed", crm_element_name(xml), ID(xml));
                 break;
 
             } else {
                 crm_trace("Cannot add new node %s at %s", crm_element_name(xml), path);
 
                 if(xml != xmlDocGetRootElement(xml->doc)) {
                     xmlUnlinkNode(xml);
                     xmlFreeNode(xml);
                 }
                 free(path);
                 return;
             }
         }
         free(path);
     }
 
     while (cIter != NULL) {
         xmlNode *child = cIter;
         cIter = __xml_next(cIter); /* In case it is free'd */
         __xml_acl_post_process(child);
     }
 }
 
 bool
 xml_acl_denied(xmlNode *xml) 
 {
     if(xml && xml->doc && xml->doc->_private){
         xml_private_t *p = xml->doc->_private;
 
         return is_set(p->flags, xpf_acl_denied);
     }
     return FALSE;
 }
 
 void
 xml_acl_disable(xmlNode *xml)
 {
     if(xml_acl_enabled(xml)) {
         xml_private_t *p = xml->doc->_private;
 
         /* Catch anything that was created but shouldn't have been */
         __xml_acl_apply(xml);
         __xml_acl_post_process(xml);
         clear_bit(p->flags, xpf_acl_enabled);
     }
 }
 
 bool
 xml_acl_enabled(xmlNode *xml)
 {
     if(xml && xml->doc && xml->doc->_private){
         xml_private_t *p = xml->doc->_private;
 
         return is_set(p->flags, xpf_acl_enabled);
     }
     return FALSE;
 }
 
 void
 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls) 
 {
     xml_accept_changes(xml);
     crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
     set_doc_flag(xml, xpf_tracking);
     if(enforce_acls) {
         if(acl_source == NULL) {
             acl_source = xml;
         }
         set_doc_flag(xml, xpf_acl_enabled);
         __xml_acl_unpack(acl_source, xml, user);
         __xml_acl_apply(xml);
     }
 }
 
 bool xml_tracking_changes(xmlNode * xml)
 {
     if(xml == NULL) {
         return FALSE;
 
     } else if(is_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
         return TRUE;
     }
     return FALSE;
 }
 
 bool xml_document_dirty(xmlNode *xml) 
 {
     if(xml != NULL && xml->doc && xml->doc->_private) {
         xml_private_t *doc = xml->doc->_private;
 
         return is_set(doc->flags, xpf_dirty);
     }
     return FALSE;
 }
 
 /*
 <diff format="2.0">
   <version>
     <source admin_epoch="1" epoch="2" num_updates="3"/>
     <target admin_epoch="1" epoch="3" num_updates="0"/>
   </version>
   <change operation="add" xpath="/cib/configuration/nodes">
     <node id="node2" uname="node2" description="foo"/>
   </change>
   <change operation="add" xpath="/cib/configuration/nodes/node[node2]">
     <instance_attributes id="nodes-node"><!-- NOTE: can be a full tree -->
       <nvpair id="nodes-node2-ram" name="ram" value="1024M"/>
     </instance_attributes>
   </change>
   <change operation="update" xpath="/cib/configuration/nodes[@id='node2']">
     <change-list>
       <change-attr operation="set" name="type" value="member"/>
       <change-attr operation="unset" name="description"/>
     </change-list>
     <change-result>
       <node id="node2" uname="node2" type="member"/><!-- NOTE: not recursive -->
     </change-result>
   </change>
   <change operation="delete" xpath="/cib/configuration/nodes/node[@id='node3'] /">
   <change operation="update" xpath="/cib/configuration/resources/group[@id='g1']">
     <change-list>
       <change-attr operation="set" name="description" value="some grabage here"/>
     </change-list>
     <change-result>
       <group id="g1" description="some grabage here"/><!-- NOTE: not recursive -->
     </change-result>
   </change>
   <change operation="update" xpath="/cib/status/node_state[@id='node2]/lrm[@id='node2']/lrm_resources/lrm_resource[@id='Fence']">
     <change-list>
       <change-attr operation="set" name="oper" value="member"/>
       <change-attr operation="set" name="operation_key" value="Fence_start_0"/>
       <change-attr operation="set" name="operation" value="start"/>
       <change-attr operation="set" name="transition-key" value="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"/>
       <change-attr operation="set" name="transition-magic" value="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"/>
       <change-attr operation="set" name="call-id" value="2"/>
       <change-attr operation="set" name="rc-code" value="0"/>
     </change-list>
     <change-result>
       <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate"  transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
     </change-result>
   </change>
 </diff>
  */
 static int __xml_offset(xmlNode *xml) 
 {
     int position = 0;
     xmlNode *cIter = NULL;
 
     for(cIter = xml; cIter->prev; cIter = cIter->prev) {
         xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
 
         if(is_not_set(p->flags, xpf_skip)) {
             position++;
         }
     }
 
     return position;
 }
 
 static int __xml_offset_no_deletions(xmlNode *xml) 
 {
     int position = 0;
     xmlNode *cIter = NULL;
 
     for(cIter = xml; cIter->prev; cIter = cIter->prev) {
         xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
 
         if(is_not_set(p->flags, xpf_deleted)) {
             position++;
         }
     }
 
     return position;
 }
 
 static void
 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
 {
     xmlNode *cIter = NULL;
     xmlAttr *pIter = NULL;
     xmlNode *change = NULL;
     xml_private_t *p = xml->_private;
 
     if(patchset && is_set(p->flags, xpf_created)) {
         int offset = 0;
         char buffer[XML_BUFFER_SIZE];
 
         if(__get_prefix(NULL, xml->parent, buffer, offset) > 0) {
             int position = __xml_offset_no_deletions(xml);
 
             change = create_xml_node(patchset, XML_DIFF_CHANGE);
 
             crm_xml_add(change, XML_DIFF_OP, "create");
             crm_xml_add(change, XML_DIFF_PATH, buffer);
             crm_xml_add_int(change, XML_DIFF_POSITION, position);
             add_node_copy(change, xml);
         }
 
         return;
     }
 
     for (pIter = crm_first_attr(xml); pIter != NULL; pIter = pIter->next) {
         xmlNode *attr = NULL;
 
         p = pIter->_private;
         if(is_not_set(p->flags, xpf_deleted) && is_not_set(p->flags, xpf_dirty)) {
             continue;
         }
 
         if(change == NULL) {
             int offset = 0;
             char buffer[XML_BUFFER_SIZE];
 
             if(__get_prefix(NULL, xml, buffer, offset) > 0) {
                 change = create_xml_node(patchset, XML_DIFF_CHANGE);
 
                 crm_xml_add(change, XML_DIFF_OP, "modify");
                 crm_xml_add(change, XML_DIFF_PATH, buffer);
 
                 change = create_xml_node(change, XML_DIFF_LIST);
             }
         }
 
         attr = create_xml_node(change, XML_DIFF_ATTR);
 
         crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, (const char *)pIter->name);
         if(p->flags & xpf_deleted) {
             crm_xml_add(attr, XML_DIFF_OP, "unset");
 
         } else {
             const char *value = crm_element_value(xml, (const char *)pIter->name);
 
             crm_xml_add(attr, XML_DIFF_OP, "set");
             crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value);
         }
     }
 
     if(change) {
         xmlNode *result = NULL;
 
         change = create_xml_node(change->parent, XML_DIFF_RESULT);
         result = create_xml_node(change, (const char *)xml->name);
 
         for (pIter = crm_first_attr(xml); pIter != NULL; pIter = pIter->next) {
             const char *value = crm_element_value(xml, (const char *)pIter->name);
 
             p = pIter->_private;
             if (is_not_set(p->flags, xpf_deleted)) {
                 crm_xml_add(result, (const char *)pIter->name, value);
             }
         }
     }
 
     for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
         __xml_build_changes(cIter, patchset);
     }
 
     p = xml->_private;
     if(patchset && is_set(p->flags, xpf_moved)) {
         int offset = 0;
         char buffer[XML_BUFFER_SIZE];
 
         crm_trace("%s.%s moved to position %d", xml->name, ID(xml), __xml_offset(xml));
         if(__get_prefix(NULL, xml, buffer, offset) > 0) {
             change = create_xml_node(patchset, XML_DIFF_CHANGE);
 
             crm_xml_add(change, XML_DIFF_OP, "move");
             crm_xml_add(change, XML_DIFF_PATH, buffer);
             crm_xml_add_int(change, XML_DIFF_POSITION, __xml_offset_no_deletions(xml));
         }
     }
 }
 
 static void
 __xml_accept_changes(xmlNode * xml)
 {
     xmlNode *cIter = NULL;
     xmlAttr *pIter = NULL;
     xml_private_t *p = xml->_private;
 
     p->flags = xpf_none;
     pIter = crm_first_attr(xml);
 
     while (pIter != NULL) {
         const xmlChar *name = pIter->name;
 
         p = pIter->_private;
         pIter = pIter->next;
 
         if(p->flags & xpf_deleted) {
             xml_remove_prop(xml, (const char *)name);
 
         } else {
             p->flags = xpf_none;
         }
     }
 
     for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
         __xml_accept_changes(cIter);
     }
 }
 
 static bool
 is_config_change(xmlNode *xml)
 {
     GListPtr gIter = NULL;
     xml_private_t *p = NULL;
     xmlNode *config = first_named_child(xml, XML_CIB_TAG_CONFIGURATION);
 
     if(config) {
         p = config->_private;
     }
     if(p && is_set(p->flags, xpf_dirty)) {
         return TRUE;
     }
 
     if(xml->doc && xml->doc->_private) {
         p = xml->doc->_private;
         for(gIter = p->deleted_objs; gIter; gIter = gIter->next) {
             xml_deleted_obj_t *deleted_obj = gIter->data;
 
             if(strstr(deleted_obj->path, "/"XML_TAG_CIB"/"XML_CIB_TAG_CONFIGURATION) != NULL) {
                 return TRUE;
             }
         }
     }
 
     return FALSE;
 }
 
 static void
 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
 {
     int lpc = 0;
     xmlNode *cib = NULL;
     xmlNode *diff_child = NULL;
 
     const char *tag = NULL;
 
     const char *vfields[] = {
         XML_ATTR_GENERATION_ADMIN,
         XML_ATTR_GENERATION,
         XML_ATTR_NUMUPDATES,
     };
 
     if (local_diff == NULL) {
         crm_trace("Nothing to do");
         return;
     }
 
     tag = "diff-removed";
     diff_child = find_xml_node(local_diff, tag, FALSE);
     if (diff_child == NULL) {
         diff_child = create_xml_node(local_diff, tag);
     }
 
     tag = XML_TAG_CIB;
     cib = find_xml_node(diff_child, tag, FALSE);
     if (cib == NULL) {
         cib = create_xml_node(diff_child, tag);
     }
 
     for(lpc = 0; last && lpc < DIMOF(vfields); lpc++){
         const char *value = crm_element_value(last, vfields[lpc]);
 
         crm_xml_add(diff_child, vfields[lpc], value);
         if(changed || lpc == 2) {
             crm_xml_add(cib, vfields[lpc], value);
         }
     }
 
     tag = "diff-added";
     diff_child = find_xml_node(local_diff, tag, FALSE);
     if (diff_child == NULL) {
         diff_child = create_xml_node(local_diff, tag);
     }
 
     tag = XML_TAG_CIB;
     cib = find_xml_node(diff_child, tag, FALSE);
     if (cib == NULL) {
         cib = create_xml_node(diff_child, tag);
     }
 
     for(lpc = 0; next && lpc < DIMOF(vfields); lpc++){
         const char *value = crm_element_value(next, vfields[lpc]);
 
         crm_xml_add(diff_child, vfields[lpc], value);
     }
 
     if (next) {
         xmlAttrPtr xIter = NULL;
 
         for (xIter = next->properties; xIter; xIter = xIter->next) {
             const char *p_name = (const char *)xIter->name;
             const char *p_value = crm_element_value(next, p_name);
 
             xmlSetProp(cib, (const xmlChar *)p_name, (const xmlChar *)p_value);
         }
     }
 
     crm_log_xml_explicit(local_diff, "Repaired-diff");
 }
 
 static xmlNode *
 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config, bool suppress)
 {
     xmlNode *patchset = diff_xml_object(source, target, suppress);
 
     if(patchset) {
         CRM_LOG_ASSERT(xml_document_dirty(target));
         xml_repair_v1_diff(source, target, patchset, config);
         crm_xml_add(patchset, "format", "1");
     }
     return patchset;
 }
 
 static xmlNode *
 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
 {
     int lpc = 0;
     GListPtr gIter = NULL;
     xml_private_t *doc = NULL;
 
     xmlNode *v = NULL;
     xmlNode *version = NULL;
     xmlNode *patchset = NULL;
     const char *vfields[] = {
         XML_ATTR_GENERATION_ADMIN,
         XML_ATTR_GENERATION,
         XML_ATTR_NUMUPDATES,
     };
 
     CRM_ASSERT(target);
     if(xml_document_dirty(target) == FALSE) {
         return NULL;
     }
 
     CRM_ASSERT(target->doc);
     doc = target->doc->_private;
 
     patchset = create_xml_node(NULL, XML_TAG_DIFF);
     crm_xml_add_int(patchset, "format", 2);
 
     version = create_xml_node(patchset, XML_DIFF_VERSION);
 
     v = create_xml_node(version, XML_DIFF_VSOURCE);
     for(lpc = 0; lpc < DIMOF(vfields); lpc++){
         const char *value = crm_element_value(source, vfields[lpc]);
 
         if(value == NULL) {
             value = "1";
         }
         crm_xml_add(v, vfields[lpc], value);
     }
 
     v = create_xml_node(version, XML_DIFF_VTARGET);
     for(lpc = 0; lpc < DIMOF(vfields); lpc++){
         const char *value = crm_element_value(target, vfields[lpc]);
 
         if(value == NULL) {
             value = "1";
         }
         crm_xml_add(v, vfields[lpc], value);
     }
 
     for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
         xml_deleted_obj_t *deleted_obj = gIter->data;
         xmlNode *change = create_xml_node(patchset, XML_DIFF_CHANGE);
 
         crm_xml_add(change, XML_DIFF_OP, "delete");
         crm_xml_add(change, XML_DIFF_PATH, deleted_obj->path);
         if (deleted_obj->position >= 0) {
             crm_xml_add_int(change, XML_DIFF_POSITION, deleted_obj->position);
         }
     }
 
     __xml_build_changes(target, patchset);
     return patchset;
 }
 
 xmlNode *
 xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
 {
     int counter = 0;
     bool config = FALSE;
     xmlNode *patch = NULL;
     const char *version = crm_element_value(source, XML_ATTR_CRM_VERSION);
 
     xml_acl_disable(target);
     if(xml_document_dirty(target) == FALSE) {
         crm_trace("No change %d", format);
         return NULL; /* No change */
     }
 
     config = is_config_change(target);
     if(config_changed) {
         *config_changed = config;
     }
 
     if(manage_version && config) {
         crm_trace("Config changed %d", format);
         crm_xml_add(target, XML_ATTR_NUMUPDATES, "0");
 
         crm_element_value_int(target, XML_ATTR_GENERATION, &counter);
         crm_xml_add_int(target, XML_ATTR_GENERATION, counter+1);
 
     } else if(manage_version) {
         crm_element_value_int(target, XML_ATTR_NUMUPDATES, &counter);
         crm_trace("Status changed %d - %d %s", format, counter, crm_element_value(source, XML_ATTR_NUMUPDATES));
         crm_xml_add_int(target, XML_ATTR_NUMUPDATES, counter+1);
     }
 
     if(format == 0) {
         if (compare_version("3.0.8", version) < 0) {
             format = 2;
 
         } else {
             format = 1;
         }
         crm_trace("Using patch format %d for version: %s", format, version);
     }
 
     switch(format) {
         case 1:
             patch = xml_create_patchset_v1(source, target, config, FALSE);
             break;
         case 2:
             patch = xml_create_patchset_v2(source, target);
             break;
         default:
             crm_err("Unknown patch format: %d", format);
             return NULL;
     }
 
     return patch;
 }
 
 void
 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
 {
     int format = 1;
     const char *version = NULL;
     char *digest = NULL;
 
     if (patch == NULL || source == NULL || target == NULL) {
         return;
     }
 
     /* NOTE: We should always call xml_accept_changes() before calculating digest. */
     /* Otherwise, with an on-tracking dirty target, we could get a wrong digest. */
     CRM_LOG_ASSERT(xml_document_dirty(target) == FALSE);
 
     crm_element_value_int(patch, "format", &format);
     if (format > 1 && with_digest == FALSE) {
         return;
     }
 
     version = crm_element_value(source, XML_ATTR_CRM_VERSION);
     digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
 
     crm_xml_add(patch, XML_ATTR_DIGEST, digest);
     free(digest);
 
     return;
 }
 
 static void
 __xml_log_element(int log_level, const char *file, const char *function, int line,
                   const char *prefix, xmlNode * data, int depth, int options);
 
 void
 xml_log_patchset(uint8_t log_level, const char *function, xmlNode * patchset)
 {
     int format = 1;
     xmlNode *child = NULL;
     xmlNode *added = NULL;
     xmlNode *removed = NULL;
     gboolean is_first = TRUE;
 
     int add[] = { 0, 0, 0 };
     int del[] = { 0, 0, 0 };
 
     const char *fmt = NULL;
     const char *digest = NULL;
     int options = xml_log_option_formatted;
 
     static struct qb_log_callsite *patchset_cs = NULL;
 
     if (patchset_cs == NULL) {
         patchset_cs = qb_log_callsite_get(function, __FILE__, "xml-patchset", log_level, __LINE__, 0);
     }
 
     if (patchset == NULL) {
         crm_trace("Empty patch");
         return;
 
     } else if (log_level == 0) {
         /* Log to stdout */
     } else if (crm_is_callsite_active(patchset_cs, log_level, 0) == FALSE) {
         return;
     }
 
     xml_patch_versions(patchset, add, del);
     fmt = crm_element_value(patchset, "format");
     digest = crm_element_value(patchset, XML_ATTR_DIGEST);
 
     if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
         do_crm_log_alias(log_level, __FILE__, function, __LINE__,
                          "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
         do_crm_log_alias(log_level, __FILE__, function, __LINE__,
                          "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
 
     } else if (patchset != NULL && (add[0] || add[1] || add[2])) {
         do_crm_log_alias(log_level, __FILE__, function, __LINE__, 
                          "%s: Local-only Change: %d.%d.%d", function ? function : "",
                          add[0], add[1], add[2]);
     }
 
     crm_element_value_int(patchset, "format", &format);
     if(format == 2) {
         xmlNode *change = NULL;
 
         for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
             const char *op = crm_element_value(change, XML_DIFF_OP);
             const char *xpath = crm_element_value(change, XML_DIFF_PATH);
 
             if(op == NULL) {
             } else if(strcmp(op, "create") == 0) {
                 int lpc = 0, max = 0;
                 char *prefix = crm_strdup_printf("++ %s: ", xpath);
 
                 max = strlen(prefix);
                 __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
                                   0, xml_log_option_formatted|xml_log_option_open);
 
                 for(lpc = 2; lpc < max; lpc++) {
                     prefix[lpc] = ' ';
                 }
 
                 __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
                                   0, xml_log_option_formatted|xml_log_option_close|xml_log_option_children);
                 free(prefix);
 
             } else if(strcmp(op, "move") == 0) {
                 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+~ %s moved to offset %s", xpath, crm_element_value(change, XML_DIFF_POSITION));
 
             } else if(strcmp(op, "modify") == 0) {
                 xmlNode *clist = first_named_child(change, XML_DIFF_LIST);
                 char buffer_set[XML_BUFFER_SIZE];
                 char buffer_unset[XML_BUFFER_SIZE];
                 int o_set = 0;
                 int o_unset = 0;
 
                 buffer_set[0] = 0;
                 buffer_unset[0] = 0;
                 for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
                     const char *name = crm_element_value(child, "name");
 
                     op = crm_element_value(child, XML_DIFF_OP);
                     if(op == NULL) {
                     } else if(strcmp(op, "set") == 0) {
                         const char *value = crm_element_value(child, "value");
 
                         if(o_set > 0) {
                             o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, ", ");
                         }
                         o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, "@%s=%s", name, value);
 
                     } else if(strcmp(op, "unset") == 0) {
                         if(o_unset > 0) {
                             o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, ", ");
                         }
                         o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, "@%s", name);
                     }
                 }
                 if(o_set) {
                     do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+  %s:  %s", xpath, buffer_set);
                 }
                 if(o_unset) {
                     do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s:  %s", xpath, buffer_unset);
                 }
 
             } else if(strcmp(op, "delete") == 0) {
                 int position = -1;
 
                 crm_element_value_int(change, XML_DIFF_POSITION, &position);
                 if (position >= 0) {
                     do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)", xpath, position);
 
                 } else {
                     do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s", xpath);
                 }
             }
         }
         return;
     }
 
     if (log_level < LOG_DEBUG || function == NULL) {
         options |= xml_log_option_diff_short;
     }
 
     removed = find_xml_node(patchset, "diff-removed", FALSE);
     for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
         log_data_element(log_level, __FILE__, function, __LINE__, "- ", child, 0,
                          options | xml_log_option_diff_minus);
         if (is_first) {
             is_first = FALSE;
         } else {
             do_crm_log_alias(log_level, __FILE__, function, __LINE__, " --- ");
         }
     }
 
     is_first = TRUE;
     added = find_xml_node(patchset, "diff-added", FALSE);
     for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
         log_data_element(log_level, __FILE__, function, __LINE__, "+ ", child, 0,
                          options | xml_log_option_diff_plus);
         if (is_first) {
             is_first = FALSE;
         } else {
             do_crm_log_alias(log_level, __FILE__, function, __LINE__, " +++ ");
         }
     }
 }
 
 void
 xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
 {
     GListPtr gIter = NULL;
     xml_private_t *doc = NULL;
 
     CRM_ASSERT(xml);
     CRM_ASSERT(xml->doc);
 
     doc = xml->doc->_private;
     if(is_not_set(doc->flags, xpf_dirty)) {
         return;
     }
 
     for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
         xml_deleted_obj_t *deleted_obj = gIter->data;
 
         if (deleted_obj->position >= 0) {
             do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
                              deleted_obj->path, deleted_obj->position);
 
         } else {
             do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
                              deleted_obj->path);
         }
     }
 
     log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
                      xml_log_option_formatted|xml_log_option_dirty_add);
 }
 
 void
 xml_accept_changes(xmlNode * xml)
 {
     xmlNode *top = NULL;
     xml_private_t *doc = NULL;
 
     if(xml == NULL) {
         return;
     }
 
     crm_trace("Accepting changes to %p", xml);
     doc = xml->doc->_private;
     top = xmlDocGetRootElement(xml->doc);
 
     __xml_private_clean(xml->doc->_private);
 
     if(is_not_set(doc->flags, xpf_dirty)) {
         doc->flags = xpf_none;
         return;
     }
 
     doc->flags = xpf_none;
     __xml_accept_changes(top);
 }
 
 static xmlNode *
 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
 {
     CRM_CHECK(needle != NULL, return NULL);
     return (needle->type == XML_COMMENT_NODE)?
            find_xml_comment(haystack, needle, exact)
            : find_entity(haystack, crm_element_name(needle), ID(needle));
 }
 
 /* Simplified version for applying v1-style XML patches */
 static void
 __subtract_xml_object(xmlNode * target, xmlNode * patch)
 {
     xmlNode *patch_child = NULL;
     xmlNode *cIter = NULL;
     xmlAttrPtr xIter = NULL;
 
     char *id = NULL;
     const char *name = NULL;
     const char *value = NULL;
 
     if (target == NULL || patch == NULL) {
         return;
     }
 
     if (target->type == XML_COMMENT_NODE) {
         gboolean dummy;
 
         subtract_xml_comment(target->parent, target, patch, &dummy);
     }
 
     name = crm_element_name(target);
     CRM_CHECK(name != NULL, return);
     CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
     CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
 
     /* check for XML_DIFF_MARKER in a child */
     id = crm_element_value_copy(target, XML_ATTR_ID);
     value = crm_element_value(patch, XML_DIFF_MARKER);
     if (value != NULL && strcmp(value, "removed:top") == 0) {
         crm_trace("We are the root of the deletion: %s.id=%s", name, id);
         free_xml(target);
         free(id);
         return;
     }
 
     for (xIter = crm_first_attr(patch); xIter != NULL; xIter = xIter->next) {
         const char *p_name = (const char *)xIter->name;
 
         /* Removing and then restoring the id field would change the ordering of properties */
         if (safe_str_neq(p_name, XML_ATTR_ID)) {
             xml_remove_prop(target, p_name);
         }
     }
 
     /* changes to child objects */
     cIter = __xml_first_child(target);
     while (cIter) {
         xmlNode *target_child = cIter;
 
         cIter = __xml_next(cIter);
         patch_child = find_element(patch, target_child, FALSE);
         __subtract_xml_object(target_child, patch_child);
     }
     free(id);
 }
 
 static void
 __add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * patch)
 {
     xmlNode *patch_child = NULL;
     xmlNode *target_child = NULL;
     xmlAttrPtr xIter = NULL;
 
     const char *id = NULL;
     const char *name = NULL;
     const char *value = NULL;
 
     if (patch == NULL) {
         return;
     } else if (parent == NULL && target == NULL) {
         return;
     }
 
     /* check for XML_DIFF_MARKER in a child */
     value = crm_element_value(patch, XML_DIFF_MARKER);
     if (target == NULL
         && value != NULL
         && strcmp(value, "added:top") == 0) {
         id = ID(patch);
         name = crm_element_name(patch);
         crm_trace("We are the root of the addition: %s.id=%s", name, id);
         add_node_copy(parent, patch);
         return;
 
     } else if(target == NULL) {
         id = ID(patch);
         name = crm_element_name(patch);
         crm_err("Could not locate: %s.id=%s", name, id);
         return;
     }
 
     if (target->type == XML_COMMENT_NODE) {
         add_xml_comment(parent, target, patch);
     }
 
     name = crm_element_name(target);
     CRM_CHECK(name != NULL, return);
     CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
     CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
 
     for (xIter = crm_first_attr(patch); xIter != NULL; xIter = xIter->next) {
         const char *p_name = (const char *)xIter->name;
         const char *p_value = crm_element_value(patch, p_name);
 
         xml_remove_prop(target, p_name); /* Preserve the patch order */
         crm_xml_add(target, p_name, p_value);
     }
 
     /* changes to child objects */
     for (patch_child = __xml_first_child(patch); patch_child != NULL;
          patch_child = __xml_next(patch_child)) {
 
         target_child = find_element(target, patch_child, FALSE);
         __add_xml_object(target, target_child, patch_child);
     }
 }
 
 /*!
  * \internal
  * \brief Find additions or removals in a patch set
  *
  * \param[in]     patchset   XML of patch
  * \param[in]     format     Patch version
  * \param[in]     added      TRUE if looking for additions, FALSE if removals
  * \param[in,out] patch_node Will be set to node if found
  *
  * \return TRUE if format is valid, FALSE if invalid
  */
 static bool
 find_patch_xml_node(xmlNode *patchset, int format, bool added,
                     xmlNode **patch_node)
 {
     xmlNode *cib_node;
     const char *label;
 
     switch(format) {
         case 1:
             label = added? "diff-added" : "diff-removed";
             *patch_node = find_xml_node(patchset, label, FALSE);
             cib_node = find_xml_node(*patch_node, "cib", FALSE);
             if (cib_node != NULL) {
                 *patch_node = cib_node;
             }
             break;
         case 2:
             label = added? "target" : "source";
             *patch_node = find_xml_node(patchset, "version", FALSE);
             *patch_node = find_xml_node(*patch_node, label, FALSE);
             break;
         default:
             crm_warn("Unknown patch format: %d", format);
             *patch_node = NULL;
             return FALSE;
     }
     return TRUE;
 }
 
 bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
 {
     int lpc = 0;
     int format = 1;
     xmlNode *tmp = NULL;
 
     const char *vfields[] = {
         XML_ATTR_GENERATION_ADMIN,
         XML_ATTR_GENERATION,
         XML_ATTR_NUMUPDATES,
     };
 
 
     crm_element_value_int(patchset, "format", &format);
 
     /* Process removals */
     if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
         return -EINVAL;
     }
     if (tmp) {
         for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
             crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
             crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
         }
     }
 
     /* Process additions */
     if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
         return -EINVAL;
     }
     if (tmp) {
         for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
             crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
             crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
         }
     }
 
     return pcmk_ok;
 }
 
 static int
 xml_patch_version_check(xmlNode *xml, xmlNode *patchset, int format) 
 {
     int lpc = 0;
     bool changed = FALSE;
 
     int this[] = { 0, 0, 0 };
     int add[] = { 0, 0, 0 };
     int del[] = { 0, 0, 0 };
 
     const char *vfields[] = {
         XML_ATTR_GENERATION_ADMIN,
         XML_ATTR_GENERATION,
         XML_ATTR_NUMUPDATES,
     };
 
     for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
         crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
         crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
         if (this[lpc] < 0) {
             this[lpc] = 0;
         }
     }
 
     /* Set some defaults in case nothing is present */
     add[0] = this[0];
     add[1] = this[1];
     add[2] = this[2] + 1;
     for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
         del[lpc] = this[lpc];
     }
 
     xml_patch_versions(patchset, add, del);
 
     for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
         if(this[lpc] < del[lpc]) {
             crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
                       this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
             return -pcmk_err_diff_resync;
 
         } else if(this[lpc] > del[lpc]) {
             crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
                      this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
             crm_log_xml_info(patchset, "OldPatch");
             return -pcmk_err_old_data;
         }
     }
 
     for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
         if(add[lpc] > del[lpc]) {
             changed = TRUE;
         }
     }
 
     if(changed == FALSE) {
         crm_notice("Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
         return -pcmk_err_old_data;
     }
 
     crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
              add[0], add[1], add[2], this[0], this[1], this[2]);
     return pcmk_ok;
 }
 
 static int
 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
 {
     int rc = pcmk_ok;
     int root_nodes_seen = 0;
 
     xmlNode *child_diff = NULL;
     xmlNode *added = find_xml_node(patchset, "diff-added", FALSE);
     xmlNode *removed = find_xml_node(patchset, "diff-removed", FALSE);
     xmlNode *old = copy_xml(xml);
 
     crm_trace("Subtraction Phase");
     for (child_diff = __xml_first_child(removed); child_diff != NULL;
          child_diff = __xml_next(child_diff)) {
         CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
         if (root_nodes_seen == 0) {
             __subtract_xml_object(xml, child_diff);
         }
         root_nodes_seen++;
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
         rc = -ENOTUNIQ;
     }
 
     root_nodes_seen = 0;
     crm_trace("Addition Phase");
     if (rc == pcmk_ok) {
         xmlNode *child_diff = NULL;
 
         for (child_diff = __xml_first_child(added); child_diff != NULL;
              child_diff = __xml_next(child_diff)) {
             CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
             if (root_nodes_seen == 0) {
                 __add_xml_object(NULL, xml, child_diff);
             }
             root_nodes_seen++;
         }
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
         rc = -ENOTUNIQ;
     }
 
     purge_diff_markers(xml);       /* Purge prior to checking the digest */
 
     free_xml(old);
     return rc;
 }
 
 static xmlNode *
 __first_xml_child_match(xmlNode *parent, const char *name, const char *id, int position)
 {
     xmlNode *cIter = NULL;
 
     for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
         if(strcmp((const char *)cIter->name, name) != 0) {
             continue;
         } else if(id) {
             const char *cid = ID(cIter);
             if(cid == NULL || strcmp(cid, id) != 0) {
                 continue;
             }
         }
 
         /* The "position" makes sense only for XML comments for now */
         if (cIter->type == XML_COMMENT_NODE
             && position >= 0
             && __xml_offset(cIter) != position) {
             continue;
         }
 
         return cIter;
     }
     return NULL;
 }
 
 static xmlNode *
 __xml_find_path(xmlNode *top, const char *key, int target_position)
 {
     xmlNode *target = (xmlNode*)top->doc;
     char *id = malloc(XML_BUFFER_SIZE);
     char *tag = malloc(XML_BUFFER_SIZE);
     char *section = malloc(XML_BUFFER_SIZE);
     char *current = strdup(key);
     char *remainder = malloc(XML_BUFFER_SIZE);
     int rc = 0;
 
     while(current) {
         rc = sscanf (current, "/%[^/]%s", section, remainder);
         if(rc <= 0) {
             crm_trace("Done");
             break;
 
         } else if(rc > 2) {
             crm_trace("Aborting on %s", current);
             target = NULL;
             break;
 
         } else if(tag && section) {
             int f = sscanf (section, "%[^[][@id='%[^']", tag, id);
             int current_position = -1;
 
             /* The "target_position" is for the target tag */
             if (rc == 1 && target_position >= 0) {
                 current_position = target_position;
             }
 
             switch(f) {
                 case 1:
                     target = __first_xml_child_match(target, tag, NULL, current_position);
                     break;
                 case 2:
                     target = __first_xml_child_match(target, tag, id, current_position);
                     break;
                 default:
                     crm_trace("Aborting on %s", section);
                     target = NULL;
                     break;
             }
 
             if(rc == 1 || target == NULL) {
                 crm_trace("Done");
                 break;
 
             } else {
                 char *tmp = current;
                 current = remainder;
                 remainder = tmp;
             }
         }
     }
 
     if(target) {
         char *path = (char *)xmlGetNodePath(target);
 
         crm_trace("Found %s for %s", path, key);
         free(path);
     } else {
         crm_debug("No match for %s", key);
     }
 
     free(remainder);
     free(current);
     free(section);
     free(tag);
     free(id);
     return target;
 }
 
 static int
 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
 {
     int rc = pcmk_ok;
     xmlNode *change = NULL;
     for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
         xmlNode *match = NULL;
         const char *op = crm_element_value(change, XML_DIFF_OP);
         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
         int position = -1;
 
         crm_trace("Processing %s %s", change->name, op);
         if(op == NULL) {
             continue;
         }
 
         if(strcmp(op, "delete") == 0) {
             crm_element_value_int(change, XML_DIFF_POSITION, &position);
         }
 #if 0
         match = get_xpath_object(xpath, xml, LOG_TRACE);
 #else
         match = __xml_find_path(xml, xpath, position);
 #endif
         crm_trace("Performing %s on %s with %p", op, xpath, match);
 
         if(match == NULL && strcmp(op, "delete") == 0) {
             crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
             continue;
 
         } else if(match == NULL) {
             crm_err("No %s match for %s in %p", op, xpath, xml->doc);
             rc = -pcmk_err_diff_failed;
             continue;
 
         } else if(strcmp(op, "create") == 0) {
             int position = 0;
             xmlNode *child = NULL;
             xmlNode *match_child = NULL;
 
             match_child = match->children;
             crm_element_value_int(change, XML_DIFF_POSITION, &position);
 
             while(match_child && position != __xml_offset(match_child)) {
                 match_child = match_child->next;
             }
 
             child = xmlDocCopyNode(change->children, match->doc, 1);
             if(match_child) {
                 crm_trace("Adding %s at position %d", child->name, position);
                 xmlAddPrevSibling(match_child, child);
 
             } else if(match->last) { /* Add to the end */
                 crm_trace("Adding %s at position %d (end)", child->name, position);
                 xmlAddNextSibling(match->last, child);
 
             } else {
                 crm_trace("Adding %s at position %d (first)", child->name, position);
                 CRM_LOG_ASSERT(position == 0);
                 xmlAddChild(match, child);
             }
             crm_node_created(child);
 
         } else if(strcmp(op, "move") == 0) {
             int position = 0;
 
             crm_element_value_int(change, XML_DIFF_POSITION, &position);
             if(position != __xml_offset(match)) {
                 xmlNode *match_child = NULL;
                 int p = position;
 
                 if(p > __xml_offset(match)) {
                     p++; /* Skip ourselves */
                 }
 
                 CRM_ASSERT(match->parent != NULL);
                 match_child = match->parent->children;
 
                 while(match_child && p != __xml_offset(match_child)) {
                     match_child = match_child->next;
                 }
 
                 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
                          match->name, position, __xml_offset(match), match->prev,
                          match_child?"next":"last", match_child?match_child:match->parent->last);
 
                 if(match_child) {
                     xmlAddPrevSibling(match_child, match);
 
                 } else {
                     CRM_ASSERT(match->parent->last != NULL);
                     xmlAddNextSibling(match->parent->last, match);
                 }
 
             } else {
                 crm_trace("%s is already in position %d", match->name, position);
             }
 
             if(position != __xml_offset(match)) {
                 crm_err("Moved %s.%d to position %d instead of %d (%p)",
                         match->name, ID(match), __xml_offset(match), position, match->prev);
                 rc = -pcmk_err_diff_failed;
             }
 
         } else if(strcmp(op, "delete") == 0) {
             free_xml(match);
 
         } else if(strcmp(op, "modify") == 0) {
             xmlAttr *pIter = crm_first_attr(match);
             xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
 
             if(attrs == NULL) {
                 rc = -ENOMSG;
                 continue;
             }
             while(pIter != NULL) {
                 const char *name = (const char *)pIter->name;
 
                 pIter = pIter->next;
                 xml_remove_prop(match, name);
             }
 
             for (pIter = crm_first_attr(attrs); pIter != NULL; pIter = pIter->next) {
                 const char *name = (const char *)pIter->name;
                 const char *value = crm_element_value(attrs, name);
 
                 crm_xml_add(match, name, value);
             }
 
         } else {
             crm_err("Unknown operation: %s", op);
         }
     }
     return rc;
 }
 
 int
 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version) 
 {
     int format = 1;
     int rc = pcmk_ok;
     xmlNode *old = NULL;
     const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
 
     if(patchset == NULL) {
         return rc;
     }
 
     xml_log_patchset(LOG_TRACE, __FUNCTION__, patchset);
 
     crm_element_value_int(patchset, "format", &format);
     if(check_version) {
         rc = xml_patch_version_check(xml, patchset, format);
         if(rc != pcmk_ok) {
             return rc;
         }
     }
 
     if(digest) {
         /* Make it available for logging if the result doesn't have the expected digest */
         old = copy_xml(xml);
     }
 
     if(rc == pcmk_ok) {
         switch(format) {
             case 1:
                 rc = xml_apply_patchset_v1(xml, patchset);
                 break;
             case 2:
                 rc = xml_apply_patchset_v2(xml, patchset);
                 break;
             default:
                 crm_err("Unknown patch format: %d", format);
                 rc = -EINVAL;
         }
     }
 
     if(rc == pcmk_ok && digest) {
         static struct qb_log_callsite *digest_cs = NULL;
 
         char *new_digest = NULL;
         char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION);
 
         if (digest_cs == NULL) {
             digest_cs =
                 qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
                                     crm_trace_nonlog);
         }
 
         new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
         if (safe_str_neq(new_digest, digest)) {
             crm_info("v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
             rc = -pcmk_err_diff_failed;
 
             if (digest_cs && digest_cs->targets) {
                 save_xml_to_file(old,     "PatchDigest:input", NULL);
                 save_xml_to_file(xml,     "PatchDigest:result", NULL);
                 save_xml_to_file(patchset,"PatchDigest:diff", NULL);
 
             } else {
                 crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
             }
 
         } else {
             crm_trace("v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
         }
         free(new_digest);
         free(version);
     }
     free_xml(old);
     return rc;
 }
 
 xmlNode *
 find_xml_node(xmlNode * root, const char *search_path, gboolean must_find)
 {
     xmlNode *a_child = NULL;
     const char *name = "NULL";
 
     if (root != NULL) {
         name = crm_element_name(root);
     }
 
     if (search_path == NULL) {
         crm_warn("Will never find <NULL>");
         return NULL;
     }
 
     for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
         if (strcmp((const char *)a_child->name, search_path) == 0) {
 /* 		crm_trace("returning node (%s).", crm_element_name(a_child)); */
             return a_child;
         }
     }
 
     if (must_find) {
         crm_warn("Could not find %s in %s.", search_path, name);
     } else if (root != NULL) {
         crm_trace("Could not find %s in %s.", search_path, name);
     } else {
         crm_trace("Could not find %s in <NULL>.", search_path);
     }
 
     return NULL;
 }
 
 xmlNode *
 find_entity(xmlNode * parent, const char *node_name, const char *id)
 {
     xmlNode *a_child = NULL;
 
     for (a_child = __xml_first_child(parent); a_child != NULL; a_child = __xml_next(a_child)) {
         /* Uncertain if node_name == NULL check is strictly necessary here */
         if (node_name == NULL || strcmp((const char *)a_child->name, node_name) == 0) {
             const char *cid = ID(a_child);
             if (id == NULL || (cid != NULL && strcmp(id, cid) == 0)) {
                 return a_child;
             }
         }
     }
 
     crm_trace("node <%s id=%s> not found in %s.", node_name, id, crm_element_name(parent));
     return NULL;
 }
 
 void
 copy_in_properties(xmlNode * target, xmlNode * src)
 {
     if (src == NULL) {
         crm_warn("No node to copy properties from");
 
     } else if (target == NULL) {
         crm_err("No node to copy properties into");
 
     } else {
         xmlAttrPtr pIter = NULL;
 
         for (pIter = crm_first_attr(src); pIter != NULL; pIter = pIter->next) {
             const char *p_name = (const char *)pIter->name;
             const char *p_value = crm_attr_value(pIter);
 
             expand_plus_plus(target, p_name, p_value);
         }
     }
 
     return;
 }
 
 void
 fix_plus_plus_recursive(xmlNode * target)
 {
     /* TODO: Remove recursion and use xpath searches for value++ */
     xmlNode *child = NULL;
     xmlAttrPtr pIter = NULL;
 
     for (pIter = crm_first_attr(target); pIter != NULL; pIter = pIter->next) {
         const char *p_name = (const char *)pIter->name;
         const char *p_value = crm_attr_value(pIter);
 
         expand_plus_plus(target, p_name, p_value);
     }
     for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
         fix_plus_plus_recursive(child);
     }
 }
 
 void
 expand_plus_plus(xmlNode * target, const char *name, const char *value)
 {
     int offset = 1;
     int name_len = 0;
     int int_value = 0;
     int value_len = 0;
 
     const char *old_value = NULL;
 
     if (value == NULL || name == NULL) {
         return;
     }
 
     old_value = crm_element_value(target, name);
 
     if (old_value == NULL) {
         /* if no previous value, set unexpanded */
         goto set_unexpanded;
 
     } else if (strstr(value, name) != value) {
         goto set_unexpanded;
     }
 
     name_len = strlen(name);
     value_len = strlen(value);
     if (value_len < (name_len + 2)
         || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
         goto set_unexpanded;
     }
 
     /* if we are expanding ourselves,
      * then no previous value was set and leave int_value as 0
      */
     if (old_value != value) {
         int_value = char2score(old_value);
     }
 
     if (value[name_len + 1] != '+') {
         const char *offset_s = value + (name_len + 2);
 
         offset = char2score(offset_s);
     }
     int_value += offset;
 
     if (int_value > INFINITY) {
         int_value = (int)INFINITY;
     }
 
     crm_xml_add_int(target, name, int_value);
     return;
 
   set_unexpanded:
     if (old_value == value) {
         /* the old value is already set, nothing to do */
         return;
     }
     crm_xml_add(target, name, value);
     return;
 }
 
 xmlDoc *
 getDocPtr(xmlNode * node)
 {
     xmlDoc *doc = NULL;
 
     CRM_CHECK(node != NULL, return NULL);
 
     doc = node->doc;
     if (doc == NULL) {
         doc = xmlNewDoc((const xmlChar *)"1.0");
         xmlDocSetRootElement(doc, node);
         xmlSetTreeDoc(node, doc);
     }
     return doc;
 }
 
 xmlNode *
 add_node_copy(xmlNode * parent, xmlNode * src_node)
 {
     xmlNode *child = NULL;
     xmlDoc *doc = getDocPtr(parent);
 
     CRM_CHECK(src_node != NULL, return NULL);
 
     child = xmlDocCopyNode(src_node, doc, 1);
     xmlAddChild(parent, child);
     crm_node_created(child);
     return child;
 }
 
 int
 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
 {
     add_node_copy(parent, child);
     free_xml(child);
     return 1;
 }
 
 static bool
 __xml_acl_check(xmlNode *xml, const char *name, enum xml_private_flags mode)
 {
     CRM_ASSERT(xml);
     CRM_ASSERT(xml->doc);
     CRM_ASSERT(xml->doc->_private);
 
 #if ENABLE_ACL
     {
         if(TRACKING_CHANGES(xml) && xml_acl_enabled(xml)) {
             int offset = 0;
             xmlNode *parent = xml;
             char buffer[XML_BUFFER_SIZE];
             xml_private_t *docp = xml->doc->_private;
 
             if(docp->acls == NULL) {
                 crm_trace("Ordinary user %s cannot access the CIB without any defined ACLs", docp->user);
                 set_doc_flag(xml, xpf_acl_denied);
                 return FALSE;
             }
 
             offset = __get_prefix(NULL, xml, buffer, offset);
             if(name) {
                 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "[@%s]", name);
             }
             CRM_LOG_ASSERT(offset > 0);
 
             /* Walk the tree upwards looking for xml_acl_* flags
              * - Creating an attribute requires write permissions for the node
              * - Creating a child requires write permissions for the parent
              */
 
             if(name) {
                 xmlAttr *attr = xmlHasProp(xml, (const xmlChar *)name);
 
                 if(attr && mode == xpf_acl_create) {
                     mode = xpf_acl_write;
                 }
             }
 
             while(parent && parent->_private) {
                 xml_private_t *p = parent->_private;
                 if(__xml_acl_mode_test(p->flags, mode)) {
                     return TRUE;
 
                 } else if(is_set(p->flags, xpf_acl_deny)) {
                     crm_trace("%x access denied to %s: parent", mode, buffer);
                     set_doc_flag(xml, xpf_acl_denied);
                     return FALSE;
                 }
                 parent = parent->parent;
             }
 
             crm_trace("%x access denied to %s: default", mode, buffer);
             set_doc_flag(xml, xpf_acl_denied);
             return FALSE;
         }
     }
 #endif
 
     return TRUE;
 }
 
 const char *
 crm_xml_add(xmlNode * node, const char *name, const char *value)
 {
     bool dirty = FALSE;
     xmlAttr *attr = NULL;
 
     CRM_CHECK(node != NULL, return NULL);
     CRM_CHECK(name != NULL, return NULL);
 
     if (value == NULL) {
         return NULL;
     }
 #if XML_PARANOIA_CHECKS
     {
         const char *old_value = NULL;
 
         old_value = crm_element_value(node, name);
 
         /* Could be re-setting the same value */
         CRM_CHECK(old_value != value, crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
                   return value);
     }
 #endif
 
     if(TRACKING_CHANGES(node)) {
         const char *old = crm_element_value(node, name);
 
         if(old == NULL || value == NULL || strcmp(old, value) != 0) {
             dirty = TRUE;
         }
     }
 
     if(dirty && __xml_acl_check(node, name, xpf_acl_create) == FALSE) {
         crm_trace("Cannot add %s=%s to %s", name, value, node->name);
         return NULL;
     }
 
     attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
     if(dirty) {
         crm_attr_dirty(attr);
     }
 
     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
     return (char *)attr->children->content;
 }
 
 const char *
 crm_xml_replace(xmlNode * node, const char *name, const char *value)
 {
     bool dirty = FALSE;
     xmlAttr *attr = NULL;
     const char *old_value = NULL;
 
     CRM_CHECK(node != NULL, return NULL);
     CRM_CHECK(name != NULL && name[0] != 0, return NULL);
 
     old_value = crm_element_value(node, name);
 
     /* Could be re-setting the same value */
     CRM_CHECK(old_value != value, return value);
 
     if(__xml_acl_check(node, name, xpf_acl_write) == FALSE) {
         /* Create a fake object linked to doc->_private instead? */
         crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
         return NULL;
 
     } else if (old_value != NULL && value == NULL) {
         xml_remove_prop(node, name);
         return NULL;
 
     } else if (value == NULL) {
         return NULL;
     }
 
     if(TRACKING_CHANGES(node)) {
         if(old_value == NULL || value == NULL || strcmp(old_value, value) != 0) {
             dirty = TRUE;
         }
     }
 
     attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
     if(dirty) {
         crm_attr_dirty(attr);
     }
     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
     return (char *)attr->children->content;
 }
 
 const char *
 crm_xml_add_int(xmlNode * node, const char *name, int value)
 {
     char *number = crm_itoa(value);
     const char *added = crm_xml_add(node, name, number);
 
     free(number);
     return added;
 }
 
 xmlNode *
 create_xml_node(xmlNode * parent, const char *name)
 {
     xmlDoc *doc = NULL;
     xmlNode *node = NULL;
 
     if (name == NULL || name[0] == 0) {
         CRM_CHECK(name != NULL && name[0] == 0, return NULL);
         return NULL;
     }
 
     if (parent == NULL) {
         doc = xmlNewDoc((const xmlChar *)"1.0");
         node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
         xmlDocSetRootElement(doc, node);
 
     } else {
         doc = getDocPtr(parent);
         node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
         xmlAddChild(parent, node);
     }
     crm_node_created(node);
     return node;
 }
 
 static inline int
 __get_prefix(const char *prefix, xmlNode *xml, char *buffer, int offset)
 {
     const char *id = ID(xml);
 
     if(offset == 0 && prefix == NULL && xml->parent) {
         offset = __get_prefix(NULL, xml->parent, buffer, offset);
     }
 
     if(id) {
         offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s[@id='%s']", (const char *)xml->name, id);
     } else if(xml->name) {
         offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s", (const char *)xml->name);
     }
 
     return offset;
 }
 
 char *
 xml_get_path(xmlNode *xml)
 {
     int offset = 0;
     char buffer[XML_BUFFER_SIZE];
 
     if(__get_prefix(NULL, xml, buffer, offset) > 0) {
         return strdup(buffer);
     }
     return NULL;
 }
 
 static void
 free_xml_with_position(xmlNode * child, int position)
 {
     if (child != NULL) {
         xmlNode *top = NULL;
         xmlDoc *doc = child->doc;
         xml_private_t *p = child->_private;
 
         if (doc != NULL) {
             top = xmlDocGetRootElement(doc);
         }
 
         if (doc != NULL && top == child) {
             /* Free everything */
             xmlFreeDoc(doc);
 
         } else if(__xml_acl_check(child, NULL, xpf_acl_write) == FALSE) {
             int offset = 0;
             char buffer[XML_BUFFER_SIZE];
 
             __get_prefix(NULL, child, buffer, offset);
             crm_trace("Cannot remove %s %x", buffer, p->flags);
             return;
 
         } else {
             if(doc && TRACKING_CHANGES(child) && is_not_set(p->flags, xpf_created)) {
                 int offset = 0;
                 char buffer[XML_BUFFER_SIZE];
 
                 if(__get_prefix(NULL, child, buffer, offset) > 0) {
                     xml_deleted_obj_t *deleted_obj = calloc(1, sizeof(xml_deleted_obj_t));
 
                     crm_trace("Deleting %s %p from %p", buffer, child, doc);
 
                     deleted_obj->path = strdup(buffer);
 
                     deleted_obj->position = -1;
                     /* Record the "position" only for XML comments for now */
                     if (child->type == XML_COMMENT_NODE) {
                         if (position >= 0) {
                             deleted_obj->position = position;
 
                         } else {
                             deleted_obj->position = __xml_offset(child);
                         }
                     }
 
                     p = doc->_private;
                     p->deleted_objs = g_list_append(p->deleted_objs, deleted_obj);
                     set_doc_flag(child, xpf_dirty);
                 }
             }
 
             /* Free this particular subtree
              * Make sure to unlink it from the parent first
              */
             xmlUnlinkNode(child);
             xmlFreeNode(child);
         }
     }
 }
 
 
 void
 free_xml(xmlNode * child)
 {
     free_xml_with_position(child, -1);
 }
 
 xmlNode *
 copy_xml(xmlNode * src)
 {
     xmlDoc *doc = xmlNewDoc((const xmlChar *)"1.0");
     xmlNode *copy = xmlDocCopyNode(src, doc, 1);
 
     xmlDocSetRootElement(doc, copy);
     xmlSetTreeDoc(copy, doc);
     return copy;
 }
 
 static void
 crm_xml_err(void *ctx, const char *fmt, ...)
 G_GNUC_PRINTF(2, 3);
 
 static void
 crm_xml_err(void *ctx, const char *fmt, ...)
 {
     va_list ap;
     static struct qb_log_callsite *xml_error_cs = NULL;
 
     if (xml_error_cs == NULL) {
         xml_error_cs = qb_log_callsite_get(
             __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
     }
 
     va_start(ap, fmt);
     if (xml_error_cs && xml_error_cs->targets) {
         CRM_XML_LOG_BASE(LOG_ERR, TRUE,
                          crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
                                    TRUE, TRUE),
                          "XML Error: ", fmt, ap);
     } else {
         CRM_XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
     }
     va_end(ap);
 }
 
 xmlNode *
 string2xml(const char *input)
 {
     xmlNode *xml = NULL;
     xmlDocPtr output = NULL;
     xmlParserCtxtPtr ctxt = NULL;
     xmlErrorPtr last_error = NULL;
 
     if (input == NULL) {
         crm_err("Can't parse NULL input");
         return NULL;
     }
 
     /* create a parser context */
     ctxt = xmlNewParserCtxt();
     CRM_CHECK(ctxt != NULL, return NULL);
 
     /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
 
     xmlCtxtResetLastError(ctxt);
     xmlSetGenericErrorFunc(ctxt, crm_xml_err);
     /* initGenericErrorDefaultFunc(crm_xml_err); */
     output =
         xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL,
                        XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
     if (output) {
         xml = xmlDocGetRootElement(output);
     }
     last_error = xmlCtxtGetLastError(ctxt);
     if (last_error && last_error->code != XML_ERR_OK) {
         /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
         /*
          * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
          * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
          */
         crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
                  last_error->domain, last_error->level, last_error->code, last_error->message);
 
         if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
             CRM_LOG_ASSERT("Cannot parse an empty string");
 
         } else if (last_error->code != XML_ERR_DOCUMENT_END) {
             crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
                     input);
             if (xml != NULL) {
                 crm_log_xml_err(xml, "Partial");
             }
 
         } else {
             int len = strlen(input);
             int lpc = 0;
 
             while(lpc < len) {
                 crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
                 lpc += 80;
             }
 
             CRM_LOG_ASSERT("String parsing error");
         }
     }
 
     xmlFreeParserCtxt(ctxt);
     return xml;
 }
 
 xmlNode *
 stdin2xml(void)
 {
     size_t data_length = 0;
     size_t read_chars = 0;
 
     char *xml_buffer = NULL;
     xmlNode *xml_obj = NULL;
 
     do {
         size_t next = XML_BUFFER_SIZE + data_length + 1;
 
         if(next <= 0) {
             crm_err("Buffer size exceeded at: %l + %d", data_length, XML_BUFFER_SIZE);
             break;
         }
 
         xml_buffer = realloc_safe(xml_buffer, next);
         read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
         data_length += read_chars;
     } while (read_chars > 0);
 
     if (data_length == 0) {
         crm_warn("No XML supplied on stdin");
         free(xml_buffer);
         return NULL;
     }
 
     xml_buffer[data_length] = '\0';
 
     xml_obj = string2xml(xml_buffer);
     free(xml_buffer);
 
     crm_log_xml_trace(xml_obj, "Created fragment");
     return xml_obj;
 }
 
 static char *
 decompress_file(const char *filename)
 {
     char *buffer = NULL;
 
 #if HAVE_BZLIB_H
     int rc = 0;
     size_t length = 0, read_len = 0;
 
     BZFILE *bz_file = NULL;
     FILE *input = fopen(filename, "r");
 
     if (input == NULL) {
         crm_perror(LOG_ERR, "Could not open %s for reading", filename);
         return NULL;
     }
 
     bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
     if (rc != BZ_OK) {
         crm_err("Could not prepare to read compressed %s: %s "
                 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
         BZ2_bzReadClose(&rc, bz_file);
         return NULL;
     }
 
     rc = BZ_OK;
     while (rc == BZ_OK) {
         buffer = realloc_safe(buffer, XML_BUFFER_SIZE + length + 1);
         read_len = BZ2_bzRead(&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
 
         crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
 
         if (rc == BZ_OK || rc == BZ_STREAM_END) {
             length += read_len;
         }
     }
 
     buffer[length] = '\0';
 
     if (rc != BZ_STREAM_END) {
         crm_err("Could not read compressed %s: %s "
                 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
         free(buffer);
         buffer = NULL;
     }
 
     BZ2_bzReadClose(&rc, bz_file);
     fclose(input);
 
 #else
     crm_err("Could not read compressed %s: not built with bzlib support",
             filename);
 #endif
     return buffer;
 }
 
 void
 strip_text_nodes(xmlNode * xml)
 {
     xmlNode *iter = xml->children;
 
     while (iter) {
         xmlNode *next = iter->next;
 
         switch (iter->type) {
             case XML_TEXT_NODE:
                 /* Remove it */
                 xmlUnlinkNode(iter);
                 xmlFreeNode(iter);
                 break;
 
             case XML_ELEMENT_NODE:
                 /* Search it */
                 strip_text_nodes(iter);
                 break;
 
             default:
                 /* Leave it */
                 break;
         }
 
         iter = next;
     }
 }
 
 xmlNode *
 filename2xml(const char *filename)
 {
     xmlNode *xml = NULL;
     xmlDocPtr output = NULL;
     gboolean uncompressed = TRUE;
     xmlParserCtxtPtr ctxt = NULL;
     xmlErrorPtr last_error = NULL;
     static int xml_options = XML_PARSE_NOBLANKS | XML_PARSE_RECOVER;
 
     /* create a parser context */
     ctxt = xmlNewParserCtxt();
     CRM_CHECK(ctxt != NULL, return NULL);
 
     /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
 
     xmlCtxtResetLastError(ctxt);
     xmlSetGenericErrorFunc(ctxt, crm_xml_err);
     /* initGenericErrorDefaultFunc(crm_xml_err); */
 
     if (filename) {
         uncompressed = !crm_ends_with_ext(filename, ".bz2");
     }
 
     if (filename == NULL) {
         /* STDIN_FILENO == fileno(stdin) */
         output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options);
 
     } else if (uncompressed) {
         output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
 
     } else {
         char *input = decompress_file(filename);
 
         output = xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL,
                                 xml_options);
         free(input);
     }
 
     if (output && (xml = xmlDocGetRootElement(output))) {
         strip_text_nodes(xml);
     }
 
     last_error = xmlCtxtGetLastError(ctxt);
     if (last_error && last_error->code != XML_ERR_OK) {
         /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
         /*
          * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
          * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
          */
         crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
                 last_error->domain, last_error->level, last_error->code, last_error->message);
 
         if (last_error && last_error->code != XML_ERR_OK) {
             crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
             if (xml != NULL) {
                 crm_log_xml_err(xml, "Partial");
             }
         }
     }
 
     xmlFreeParserCtxt(ctxt);
     return xml;
 }
 
 /*!
  * \internal
  * \brief Add a "last written" attribute to an XML node, set to current time
  *
  * \param[in] xml_node XML node to get attribute
  *
  * \return Value that was set, or NULL on error
  */
 const char *
 crm_xml_add_last_written(xmlNode *xml_node)
 {
     time_t now = time(NULL);
     char *now_str = ctime(&now);
 
     now_str[24] = EOS; /* replace the newline */
     return crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
 }
 
 /*!
  * \brief Sanitize a string so it is usable as an XML ID
  *
  * \param[in,out] id  String to sanitize
  */
 void
 crm_xml_sanitize_id(char *id)
 {
     char *c;
 
     for (c = id; *c; ++c) {
         /* @TODO Sanitize more comprehensively */
         switch (*c) {
             case ':':
             case '#':
                 *c = '.';
         }
     }
 }
 
 /*!
  * \brief Set the ID of an XML element using a format
  *
  * \param[in,out] xml  XML element
  * \param[in]     fmt  printf-style format
  * \param[in]     ...  any arguments required by format
  */
 void
 crm_xml_set_id(xmlNode *xml, const char *format, ...)
 {
     va_list ap;
     int len = 0;
     char *id = NULL;
 
     /* equivalent to crm_strdup_printf() */
     va_start(ap, format);
     len = vasprintf(&id, format, ap);
     va_end(ap);
     CRM_ASSERT(len > 0);
 
     crm_xml_sanitize_id(id);
     crm_xml_add(xml, XML_ATTR_ID, id);
     free(id);
 }
 
 /*!
  * \internal
  * \brief Write XML to a file stream
  *
  * \param[in] xml_node  XML to write
  * \param[in] filename  Name of file being written (for logging only)
  * \param[in] stream    Open file stream corresponding to filename
  * \param[in] compress  Whether to compress XML before writing
  *
  * \return Number of bytes written on success, -errno otherwise
  */
 static int
 write_xml_stream(xmlNode * xml_node, const char *filename, FILE * stream, gboolean compress)
 {
     int res = 0;
     char *buffer = NULL;
     unsigned int out = 0;
 
     crm_log_xml_trace(xml_node, "writing");
 
     buffer = dump_xml_formatted(xml_node);
     CRM_CHECK(buffer && strlen(buffer),
               crm_log_xml_warn(xml_node, "formatting failed");
               res = -pcmk_err_generic;
               goto bail);
 
     if (compress) {
 #if HAVE_BZLIB_H
         int rc = BZ_OK;
         unsigned int in = 0;
         BZFILE *bz_file = NULL;
 
         bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
         if (rc != BZ_OK) {
             crm_warn("Not compressing %s: could not prepare file stream: %s "
                      CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
         } else {
             BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
             if (rc != BZ_OK) {
                 crm_warn("Not compressing %s: could not compress data: %s "
                          CRM_XS " bzerror=%d errno=%d",
                          filename, bz2_strerror(rc), rc, errno);
             }
         }
 
         if (rc == BZ_OK) {
             BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
             if (rc != BZ_OK) {
                 crm_warn("Not compressing %s: could not write compressed data: %s "
                          CRM_XS " bzerror=%d errno=%d",
                          filename, bz2_strerror(rc), rc, errno);
                 out = -1; // retry without compression
             } else {
                 res = (int) out;
                 crm_trace("Compressed XML for %s from %d bytes to %d",
                           filename, in, out);
             }
         }
 #else
         crm_warn("Not compressing %s: not built with bzlib support", filename);
 #endif
     }
 
     if (out <= 0) {
         res = fprintf(stream, "%s", buffer);
         if (res < 0) {
             res = -errno;
             crm_perror(LOG_ERR, "writing %s", filename);
             goto bail;
         }
     }
 
   bail:
 
     if (fflush(stream) != 0) {
         res = -errno;
         crm_perror(LOG_ERR, "flushing %s", filename);
     }
 
     /* Don't report error if the file does not support synchronization */
     if (fsync(fileno(stream)) < 0 && errno != EROFS  && errno != EINVAL) {
         res = -errno;
         crm_perror(LOG_ERR, "synchronizing %s", filename);
     }
 
     fclose(stream);
 
     crm_trace("Saved %d bytes%s to %s as XML",
               res, ((out > 0)? " (compressed)" : ""), filename);
     free(buffer);
 
     return res;
 }
 
 /*!
  * \brief Write XML to a file descriptor
  *
  * \param[in] xml_node  XML to write
  * \param[in] filename  Name of file being written (for logging only)
  * \param[in] fd        Open file descriptor corresponding to filename
  * \param[in] compress  Whether to compress XML before writing
  *
  * \return Number of bytes written on success, -errno otherwise
  */
 int
 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
 {
     FILE *stream = NULL;
 
     CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
     stream = fdopen(fd, "w");
     if (stream == NULL) {
         return -errno;
     }
     return write_xml_stream(xml_node, filename, stream, compress);
 }
 
 /*!
  * \brief Write XML to a file
  *
  * \param[in] xml_node  XML to write
  * \param[in] filename  Name of file to write
  * \param[in] compress  Whether to compress XML before writing
  *
  * \return Number of bytes written on success, -errno otherwise
  */
 int
 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
 {
     FILE *stream = NULL;
 
     CRM_CHECK(xml_node && filename, return -EINVAL);
     stream = fopen(filename, "w");
     if (stream == NULL) {
         return -errno;
     }
     return write_xml_stream(xml_node, filename, stream, compress);
 }
 
 xmlNode *
 get_message_xml(xmlNode * msg, const char *field)
 {
     xmlNode *tmp = first_named_child(msg, field);
 
     return __xml_first_child(tmp);
 }
 
 gboolean
 add_message_xml(xmlNode * msg, const char *field, xmlNode * xml)
 {
     xmlNode *holder = create_xml_node(msg, field);
 
     add_node_copy(holder, xml);
     return TRUE;
 }
 
 static char *
 crm_xml_escape_shuffle(char *text, int start, int *length, const char *replace)
 {
     int lpc;
     int offset = strlen(replace) - 1;   /* We have space for 1 char already */
 
     *length += offset;
     text = realloc_safe(text, *length);
 
     for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
         text[lpc] = text[lpc - offset];
     }
 
     memcpy(text + start, replace, offset + 1);
     return text;
 }
 
 char *
 crm_xml_escape(const char *text)
 {
     int index;
     int changes = 0;
     int length = 1 + strlen(text);
     char *copy = strdup(text);
 
     /*
      * When xmlCtxtReadDoc() parses &lt; and friends in a
      * value, it converts them to their human readable
      * form.
      *
      * If one uses xmlNodeDump() to convert it back to a
      * string, all is well, because special characters are
      * converted back to their escape sequences.
      *
      * However xmlNodeDump() is randomly dog slow, even with the same
      * input. So we need to replicate the escaping in our custom
      * version so that the result can be re-parsed by xmlCtxtReadDoc()
      * when necessary.
      */
 
     for (index = 0; index < length; index++) {
         switch (copy[index]) {
             case 0:
                 break;
             case '<':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "&lt;");
                 changes++;
                 break;
             case '>':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "&gt;");
                 changes++;
                 break;
             case '"':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "&quot;");
                 changes++;
                 break;
             case '\'':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "&apos;");
                 changes++;
                 break;
             case '&':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "&amp;");
                 changes++;
                 break;
             case '\t':
                 /* Might as well just expand to a few spaces... */
                 copy = crm_xml_escape_shuffle(copy, index, &length, "    ");
                 changes++;
                 break;
             case '\n':
                 /* crm_trace("Convert: \\%.3o", copy[index]); */
                 copy = crm_xml_escape_shuffle(copy, index, &length, "\\n");
                 changes++;
                 break;
             case '\r':
                 copy = crm_xml_escape_shuffle(copy, index, &length, "\\r");
                 changes++;
                 break;
                 /* For debugging...
             case '\\':
                 crm_trace("Passthrough: \\%c", copy[index+1]);
                 break;
                 */
             default:
                 /* Check for and replace non-printing characters with their octal equivalent */
                 if(copy[index] < ' ' || copy[index] > '~') {
                     char *replace = crm_strdup_printf("\\%.3o", copy[index]);
 
                     /* crm_trace("Convert to octal: \\%.3o", copy[index]); */
                     copy = crm_xml_escape_shuffle(copy, index, &length, replace);
                     free(replace);
                     changes++;
                 }
         }
     }
 
     if (changes) {
         crm_trace("Dumped '%s'", copy);
     }
     return copy;
 }
 
 static inline void
 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
 {
     char *p_value = NULL;
     const char *p_name = NULL;
     xml_private_t *p = NULL;
 
     CRM_ASSERT(buffer != NULL);
     if (attr == NULL || attr->children == NULL) {
         return;
     }
 
     p = attr->_private;
     if (p && is_set(p->flags, xpf_deleted)) {
         return;
     }
 
     p_name = (const char *)attr->name;
     p_value = crm_xml_escape((const char *)attr->children->content);
     buffer_print(*buffer, *max, *offset, " %s=\"%s\"", p_name, p_value);
     free(p_value);
 }
 
 static void
 __xml_log_element(int log_level, const char *file, const char *function, int line,
                   const char *prefix, xmlNode * data, int depth, int options)
 {
     int max = 0;
     int offset = 0;
     const char *name = NULL;
     const char *hidden = NULL;
 
     xmlNode *child = NULL;
     xmlAttrPtr pIter = NULL;
 
     if(data == NULL) {
         return;
     }
 
     name = crm_element_name(data);
 
     if(is_set(options, xml_log_option_open)) {
         char *buffer = NULL;
 
         insert_prefix(options, &buffer, &offset, &max, depth);
 
         if (data->type == XML_COMMENT_NODE) {
             buffer_print(buffer, max, offset, "<!--%s-->", data->content);
 
         } else {
             buffer_print(buffer, max, offset, "<%s", name);
 
             hidden = crm_element_value(data, "hidden");
             for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
                 xml_private_t *p = pIter->_private;
                 const char *p_name = (const char *)pIter->name;
                 const char *p_value = crm_attr_value(pIter);
                 char *p_copy = NULL;
 
                 if(is_set(p->flags, xpf_deleted)) {
                     continue;
                 } else if ((is_set(options, xml_log_option_diff_plus)
                      || is_set(options, xml_log_option_diff_minus))
                     && strcmp(XML_DIFF_MARKER, p_name) == 0) {
                     continue;
 
                 } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
                     p_copy = strdup("*****");
 
                 } else {
                     p_copy = crm_xml_escape(p_value);
                 }
 
                 buffer_print(buffer, max, offset, " %s=\"%s\"", p_name, p_copy);
                 free(p_copy);
             }
 
             if(xml_has_children(data) == FALSE) {
                 buffer_print(buffer, max, offset, "/>");
 
             } else if(is_set(options, xml_log_option_children)) {
                 buffer_print(buffer, max, offset, ">");
 
             } else {
                 buffer_print(buffer, max, offset, "/>");
             }
         }
 
         do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
         free(buffer);
     }
 
     if(data->type == XML_COMMENT_NODE) {
         return;
 
     } else if(xml_has_children(data) == FALSE) {
         return;
 
     } else if(is_set(options, xml_log_option_children)) {
         offset = 0;
         max = 0;
 
         for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
             __xml_log_element(log_level, file, function, line, prefix, child, depth + 1, options|xml_log_option_open|xml_log_option_close);
         }
     }
 
     if(is_set(options, xml_log_option_close)) {
         char *buffer = NULL;
 
         insert_prefix(options, &buffer, &offset, &max, depth);
         buffer_print(buffer, max, offset, "</%s>", name);
 
         do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
         free(buffer);
     }
 }
 
 static void
 __xml_log_change_element(int log_level, const char *file, const char *function, int line,
                          const char *prefix, xmlNode * data, int depth, int options)
 {
     xml_private_t *p;
     char *prefix_m = NULL;
     xmlNode *child = NULL;
     xmlAttrPtr pIter = NULL;
 
     if(data == NULL) {
         return;
     }
 
     p = data->_private;
 
     prefix_m = strdup(prefix);
     prefix_m[1] = '+';
 
     if(is_set(p->flags, xpf_dirty) && is_set(p->flags, xpf_created)) {
         /* Continue and log full subtree */
         __xml_log_element(log_level, file, function, line,
                           prefix_m, data, depth, options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
 
     } else if(is_set(p->flags, xpf_dirty)) {
         char *spaces = calloc(80, 1);
         int s_count = 0, s_max = 80;
         char *prefix_del = NULL;
         char *prefix_moved = NULL;
         const char *flags = prefix;
 
         insert_prefix(options, &spaces, &s_count, &s_max, depth);
         prefix_del = strdup(prefix);
         prefix_del[0] = '-';
         prefix_del[1] = '-';
         prefix_moved = strdup(prefix);
         prefix_moved[1] = '~';
 
         if(is_set(p->flags, xpf_moved)) {
             flags = prefix_moved;
         } else {
             flags = prefix;
         }
 
         __xml_log_element(log_level, file, function, line,
                           flags, data, depth, options|xml_log_option_open);
 
         for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
             const char *aname = (const char*)pIter->name;
 
             p = pIter->_private;
             if(is_set(p->flags, xpf_deleted)) {
                 const char *value = crm_element_value(data, aname);
                 flags = prefix_del;
                 do_crm_log_alias(log_level, file, function, line,
                                  "%s %s @%s=%s", flags, spaces, aname, value);
 
             } else if(is_set(p->flags, xpf_dirty)) {
                 const char *value = crm_element_value(data, aname);
 
                 if(is_set(p->flags, xpf_created)) {
                     flags = prefix_m;
 
                 } else if(is_set(p->flags, xpf_modified)) {
                     flags = prefix;
 
                 } else if(is_set(p->flags, xpf_moved)) {
                     flags = prefix_moved;
 
                 } else {
                     flags = prefix;
                 }
                 do_crm_log_alias(log_level, file, function, line,
                                  "%s %s @%s=%s", flags, spaces, aname, value);
             }
         }
         free(prefix_moved);
         free(prefix_del);
         free(spaces);
 
         for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
             __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
         }
 
         __xml_log_element(log_level, file, function, line,
                           prefix, data, depth, options|xml_log_option_close);
 
     } else {
         for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
             __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
         }
     }
 
     free(prefix_m);
 
 }
 
 void
 log_data_element(int log_level, const char *file, const char *function, int line,
                  const char *prefix, xmlNode * data, int depth, int options)
 {
     xmlNode *a_child = NULL;
 
     char *prefix_m = NULL;
 
     if (prefix == NULL) {
         prefix = "";
     }
 
     /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
     if (data == NULL) {
         do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
                          "No data to dump as XML");
         return;
     }
 
     if(is_set(options, xml_log_option_dirty_add) || is_set(options, xml_log_option_dirty_add)) {
         __xml_log_change_element(log_level, file, function, line, prefix, data, depth, options);
         return;
     }
 
     if (is_set(options, xml_log_option_formatted)) {
         if (is_set(options, xml_log_option_diff_plus)
             && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
             options |= xml_log_option_diff_all;
             prefix_m = strdup(prefix);
             prefix_m[1] = '+';
             prefix = prefix_m;
 
         } else if (is_set(options, xml_log_option_diff_minus)
                    && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
             options |= xml_log_option_diff_all;
             prefix_m = strdup(prefix);
             prefix_m[1] = '-';
             prefix = prefix_m;
         }
     }
 
     if (is_set(options, xml_log_option_diff_short)
                && is_not_set(options, xml_log_option_diff_all)) {
         /* Still searching for the actual change */
         for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
             log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
         }
     } else {
         __xml_log_element(log_level, file, function, line, prefix, data, depth,
                           options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
     }
     free(prefix_m);
 }
 
 static void
 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
 {
     int lpc;
     xmlAttrPtr xIter = NULL;
     static int filter_len = DIMOF(filter);
 
     for (lpc = 0; options && lpc < filter_len; lpc++) {
         filter[lpc].found = FALSE;
     }
 
     for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
         bool skip = FALSE;
         const char *p_name = (const char *)xIter->name;
 
         for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
             if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].string) == 0) {
                 filter[lpc].found = TRUE;
                 skip = TRUE;
                 break;
             }
         }
 
         if (skip == FALSE) {
             dump_xml_attr(xIter, options, buffer, offset, max);
         }
     }
 }
 
 static void
 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
 {
     const char *name = NULL;
 
     CRM_ASSERT(max != NULL);
     CRM_ASSERT(offset != NULL);
     CRM_ASSERT(buffer != NULL);
 
     if (data == NULL) {
         crm_trace("Nothing to dump");
         return;
     }
 
     if (*buffer == NULL) {
         *offset = 0;
         *max = 0;
     }
 
     name = crm_element_name(data);
     CRM_ASSERT(name != NULL);
 
     insert_prefix(options, buffer, offset, max, depth);
     buffer_print(*buffer, *max, *offset, "<%s", name);
 
     if (options & xml_log_option_filtered) {
         dump_filtered_xml(data, options, buffer, offset, max);
 
     } else {
         xmlAttrPtr xIter = NULL;
 
         for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
             dump_xml_attr(xIter, options, buffer, offset, max);
         }
     }
 
     if (data->children == NULL) {
         buffer_print(*buffer, *max, *offset, "/>");
 
     } else {
         buffer_print(*buffer, *max, *offset, ">");
     }
 
     if (options & xml_log_option_formatted) {
         buffer_print(*buffer, *max, *offset, "\n");
     }
 
     if (data->children) {
         xmlNode *xChild = NULL;
         for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
             crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
         }
 
         insert_prefix(options, buffer, offset, max, depth);
         buffer_print(*buffer, *max, *offset, "</%s>", name);
 
         if (options & xml_log_option_formatted) {
             buffer_print(*buffer, *max, *offset, "\n");
         }
     }
 }
 
 static void
 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
 {
     CRM_ASSERT(max != NULL);
     CRM_ASSERT(offset != NULL);
     CRM_ASSERT(buffer != NULL);
 
     if (data == NULL) {
         crm_trace("Nothing to dump");
         return;
     }
 
     if (*buffer == NULL) {
         *offset = 0;
         *max = 0;
     }
 
     insert_prefix(options, buffer, offset, max, depth);
 
     buffer_print(*buffer, *max, *offset, "%s", data->content);
 
     if (options & xml_log_option_formatted) {
         buffer_print(*buffer, *max, *offset, "\n");
     }
 }
 
 
 static void
 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
 {
     CRM_ASSERT(max != NULL);
     CRM_ASSERT(offset != NULL);
     CRM_ASSERT(buffer != NULL);
 
     if (data == NULL) {
         crm_trace("Nothing to dump");
         return;
     }
 
     if (*buffer == NULL) {
         *offset = 0;
         *max = 0;
     }
 
     insert_prefix(options, buffer, offset, max, depth);
 
     buffer_print(*buffer, *max, *offset, "<!--");
     buffer_print(*buffer, *max, *offset, "%s", data->content);
     buffer_print(*buffer, *max, *offset, "-->");
 
     if (options & xml_log_option_formatted) {
         buffer_print(*buffer, *max, *offset, "\n");
     }
 }
 
 void
 crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
 {
     if(data == NULL) {
         *offset = 0;
         *max = 0;
         return;
     }
 #if 0
     if (is_not_set(options, xml_log_option_filtered)) {
         /* Turning this code on also changes the PE tests for some reason
          * (not just newlines).  Figure out why before considering to
          * enable this permanently.
          *
          * It exists to help debug slowness in xmlNodeDump() and
          * potentially if we ever want to go back to it.
          *
          * In theory it's a good idea (reuse) but our custom version does
          * better for the filtered case and avoids the final strdup() for
          * everything
          */
 
         time_t now, next;
         xmlDoc *doc = NULL;
         xmlBuffer *xml_buffer = NULL;
 
         *buffer = NULL;
         doc = getDocPtr(data);
         /* doc will only be NULL if data is */
         CRM_CHECK(doc != NULL, return);
 
         now = time(NULL);
         xml_buffer = xmlBufferCreate();
         CRM_ASSERT(xml_buffer != NULL);
 
         /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
          * realloc()s and it can take upwards of 18 seconds (yes, seconds)
          * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
          * less than 1 second.
          *
          * We could also use xmlBufferCreateSize() to start with a
          * sane-ish initial size and avoid the first few doubles.
          */
         xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
 
         *max = xmlNodeDump(xml_buffer, doc, data, 0, (options & xml_log_option_formatted));
         if (*max > 0) {
             *buffer = strdup((char *)xml_buffer->content);
         }
 
         next = time(NULL);
         if ((now + 1) < next) {
             crm_log_xml_trace(data, "Long time");
             crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
         }
 
         xmlBufferFree(xml_buffer);
         return;
     }
 #endif
 
     switch(data->type) {
         case XML_ELEMENT_NODE:
             /* Handle below */
             dump_xml_element(data, options, buffer, offset, max, depth);
             break;
         case XML_TEXT_NODE:
             /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
             if (options & xml_log_option_text) {
                 dump_xml_text(data, options, buffer, offset, max, depth);
             }
             return;
         case XML_COMMENT_NODE:
             dump_xml_comment(data, options, buffer, offset, max, depth);
             break;
         default:
             crm_warn("Unhandled type: %d", data->type);
             return;
 
             /*
             XML_ATTRIBUTE_NODE = 2
             XML_CDATA_SECTION_NODE = 4
             XML_ENTITY_REF_NODE = 5
             XML_ENTITY_NODE = 6
             XML_PI_NODE = 7
             XML_DOCUMENT_NODE = 9
             XML_DOCUMENT_TYPE_NODE = 10
             XML_DOCUMENT_FRAG_NODE = 11
             XML_NOTATION_NODE = 12
             XML_HTML_DOCUMENT_NODE = 13
             XML_DTD_NODE = 14
             XML_ELEMENT_DECL = 15
             XML_ATTRIBUTE_DECL = 16
             XML_ENTITY_DECL = 17
             XML_NAMESPACE_DECL = 18
             XML_XINCLUDE_START = 19
             XML_XINCLUDE_END = 20
             XML_DOCB_DOCUMENT_NODE = 21
             */
     }
 
 }
 
 void
 crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
 {
     buffer_print(*buffer, *max, *offset, "%c", c);
 }
 
 char *
 dump_xml_formatted_with_text(xmlNode * an_xml_node)
 {
     char *buffer = NULL;
     int offset = 0, max = 0;
 
     crm_xml_dump(an_xml_node, xml_log_option_formatted|xml_log_option_text, &buffer, &offset, &max, 0);
     return buffer;
 }
 
 char *
 dump_xml_formatted(xmlNode * an_xml_node)
 {
     char *buffer = NULL;
     int offset = 0, max = 0;
 
     crm_xml_dump(an_xml_node, xml_log_option_formatted, &buffer, &offset, &max, 0);
     return buffer;
 }
 
 char *
 dump_xml_unformatted(xmlNode * an_xml_node)
 {
     char *buffer = NULL;
     int offset = 0, max = 0;
 
     crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
     return buffer;
 }
 
 gboolean
 xml_has_children(const xmlNode * xml_root)
 {
     if (xml_root != NULL && xml_root->children != NULL) {
         return TRUE;
     }
     return FALSE;
 }
 
 int
-crm_element_value_int(xmlNode * data, const char *name, int *dest)
+crm_element_value_int(const xmlNode *data, const char *name, int *dest)
 {
     const char *value = crm_element_value(data, name);
 
     CRM_CHECK(dest != NULL, return -1);
     if (value) {
         *dest = crm_int_helper(value, NULL);
         return 0;
     }
     return -1;
 }
 
-int
-crm_element_value_const_int(const xmlNode * data, const char *name, int *dest)
-{
-    return crm_element_value_int((xmlNode *) data, name, dest);
-}
-
-const char *
-crm_element_value_const(const xmlNode * data, const char *name)
-{
-    return crm_element_value((xmlNode *) data, name);
-}
-
 char *
-crm_element_value_copy(xmlNode * data, const char *name)
+crm_element_value_copy(const xmlNode *data, const char *name)
 {
     char *value_copy = NULL;
     const char *value = crm_element_value(data, name);
 
     if (value != NULL) {
         value_copy = strdup(value);
     }
     return value_copy;
 }
 
 void
 xml_remove_prop(xmlNode * obj, const char *name)
 {
     if(__xml_acl_check(obj, NULL, xpf_acl_write) == FALSE) {
         crm_trace("Cannot remove %s from %s", name, obj->name);
 
     } else if(TRACKING_CHANGES(obj)) {
         /* Leave in place (marked for removal) until after the diff is calculated */
         xml_private_t *p = NULL;
         xmlAttr *attr = xmlHasProp(obj, (const xmlChar *)name);
 
         p = attr->_private;
         set_parent_flag(obj, xpf_dirty);
         p->flags |= xpf_deleted;
         /* crm_trace("Setting flag %x due to %s[@id=%s].%s", xpf_dirty, obj->name, ID(obj), name); */
 
     } else {
         xmlUnsetProp(obj, (const xmlChar *)name);
     }
 }
 
 void
 purge_diff_markers(xmlNode * a_node)
 {
     xmlNode *child = NULL;
 
     CRM_CHECK(a_node != NULL, return);
 
     xml_remove_prop(a_node, XML_DIFF_MARKER);
     for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
         purge_diff_markers(child);
     }
 }
 
 void
 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
 {
     char *f = NULL;
 
     if (filename == NULL) {
         char *uuid = crm_generate_uuid();
 
         f = crm_strdup_printf("%s/%s", crm_get_tmpdir(), uuid);
         filename = f;
         free(uuid);
     }
 
     crm_info("Saving %s to %s", desc, filename);
     write_xml_file(xml, filename, FALSE);
     free(f);
 }
 
 gboolean
 apply_xml_diff(xmlNode *old_xml, xmlNode * diff, xmlNode **new_xml)
 {
     gboolean result = TRUE;
     int root_nodes_seen = 0;
     static struct qb_log_callsite *digest_cs = NULL;
     const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
     const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
 
     xmlNode *child_diff = NULL;
     xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
     xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
 
     CRM_CHECK(new_xml != NULL, return FALSE);
     if (digest_cs == NULL) {
         digest_cs =
             qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
                                 crm_trace_nonlog);
     }
 
     crm_trace("Subtraction Phase");
     for (child_diff = __xml_first_child(removed); child_diff != NULL;
          child_diff = __xml_next(child_diff)) {
         CRM_CHECK(root_nodes_seen == 0, result = FALSE);
         if (root_nodes_seen == 0) {
             *new_xml = subtract_xml_object(NULL, old_xml, child_diff, FALSE, NULL, NULL);
         }
         root_nodes_seen++;
     }
 
     if (root_nodes_seen == 0) {
         *new_xml = copy_xml(old_xml);
 
     } else if (root_nodes_seen > 1) {
         crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
         result = FALSE;
     }
 
     root_nodes_seen = 0;
     crm_trace("Addition Phase");
     if (result) {
         xmlNode *child_diff = NULL;
 
         for (child_diff = __xml_first_child(added); child_diff != NULL;
              child_diff = __xml_next(child_diff)) {
             CRM_CHECK(root_nodes_seen == 0, result = FALSE);
             if (root_nodes_seen == 0) {
                 add_xml_object(NULL, *new_xml, child_diff, TRUE);
             }
             root_nodes_seen++;
         }
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
         result = FALSE;
 
     } else if (result && digest) {
         char *new_digest = NULL;
 
         purge_diff_markers(*new_xml);       /* Purge now so the diff is ok */
         new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE, version);
         if (safe_str_neq(new_digest, digest)) {
             crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest);
             result = FALSE;
 
             crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
             if (digest_cs && digest_cs->targets) {
                 save_xml_to_file(old_xml, "diff:original", NULL);
                 save_xml_to_file(diff, "diff:input", NULL);
                 save_xml_to_file(*new_xml, "diff:new", NULL);
             }
 
         } else {
             crm_trace("Digest matched: expected %s, calculated %s", digest, new_digest);
         }
         free(new_digest);
 
     } else if (result) {
         purge_diff_markers(*new_xml);       /* Purge now so the diff is ok */
     }
 
     return result;
 }
 
 static void
 __xml_diff_object(xmlNode * old, xmlNode * new)
 {
     xmlNode *cIter = NULL;
     xmlAttr *pIter = NULL;
 
     CRM_CHECK(new != NULL, return);
     if(old == NULL) {
         crm_node_created(new);
         __xml_acl_post_process(new); /* Check creation is allowed */
         return;
 
     } else {
         xml_private_t *p = new->_private;
 
         if(p->flags & xpf_processed) {
             /* Avoid re-comparing nodes */
             return;
         }
         p->flags |= xpf_processed;
     }
 
     for (pIter = crm_first_attr(new); pIter != NULL; pIter = pIter->next) {
         xml_private_t *p = pIter->_private;
 
         /* Assume everything was just created and take it from there */
         p->flags |= xpf_created;
     }
 
     for (pIter = crm_first_attr(old); pIter != NULL; ) {
         xmlAttr *prop = pIter;
         xml_private_t *p = NULL;
         const char *name = (const char *)pIter->name;
         const char *old_value = crm_element_value(old, name);
         xmlAttr *exists = xmlHasProp(new, pIter->name);
 
         pIter = pIter->next;
         if(exists == NULL) {
             p = new->doc->_private;
 
             /* Prevent the dirty flag being set recursively upwards */
             clear_bit(p->flags, xpf_tracking);
             exists = xmlSetProp(new, (const xmlChar *)name, (const xmlChar *)old_value);
             set_bit(p->flags, xpf_tracking);
 
             p = exists->_private;
             p->flags = 0;
 
             crm_trace("Lost %s@%s=%s", old->name, name, old_value);
             xml_remove_prop(new, name);
 
         } else {
             int p_new = __xml_offset((xmlNode*)exists);
             int p_old = __xml_offset((xmlNode*)prop);
             const char *value = crm_element_value(new, name);
 
             p = exists->_private;
             p->flags = (p->flags & ~xpf_created);
 
             if(strcmp(value, old_value) != 0) {
                 /* Restore the original value, so we can call crm_xml_add(),
                  * which checks ACLs
                  */
                 char *vcopy = crm_element_value_copy(new, name);
 
                 crm_trace("Modified %s@%s %s->%s", old->name, name, old_value, vcopy);
                 xmlSetProp(new, prop->name, (const xmlChar *)old_value);
                 crm_xml_add(new, name, vcopy);
                 free(vcopy);
 
             } else if(p_old != p_new) {
                 crm_info("Moved %s@%s (%d -> %d)", old->name, name, p_old, p_new);
                 __xml_node_dirty(new);
                 p->flags |= xpf_dirty|xpf_moved;
 
                 if(p_old > p_new) {
                     p = prop->_private;
                     p->flags |= xpf_skip;
 
                 } else {
                     p = exists->_private;
                     p->flags |= xpf_skip;
                 }
             }
         }
     }
 
     for (pIter = crm_first_attr(new); pIter != NULL; ) {
         xmlAttr *prop = pIter;
         xml_private_t *p = pIter->_private;
 
         pIter = pIter->next;
         if(is_set(p->flags, xpf_created)) {
             char *name = strdup((const char *)prop->name);
             char *value = crm_element_value_copy(new, name);
 
             crm_trace("Created %s@%s=%s", new->name, name, value);
             /* Remove plus create won't work as it will modify the relative attribute ordering */
             if(__xml_acl_check(new, name, xpf_acl_write)) {
                 crm_attr_dirty(prop);
             } else {
                 xmlUnsetProp(new, prop->name); /* Remove - change not allowed */
             }
 
             free(value);
             free(name);
         }
     }
 
     for (cIter = __xml_first_child(old); cIter != NULL; ) {
         xmlNode *old_child = cIter;
         xmlNode *new_child = find_element(new, cIter, TRUE);
 
         cIter = __xml_next(cIter);
         if(new_child) {
             __xml_diff_object(old_child, new_child);
 
         } else {
             /* Create then free (which will check the acls if necessary) */
             xmlNode *candidate = add_node_copy(new, old_child);
             xmlNode *top = xmlDocGetRootElement(candidate->doc);
 
             __xml_node_clean(candidate);
             __xml_acl_apply(top); /* Make sure any ACLs are applied to 'candidate' */
             /* Record the old position */
             free_xml_with_position(candidate, __xml_offset(old_child));
 
             if (find_element(new, old_child, TRUE) == NULL) {
                 xml_private_t *p = old_child->_private;
 
                 p->flags |= xpf_skip;
             }
         }
     }
 
     for (cIter = __xml_first_child(new); cIter != NULL; ) {
         xmlNode *new_child = cIter;
         xmlNode *old_child = find_element(old, cIter, TRUE);
 
         cIter = __xml_next(cIter);
         if(old_child == NULL) {
             xml_private_t *p = new_child->_private;
             p->flags |= xpf_skip;
             __xml_diff_object(old_child, new_child);
 
         } else {
             /* Check for movement, we already checked for differences */
             int p_new = __xml_offset(new_child);
             int p_old = __xml_offset(old_child);
 
             if(p_old != p_new) {
                 xml_private_t *p = new_child->_private;
 
                 crm_info("%s.%s moved from %d to %d",
                          new_child->name, ID(new_child), p_old, p_new);
                 __xml_node_dirty(new);
                 p->flags |= xpf_moved;
 
                 if(p_old > p_new) {
                     p = old_child->_private;
                 } else {
                     p = new_child->_private;
                 }
                 p->flags |= xpf_skip;
             }
         }
     }
 }
 
 void
 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
 {
     CRM_CHECK(safe_str_eq(crm_element_name(old_xml),
                           crm_element_name(new_xml)),
               return);
     CRM_CHECK(safe_str_eq(ID(old_xml), ID(new_xml)), return);
 
     if(xml_tracking_changes(new_xml) == FALSE) {
         xml_track_changes(new_xml, NULL, NULL, FALSE);
     }
 
     __xml_diff_object(old_xml, new_xml);
 }
 
 xmlNode *
 diff_xml_object(xmlNode * old, xmlNode * new, gboolean suppress)
 {
     xmlNode *tmp1 = NULL;
     xmlNode *diff = create_xml_node(NULL, "diff");
     xmlNode *removed = create_xml_node(diff, "diff-removed");
     xmlNode *added = create_xml_node(diff, "diff-added");
 
     crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 
     tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top");
     if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
         free_xml(tmp1);
     }
 
     tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top");
     if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
         free_xml(tmp1);
     }
 
     if (added->children == NULL && removed->children == NULL) {
         free_xml(diff);
         diff = NULL;
     }
 
     return diff;
 }
 
 gboolean
 can_prune_leaf(xmlNode * xml_node)
 {
     xmlNode *cIter = NULL;
     xmlAttrPtr pIter = NULL;
     gboolean can_prune = TRUE;
     const char *name = crm_element_name(xml_node);
 
     if (safe_str_eq(name, XML_TAG_RESOURCE_REF)
         || safe_str_eq(name, XML_CIB_TAG_OBJ_REF)
         || safe_str_eq(name, XML_ACL_TAG_ROLE_REF)
         || safe_str_eq(name, XML_ACL_TAG_ROLE_REFv1)) {
         return FALSE;
     }
 
     for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
         const char *p_name = (const char *)pIter->name;
 
         if (strcmp(p_name, XML_ATTR_ID) == 0) {
             continue;
         }
         can_prune = FALSE;
     }
 
     cIter = __xml_first_child(xml_node);
     while (cIter) {
         xmlNode *child = cIter;
 
         cIter = __xml_next(cIter);
         if (can_prune_leaf(child)) {
             free_xml(child);
         } else {
             can_prune = FALSE;
         }
     }
     return can_prune;
 }
 
 void
 diff_filter_context(int context, int upper_bound, int lower_bound,
                     xmlNode * xml_node, xmlNode * parent)
 {
     xmlNode *us = NULL;
     xmlNode *child = NULL;
     xmlAttrPtr pIter = NULL;
     xmlNode *new_parent = parent;
     const char *name = crm_element_name(xml_node);
 
     CRM_CHECK(xml_node != NULL && name != NULL, return);
 
     us = create_xml_node(parent, name);
     for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
         const char *p_name = (const char *)pIter->name;
         const char *p_value = crm_attr_value(pIter);
 
         lower_bound = context;
         crm_xml_add(us, p_name, p_value);
     }
 
     if (lower_bound >= 0 || upper_bound >= 0) {
         crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
         new_parent = us;
 
     } else {
         upper_bound = in_upper_context(0, context, xml_node);
         if (upper_bound >= 0) {
             crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
             new_parent = us;
         } else {
             free_xml(us);
             us = NULL;
         }
     }
 
     for (child = __xml_first_child(us); child != NULL; child = __xml_next(child)) {
         diff_filter_context(context, upper_bound - 1, lower_bound - 1, child, new_parent);
     }
 }
 
 int
 in_upper_context(int depth, int context, xmlNode * xml_node)
 {
     if (context == 0) {
         return 0;
     }
 
     if (xml_node->properties) {
         return depth;
 
     } else if (depth < context) {
         xmlNode *child = NULL;
 
         for (child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
             if (in_upper_context(depth + 1, context, child)) {
                 return depth;
             }
         }
     }
     return 0;
 }
 
 static xmlNode *
 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
 {
     xmlNode *a_child = NULL;
     int search_offset = __xml_offset(search_comment);
 
     CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
 
     for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
         if (exact) {
             int offset = __xml_offset(a_child);
             xml_private_t *p = a_child->_private;
 
             if (offset < search_offset) {
                 continue;
 
             } else if (offset > search_offset) {
                 return NULL;
             }
 
             if (is_set(p->flags, xpf_skip)) {
                 continue;
             }
         }
 
         if (a_child->type == XML_COMMENT_NODE
             && safe_str_eq((const char *)a_child->content, (const char *)search_comment->content)) {
             return a_child;
 
         } else if (exact) {
             return NULL;
         }
     }
 
     return NULL;
 }
 
 static xmlNode *
 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
                      gboolean * changed)
 {
     CRM_CHECK(left != NULL, return NULL);
     CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
 
     if (right == NULL
         || safe_str_neq((const char *)left->content, (const char *)right->content)) {
         xmlNode *deleted = NULL;
 
         deleted = add_node_copy(parent, left);
         *changed = TRUE;
 
         return deleted;
     }
 
     return NULL;
 }
 
 xmlNode *
 subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
                     gboolean full, gboolean * changed, const char *marker)
 {
     gboolean dummy = FALSE;
     gboolean skip = FALSE;
     xmlNode *diff = NULL;
     xmlNode *right_child = NULL;
     xmlNode *left_child = NULL;
     xmlAttrPtr xIter = NULL;
 
     const char *id = NULL;
     const char *name = NULL;
     const char *value = NULL;
     const char *right_val = NULL;
 
     int lpc = 0;
     static int filter_len = DIMOF(filter);
 
     if (changed == NULL) {
         changed = &dummy;
     }
 
     if (left == NULL) {
         return NULL;
     }
 
     if (left->type == XML_COMMENT_NODE) {
         return subtract_xml_comment(parent, left, right, changed);
     }
 
     id = ID(left);
     if (right == NULL) {
         xmlNode *deleted = NULL;
 
         crm_trace("Processing <%s id=%s> (complete copy)", crm_element_name(left), id);
         deleted = add_node_copy(parent, left);
         crm_xml_add(deleted, XML_DIFF_MARKER, marker);
 
         *changed = TRUE;
         return deleted;
     }
 
     name = crm_element_name(left);
     CRM_CHECK(name != NULL, return NULL);
     CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL);
 
     /* check for XML_DIFF_MARKER in a child */
     value = crm_element_value(right, XML_DIFF_MARKER);
     if (value != NULL && strcmp(value, "removed:top") == 0) {
         crm_trace("We are the root of the deletion: %s.id=%s", name, id);
         *changed = TRUE;
         return NULL;
     }
 
     /* Avoiding creating the full heirarchy would save even more work here */
     diff = create_xml_node(parent, name);
 
     /* Reset filter */
     for (lpc = 0; lpc < filter_len; lpc++) {
         filter[lpc].found = FALSE;
     }
 
     /* changes to child objects */
     for (left_child = __xml_first_child(left); left_child != NULL;
          left_child = __xml_next(left_child)) {
         gboolean child_changed = FALSE;
 
         right_child = find_element(right, left_child, FALSE);
         subtract_xml_object(diff, left_child, right_child, full, &child_changed, marker);
         if (child_changed) {
             *changed = TRUE;
         }
     }
 
     if (*changed == FALSE) {
         /* Nothing to do */
 
     } else if (full) {
         xmlAttrPtr pIter = NULL;
 
         for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
             const char *p_name = (const char *)pIter->name;
             const char *p_value = crm_attr_value(pIter);
 
             xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
         }
 
         /* We already have everything we need... */
         goto done;
 
     } else if (id) {
         xmlSetProp(diff, (const xmlChar *)XML_ATTR_ID, (const xmlChar *)id);
     }
 
     /* changes to name/value pairs */
     for (xIter = crm_first_attr(left); xIter != NULL; xIter = xIter->next) {
         const char *prop_name = (const char *)xIter->name;
         xmlAttrPtr right_attr = NULL;
         xml_private_t *p = NULL;
 
         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
             continue;
         }
 
         skip = FALSE;
         for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
             if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].string) == 0) {
                 filter[lpc].found = TRUE;
                 skip = TRUE;
                 break;
             }
         }
 
         if (skip) {
             continue;
         }
 
         right_attr = xmlHasProp(right, (const xmlChar *)prop_name);
         if (right_attr) {
             p = right_attr->_private;
         }
 
         right_val = crm_element_value(right, prop_name);
         if (right_val == NULL || (p && is_set(p->flags, xpf_deleted))) {
             /* new */
             *changed = TRUE;
             if (full) {
                 xmlAttrPtr pIter = NULL;
 
                 for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
                     const char *p_name = (const char *)pIter->name;
                     const char *p_value = crm_attr_value(pIter);
 
                     xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
                 }
                 break;
 
             } else {
                 const char *left_value = crm_element_value(left, prop_name);
 
                 xmlSetProp(diff, (const xmlChar *)prop_name, (const xmlChar *)value);
                 crm_xml_add(diff, prop_name, left_value);
             }
 
         } else {
             /* Only now do we need the left value */
             const char *left_value = crm_element_value(left, prop_name);
 
             if (strcmp(left_value, right_val) == 0) {
                 /* unchanged */
 
             } else {
                 *changed = TRUE;
                 if (full) {
                     xmlAttrPtr pIter = NULL;
 
                     crm_trace("Changes detected to %s in <%s id=%s>", prop_name,
                               crm_element_name(left), id);
                     for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
                         const char *p_name = (const char *)pIter->name;
                         const char *p_value = crm_attr_value(pIter);
 
                         xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
                     }
                     break;
 
                 } else {
                     crm_trace("Changes detected to %s (%s -> %s) in <%s id=%s>",
                               prop_name, left_value, right_val, crm_element_name(left), id);
                     crm_xml_add(diff, prop_name, left_value);
                 }
             }
         }
     }
 
     if (*changed == FALSE) {
         free_xml(diff);
         return NULL;
 
     } else if (full == FALSE && id) {
         crm_xml_add(diff, XML_ATTR_ID, id);
     }
   done:
     return diff;
 }
 
 static int
 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
 {
     CRM_CHECK(update != NULL, return 0);
     CRM_CHECK(update->type == XML_COMMENT_NODE, return 0);
 
     if (target == NULL) {
         target = find_xml_comment(parent, update, FALSE);
     }
 
     if (target == NULL) {
         add_node_copy(parent, update);
 
     /* We won't reach here currently */
     } else if (safe_str_neq((const char *)target->content, (const char *)update->content)) {
         xmlFree(target->content);
         target->content = xmlStrdup(update->content);
     }
 
     return 0;
 }
 
 int
 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
 {
     xmlNode *a_child = NULL;
     const char *object_id = NULL;
     const char *object_name = NULL;
 
 #if XML_PARSE_DEBUG
     crm_log_xml_trace("update:", update);
     crm_log_xml_trace("target:", target);
 #endif
 
     CRM_CHECK(update != NULL, return 0);
 
     if (update->type == XML_COMMENT_NODE) {
         return add_xml_comment(parent, target, update);
     }
 
     object_name = crm_element_name(update);
     object_id = ID(update);
 
     CRM_CHECK(object_name != NULL, return 0);
 
     if (target == NULL && object_id == NULL) {
         /*  placeholder object */
         target = find_xml_node(parent, object_name, FALSE);
 
     } else if (target == NULL) {
         target = find_entity(parent, object_name, object_id);
     }
 
     if (target == NULL) {
         target = create_xml_node(parent, object_name);
         CRM_CHECK(target != NULL, return 0);
 #if XML_PARSER_DEBUG
         crm_trace("Added  <%s%s%s/>", crm_str(object_name),
                   object_id ? " id=" : "", object_id ? object_id : "");
 
     } else {
         crm_trace("Found node <%s%s%s/> to update",
                   crm_str(object_name), object_id ? " id=" : "", object_id ? object_id : "");
 #endif
     }
 
     CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(update)), return 0);
 
     if (as_diff == FALSE) {
         /* So that expand_plus_plus() gets called */
         copy_in_properties(target, update);
 
     } else {
         /* No need for expand_plus_plus(), just raw speed */
         xmlAttrPtr pIter = NULL;
 
         for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
             const char *p_name = (const char *)pIter->name;
             const char *p_value = crm_attr_value(pIter);
 
             /* Remove it first so the ordering of the update is preserved */
             xmlUnsetProp(target, (const xmlChar *)p_name);
             xmlSetProp(target, (const xmlChar *)p_name, (const xmlChar *)p_value);
         }
     }
 
     for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
 #if XML_PARSER_DEBUG
         crm_trace("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child));
 #endif
         add_xml_object(target, NULL, a_child, as_diff);
     }
 
 #if XML_PARSER_DEBUG
     crm_trace("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id));
 #endif
     return 0;
 }
 
 gboolean
 update_xml_child(xmlNode * child, xmlNode * to_update)
 {
     gboolean can_update = TRUE;
     xmlNode *child_of_child = NULL;
 
     CRM_CHECK(child != NULL, return FALSE);
     CRM_CHECK(to_update != NULL, return FALSE);
 
     if (safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
         can_update = FALSE;
 
     } else if (safe_str_neq(ID(to_update), ID(child))) {
         can_update = FALSE;
 
     } else if (can_update) {
 #if XML_PARSER_DEBUG
         crm_log_xml_trace(child, "Update match found...");
 #endif
         add_xml_object(NULL, child, to_update, FALSE);
     }
 
     for (child_of_child = __xml_first_child(child); child_of_child != NULL;
          child_of_child = __xml_next(child_of_child)) {
         /* only update the first one */
         if (can_update) {
             break;
         }
         can_update = update_xml_child(child_of_child, to_update);
     }
 
     return can_update;
 }
 
 int
 find_xml_children(xmlNode ** children, xmlNode * root,
                   const char *tag, const char *field, const char *value, gboolean search_matches)
 {
     int match_found = 0;
 
     CRM_CHECK(root != NULL, return FALSE);
     CRM_CHECK(children != NULL, return FALSE);
 
     if (tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
 
     } else if (value != NULL && safe_str_neq(value, crm_element_value(root, field))) {
 
     } else {
         if (*children == NULL) {
             *children = create_xml_node(NULL, __FUNCTION__);
         }
         add_node_copy(*children, root);
         match_found = 1;
     }
 
     if (search_matches || match_found == 0) {
         xmlNode *child = NULL;
 
         for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
             match_found += find_xml_children(children, child, tag, field, value, search_matches);
         }
     }
 
     return match_found;
 }
 
 gboolean
 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
 {
     gboolean can_delete = FALSE;
     xmlNode *child_of_child = NULL;
 
     const char *up_id = NULL;
     const char *child_id = NULL;
     const char *right_val = NULL;
 
     CRM_CHECK(child != NULL, return FALSE);
     CRM_CHECK(update != NULL, return FALSE);
 
     up_id = ID(update);
     child_id = ID(child);
 
     if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
         can_delete = TRUE;
     }
     if (safe_str_neq(crm_element_name(update), crm_element_name(child))) {
         can_delete = FALSE;
     }
     if (can_delete && delete_only) {
         xmlAttrPtr pIter = NULL;
 
         for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
             const char *p_name = (const char *)pIter->name;
             const char *p_value = crm_attr_value(pIter);
 
             right_val = crm_element_value(child, p_name);
             if (safe_str_neq(p_value, right_val)) {
                 can_delete = FALSE;
             }
         }
     }
 
     if (can_delete && parent != NULL) {
         crm_log_xml_trace(child, "Delete match found...");
         if (delete_only || update == NULL) {
             free_xml(child);
 
         } else {
             xmlNode *tmp = copy_xml(update);
             xmlDoc *doc = tmp->doc;
             xmlNode *old = NULL;
 
             xml_accept_changes(tmp);
             old = xmlReplaceNode(child, tmp);
 
             if(xml_tracking_changes(tmp)) {
                 /* Replaced sections may have included relevant ACLs */
                 __xml_acl_apply(tmp);
             }
 
             xml_calculate_changes(old, tmp);
             xmlDocSetRootElement(doc, old);
             free_xml(old);
         }
         child = NULL;
         return TRUE;
 
     } else if (can_delete) {
         crm_log_xml_debug(child, "Cannot delete the search root");
         can_delete = FALSE;
     }
 
     child_of_child = __xml_first_child(child);
     while (child_of_child) {
         xmlNode *next = __xml_next(child_of_child);
 
         can_delete = replace_xml_child(child, child_of_child, update, delete_only);
 
         /* only delete the first one */
         if (can_delete) {
             child_of_child = NULL;
         } else {
             child_of_child = next;
         }
     }
 
     return can_delete;
 }
 
 /*!
  * \brief Create an XML name/value pair
  *
  * \param[in] parent  If not NULL, make new XML node a child of this one
  * \param[in] id      If not NULL, use this as ID (otherwise auto-generate)
  * \param[in] name    Name to use
  * \param[in] value   Value to use
  *
  * \return New XML object on success, NULL otherwise
  */
 xmlNode *
 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
                       const char *value)
 {
     xmlNode *nvp;
 
     /* id can be NULL so we auto-generate one, and name can be NULL if this
      * will be used to delete a name/value pair by ID, but both can't be NULL
      */
     CRM_CHECK(id || name, return NULL);
 
     nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
     CRM_CHECK(nvp, return NULL);
 
     if (id) {
         crm_xml_add(nvp, XML_ATTR_ID, id);
     } else {
         const char *parent_id = ID(parent);
 
         crm_xml_set_id(nvp, "%s-%s",
                        (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
     }
     crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
     crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
     return nvp;
 }
 
 void
 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
 {
     const char *name = key;
     const char *s_value = value;
     xmlNode *xml_node = user_data;
 
     crm_create_nvpair_xml(xml_node, name, name, s_value);
     crm_trace("dumped: name=%s value=%s", name, s_value);
 }
 
 void
 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
 {
     const char *name = key;
     const char *s_value = value;
 
     xmlNode *xml_node = user_data;
 
     if (isdigit(name[0])) {
         xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
 
         crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
         crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
 
     } else if (crm_element_value(xml_node, name) == NULL) {
         crm_xml_add(xml_node, name, s_value);
         crm_trace("dumped: %s=%s", name, s_value);
 
     } else {
         crm_trace("duplicate: %s=%s", name, s_value);
     }
 }
 
 void
 hash2field(gpointer key, gpointer value, gpointer user_data)
 {
     const char *name = key;
     const char *s_value = value;
 
     xmlNode *xml_node = user_data;
 
     if (crm_element_value(xml_node, name) == NULL) {
         crm_xml_add(xml_node, name, s_value);
 
     } else {
         crm_trace("duplicate: %s=%s", name, s_value);
     }
 }
 
 void
 hash2metafield(gpointer key, gpointer value, gpointer user_data)
 {
     char *crm_name = NULL;
 
     if (key == NULL || value == NULL) {
         return;
     }
 
     /* Filter out cluster-generated attributes that contain a '#' or ':'
      * (like fail-count and last-failure).
      */
     for (crm_name = key; *crm_name; ++crm_name) {
         if ((*crm_name == '#') || (*crm_name == ':')) {
             return;
         }
     }
 
     crm_name = crm_meta_name(key);
     hash2field(crm_name, value, user_data);
     free(crm_name);
 }
 
 GHashTable *
 xml2list(xmlNode * parent)
 {
     xmlNode *child = NULL;
     xmlAttrPtr pIter = NULL;
     xmlNode *nvpair_list = NULL;
     GHashTable *nvpair_hash = crm_str_table_new();
 
     CRM_CHECK(parent != NULL, return nvpair_hash);
 
     nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
     if (nvpair_list == NULL) {
         crm_trace("No attributes in %s", crm_element_name(parent));
         crm_log_xml_trace(parent, "No attributes for resource op");
     }
 
     crm_log_xml_trace(nvpair_list, "Unpacking");
 
     for (pIter = crm_first_attr(nvpair_list); pIter != NULL; pIter = pIter->next) {
         const char *p_name = (const char *)pIter->name;
         const char *p_value = crm_attr_value(pIter);
 
         crm_trace("Added %s=%s", p_name, p_value);
 
         g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
     }
 
     for (child = __xml_first_child(nvpair_list); child != NULL; child = __xml_next(child)) {
         if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
             const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
             const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
 
             crm_trace("Added %s=%s", key, value);
             if (key != NULL && value != NULL) {
                 g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
             }
         }
     }
 
     return nvpair_hash;
 }
 
 typedef struct name_value_s {
     const char *name;
     const void *value;
 } name_value_t;
 
 static gint
 sort_pairs(gconstpointer a, gconstpointer b)
 {
     int rc = 0;
     const name_value_t *pair_a = a;
     const name_value_t *pair_b = b;
 
     CRM_ASSERT(a != NULL);
     CRM_ASSERT(pair_a->name != NULL);
 
     CRM_ASSERT(b != NULL);
     CRM_ASSERT(pair_b->name != NULL);
 
     rc = strcmp(pair_a->name, pair_b->name);
     if (rc < 0) {
         return -1;
     } else if (rc > 0) {
         return 1;
     }
     return 0;
 }
 
 static void
 dump_pair(gpointer data, gpointer user_data)
 {
     name_value_t *pair = data;
     xmlNode *parent = user_data;
 
     crm_xml_add(parent, pair->name, pair->value);
 }
 
 xmlNode *
 sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive)
 {
     xmlNode *child = NULL;
     GListPtr sorted = NULL;
     GListPtr unsorted = NULL;
     name_value_t *pair = NULL;
     xmlNode *result = NULL;
     const char *name = NULL;
     xmlAttrPtr pIter = NULL;
 
     CRM_CHECK(input != NULL, return NULL);
 
     name = crm_element_name(input);
     CRM_CHECK(name != NULL, return NULL);
 
     result = create_xml_node(parent, name);
 
     for (pIter = crm_first_attr(input); pIter != NULL; pIter = pIter->next) {
         const char *p_name = (const char *)pIter->name;
         const char *p_value = crm_attr_value(pIter);
 
         pair = calloc(1, sizeof(name_value_t));
         pair->name = p_name;
         pair->value = p_value;
         unsorted = g_list_prepend(unsorted, pair);
         pair = NULL;
     }
 
     sorted = g_list_sort(unsorted, sort_pairs);
     g_list_foreach(sorted, dump_pair, result);
     g_list_free_full(sorted, free);
 
     for (child = __xml_first_child(input); child != NULL; child = __xml_next(child)) {
         if (recursive) {
             sorted_xml(child, result, recursive);
         } else {
             add_node_copy(result, child);
         }
     }
 
     return result;
 }
 
 xmlNode *
-first_named_child(xmlNode * parent, const char *name)
+first_named_child(const xmlNode *parent, const char *name)
 {
     xmlNode *match = NULL;
 
     for (match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
         /*
          * name == NULL gives first child regardless of name; this is
          * semantically incorrect in this function, but may be necessary
          * due to prior use of xml_child_iter_filter
          */
         if (name == NULL || strcmp((const char *)match->name, name) == 0) {
             return match;
         }
     }
     return NULL;
 }
 
 /*!
  * \brief Get next instance of same XML tag
  *
  * \param[in] sibling  XML tag to start from
  *
  * \return Next sibling XML tag with same name
  */
 xmlNode *
-crm_next_same_xml(xmlNode *sibling)
+crm_next_same_xml(const xmlNode *sibling)
 {
     xmlNode *match = __xml_next(sibling);
     const char *name = crm_element_name(sibling);
 
     while (match != NULL) {
         if (!strcmp(crm_element_name(match), name)) {
             return match;
         }
         match = __xml_next(match);
     }
     return NULL;
 }
 
 void
 crm_xml_init(void)
 {
     static bool init = TRUE;
 
     if(init) {
         init = FALSE;
         /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
          * realloc_safe()s and it can take upwards of 18 seconds (yes, seconds)
          * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
          * less than 1 second.
          */
         xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
 
         /* Populate and free the _private field when nodes are created and destroyed */
         xmlDeregisterNodeDefault(pcmkDeregisterNode);
         xmlRegisterNodeDefault(pcmkRegisterNode);
 
         crm_schema_init();
     }
 }
 
 void
 crm_xml_cleanup(void)
 {
     crm_info("Cleaning up memory from libxml2");
     crm_schema_cleanup();
     xmlCleanupParser();
 }
 
 #define XPATH_MAX 512
 
 xmlNode *
 expand_idref(xmlNode * input, xmlNode * top)
 {
     const char *tag = NULL;
     const char *ref = NULL;
     xmlNode *result = input;
 
     if (result == NULL) {
         return NULL;
 
     } else if (top == NULL) {
         top = input;
     }
 
     tag = crm_element_name(result);
     ref = crm_element_value(result, XML_ATTR_IDREF);
 
     if (ref != NULL) {
         char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
 
         result = get_xpath_object(xpath_string, top, LOG_ERR);
         if (result == NULL) {
             char *nodePath = (char *)xmlGetNodePath(top);
 
             crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
                     crm_str(nodePath));
             free(nodePath);
         }
         free(xpath_string);
     }
     return result;
 }
 
 const char *
-crm_element_value(xmlNode * data, const char *name)
+crm_element_value(const xmlNode *data, const char *name)
 {
     xmlAttr *attr = NULL;
 
     if (data == NULL) {
         crm_err("Couldn't find %s in NULL", name ? name : "<null>");
         CRM_LOG_ASSERT(data != NULL);
         return NULL;
 
     } else if (name == NULL) {
         crm_err("Couldn't find NULL in %s", crm_element_name(data));
         return NULL;
     }
 
-    attr = xmlHasProp(data, (const xmlChar *)name);
+    /* The first argument to xmlHasProp() has always been const,
+     * but libxml2 <2.9.2 didn't declare that, so cast it
+     */
+    attr = xmlHasProp((xmlNode *) data, (const xmlChar *)name);
     if (attr == NULL || attr->children == NULL) {
         return NULL;
     }
     return (const char *)attr->children->content;
 }
 
 void
 crm_destroy_xml(gpointer data)
 {
     free_xml(data);
 }
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 3228325c9b..c6ecda2f25 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,2328 +1,2328 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 
 #include <glib.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/pengine/internal.h>
 
 #include <unpack.h>
 
 pe_working_set_t *pe_dataset = NULL;
 
 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
 void print_str_str(gpointer key, gpointer value, gpointer user_data);
 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
 void unpack_operation(action_t * action, xmlNode * xml_obj, resource_t * container,
                       pe_working_set_t * data_set);
 static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key,
                                          gboolean include_disabled);
 
 #if ENABLE_VERSIONED_ATTRS
 pe_rsc_action_details_t *
 pe_rsc_action_details(pe_action_t *action)
 {
     pe_rsc_action_details_t *details;
 
     CRM_CHECK(action != NULL, return NULL);
 
     if (action->action_details == NULL) {
         action->action_details = calloc(1, sizeof(pe_rsc_action_details_t));
         CRM_CHECK(action->action_details != NULL, return NULL);
     }
 
     details = (pe_rsc_action_details_t *) action->action_details;
     if (details->versioned_parameters == NULL) {
         details->versioned_parameters = create_xml_node(NULL,
                                                         XML_TAG_OP_VER_ATTRS);
     }
     if (details->versioned_meta == NULL) {
         details->versioned_meta = create_xml_node(NULL, XML_TAG_OP_VER_META);
     }
     return details;
 }
 
 static void
 pe_free_rsc_action_details(pe_action_t *action)
 {
     pe_rsc_action_details_t *details;
 
     if ((action == NULL) || (action->action_details == NULL)) {
         return;
     }
 
     details = (pe_rsc_action_details_t *) action->action_details;
 
     if (details->versioned_parameters) {
         free_xml(details->versioned_parameters);
     }
     if (details->versioned_meta) {
         free_xml(details->versioned_meta);
     }
 
     action->action_details = NULL;
 }
 #endif
 
 /*!
  * \internal
  * \brief Check whether we can fence a particular node
  *
  * \param[in] data_set  Working set for cluster
  * \param[in] node      Name of node to check
  *
  * \return TRUE if node can be fenced, FALSE otherwise
  *
  * \note This function should only be called for cluster nodes and baremetal
  *       remote nodes; guest nodes are fenced by stopping their container
  *       resource, so fence execution requirements do not apply to them.
  */
 bool pe_can_fence(pe_working_set_t * data_set, node_t *node)
 {
     if(is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
         return FALSE; /* Turned off */
 
     } else if (is_not_set(data_set->flags, pe_flag_have_stonith_resource)) {
         return FALSE; /* No devices */
 
     } else if (is_set(data_set->flags, pe_flag_have_quorum)) {
         return TRUE;
 
     } else if (data_set->no_quorum_policy == no_quorum_ignore) {
         return TRUE;
 
     } else if(node == NULL) {
         return FALSE;
 
     } else if(node->details->online) {
         crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
         return TRUE;
     }
 
     crm_trace("Cannot fence %s", node->details->uname);
     return FALSE;
 }
 
 node_t *
 node_copy(const node_t *this_node)
 {
     node_t *new_node = NULL;
 
     CRM_CHECK(this_node != NULL, return NULL);
 
     new_node = calloc(1, sizeof(node_t));
     CRM_ASSERT(new_node != NULL);
 
     crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node);
 
     new_node->rsc_discover_mode = this_node->rsc_discover_mode;
     new_node->weight = this_node->weight;
     new_node->fixed = this_node->fixed;
     new_node->details = this_node->details;
 
     return new_node;
 }
 
 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 void
 node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores)
 {
     GHashTable *result = hash;
     node_t *other_node = NULL;
     GListPtr gIter = list;
 
     GHashTableIter iter;
     node_t *node = NULL;
 
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 
         other_node = pe_find_node_id(list, node->details->id);
         if (other_node == NULL) {
             node->weight = -INFINITY;
         } else if (merge_scores) {
             node->weight = merge_weights(node->weight, other_node->weight);
         }
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         other_node = pe_hash_table_lookup(result, node->details->id);
 
         if (other_node == NULL) {
             node_t *new_node = node_copy(node);
 
             new_node->weight = -INFINITY;
             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
         }
     }
 }
 
 GHashTable *
 node_hash_from_list(GListPtr list)
 {
     GListPtr gIter = list;
     GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         node_t *n = node_copy(node);
 
         g_hash_table_insert(result, (gpointer) n->details->id, n);
     }
 
     return result;
 }
 
 GListPtr
 node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
 {
     GListPtr result = NULL;
     GListPtr gIter = list1;
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *new_node = NULL;
         node_t *this_node = (node_t *) gIter->data;
 
         if (filter && this_node->weight < 0) {
             continue;
         }
 
         new_node = node_copy(this_node);
         if (reset) {
             new_node->weight = 0;
         }
         if (new_node != NULL) {
             result = g_list_prepend(result, new_node);
         }
     }
 
     return result;
 }
 
 gint
 sort_node_uname(gconstpointer a, gconstpointer b)
 {
     const node_t *node_a = a;
     const node_t *node_b = b;
 
     return strcmp(node_a->details->uname, node_b->details->uname);
 }
 
 void
 dump_node_scores_worker(int level, const char *file, const char *function, int line,
                         resource_t * rsc, const char *comment, GHashTable * nodes)
 {
     GHashTable *hash = nodes;
     GHashTableIter iter;
     node_t *node = NULL;
 
     if (rsc) {
         hash = rsc->allowed_nodes;
     }
 
     if (rsc && is_set(rsc->flags, pe_rsc_orphan)) {
         /* Don't show the allocation scores for orphans */
         return;
     }
 
     if (level == 0) {
         char score[128];
         int len = sizeof(score);
         /* For now we want this in sorted order to keep the regression tests happy */
         GListPtr gIter = NULL;
         GListPtr list = g_hash_table_get_values(hash);
 
         list = g_list_sort(list, sort_node_uname);
 
         gIter = list;
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *node = (node_t *) gIter->data;
             /* This function is called a whole lot, use stack allocated score */
             score2char_stack(node->weight, score, len);
 
             if (rsc) {
                 printf("%s: %s allocation score on %s: %s\n",
                        comment, rsc->id, node->details->uname, score);
             } else {
                 printf("%s: %s = %s\n", comment, node->details->uname, score);
             }
         }
 
         g_list_free(list);
 
     } else if (hash) {
         char score[128];
         int len = sizeof(score);
         g_hash_table_iter_init(&iter, hash);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
             /* This function is called a whole lot, use stack allocated score */
             score2char_stack(node->weight, score, len);
 
             if (rsc) {
                 do_crm_log_alias(LOG_TRACE, file, function, line,
                                  "%s: %s allocation score on %s: %s", comment, rsc->id,
                                  node->details->uname, score);
             } else {
                 do_crm_log_alias(LOG_TRACE, file, function, line + 1, "%s: %s = %s", comment,
                                  node->details->uname, score);
             }
         }
     }
 
     if (rsc && rsc->children) {
         GListPtr gIter = NULL;
 
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             dump_node_scores_worker(level, file, function, line, child, comment, nodes);
         }
     }
 }
 
 static void
 append_dump_text(gpointer key, gpointer value, gpointer user_data)
 {
     char **dump_text = user_data;
     char *new_text = crm_strdup_printf("%s %s=%s",
                                        *dump_text, (char *)key, (char *)value);
 
     free(*dump_text);
     *dump_text = new_text;
 }
 
 void
 dump_node_capacity(int level, const char *comment, node_t * node)
 {
     char *dump_text = crm_strdup_printf("%s: %s capacity:",
                                         comment, node->details->uname);
 
     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
 
     if (level == 0) {
         fprintf(stdout, "%s\n", dump_text);
     } else {
         crm_trace("%s", dump_text);
     }
 
     free(dump_text);
 }
 
 void
 dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node)
 {
     char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
                                         comment, rsc->id, node->details->uname);
 
     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
 
     if (level == 0) {
         fprintf(stdout, "%s\n", dump_text);
     } else {
         crm_trace("%s", dump_text);
     }
 
     free(dump_text);
 }
 
 gint
 sort_rsc_index(gconstpointer a, gconstpointer b)
 {
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     if (a == NULL && b == NULL) {
         return 0;
     }
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     if (resource1->sort_index > resource2->sort_index) {
         return -1;
     }
 
     if (resource1->sort_index < resource2->sort_index) {
         return 1;
     }
 
     return 0;
 }
 
 gint
 sort_rsc_priority(gconstpointer a, gconstpointer b)
 {
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     if (a == NULL && b == NULL) {
         return 0;
     }
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     if (resource1->priority > resource2->priority) {
         return -1;
     }
 
     if (resource1->priority < resource2->priority) {
         return 1;
     }
 
     return 0;
 }
 
 action_t *
 custom_action(resource_t * rsc, char *key, const char *task,
               node_t * on_node, gboolean optional, gboolean save_action,
               pe_working_set_t * data_set)
 {
     action_t *action = NULL;
     GListPtr possible_matches = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
     CRM_CHECK(task != NULL, free(key); return NULL);
 
     if (save_action && rsc != NULL) {
         possible_matches = find_actions(rsc->actions, key, on_node);
     } else if(save_action) {
 #if 0
         action = g_hash_table_lookup(data_set->singletons, key);
 #else
         /* More expensive but takes 'node' into account */
         possible_matches = find_actions(data_set->actions, key, on_node);
 #endif
     }
 
     if(data_set->singletons == NULL) {
         data_set->singletons = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, NULL);
     }
 
     if (possible_matches != NULL) {
         if (g_list_length(possible_matches) > 1) {
             pe_warn("Action %s for %s on %s exists %d times",
                     task, rsc ? rsc->id : "<NULL>",
                     on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
         }
 
         action = g_list_nth_data(possible_matches, 0);
         pe_rsc_trace(rsc, "Found existing action (%d) %s for %s on %s",
                      action->id, task, rsc ? rsc->id : "<NULL>",
                      on_node ? on_node->details->uname : "<NULL>");
         g_list_free(possible_matches);
     }
 
     if (action == NULL) {
         if (save_action) {
             pe_rsc_trace(rsc, "Creating%s action %d: %s for %s on %s %d",
                          optional ? "" : " mandatory", data_set->action_id, key,
                          rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>", optional);
         }
 
         action = calloc(1, sizeof(action_t));
         if (save_action) {
             action->id = data_set->action_id++;
         } else {
             action->id = 0;
         }
         action->rsc = rsc;
         CRM_ASSERT(task != NULL);
         action->task = strdup(task);
         if (on_node) {
             action->node = node_copy(on_node);
         }
         action->uuid = strdup(key);
 
         pe_set_action_bit(action, pe_action_runnable);
         if (optional) {
             pe_rsc_trace(rsc, "Set optional on %s", action->uuid);
             pe_set_action_bit(action, pe_action_optional);
         } else {
             pe_clear_action_bit(action, pe_action_optional);
             pe_rsc_trace(rsc, "Unset optional on %s", action->uuid);
         }
 
 /*
   Implied by calloc()...
   action->actions_before   = NULL;
   action->actions_after    = NULL;
 
   action->pseudo     = FALSE;
   action->dumped     = FALSE;
   action->processed  = FALSE;
   action->seen_count = 0;
 */
 
         action->extra = crm_str_table_new();
         action->meta = crm_str_table_new();
 
         if (save_action) {
             data_set->actions = g_list_prepend(data_set->actions, action);
             if(rsc == NULL) {
                 g_hash_table_insert(data_set->singletons, action->uuid, action);
             }
         }
 
         if (rsc != NULL) {
             action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
 
             unpack_operation(action, action->op_entry, rsc->container, data_set);
 
             if (save_action) {
                 rsc->actions = g_list_prepend(rsc->actions, action);
             }
         }
 
         if (save_action) {
             pe_rsc_trace(rsc, "Action %d created", action->id);
         }
     }
 
     if (optional == FALSE) {
         pe_rsc_trace(rsc, "Unset optional on %s", action->uuid);
         pe_clear_action_bit(action, pe_action_optional);
     }
 
     if (rsc != NULL) {
         enum action_tasks a_task = text2task(action->task);
         int warn_level = LOG_TRACE;
 
         if (save_action) {
             warn_level = LOG_WARNING;
         }
 
         if (is_set(action->flags, pe_action_have_node_attrs) == FALSE
             && action->node != NULL && action->op_entry != NULL) {
             pe_set_action_bit(action, pe_action_have_node_attrs);
             unpack_instance_attributes(data_set->input, action->op_entry, XML_TAG_ATTR_SETS,
                                        action->node->details->attrs,
                                        action->extra, NULL, FALSE, data_set->now);
         }
 
         if (is_set(action->flags, pe_action_pseudo)) {
             /* leave untouched */
 
         } else if (action->node == NULL) {
             pe_rsc_trace(rsc, "Unset runnable on %s", action->uuid);
             pe_clear_action_bit(action, pe_action_runnable);
 
         } else if (is_not_set(rsc->flags, pe_rsc_managed)
                    && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) {
             crm_debug("Action %s (unmanaged)", action->uuid);
             pe_rsc_trace(rsc, "Set optional on %s", action->uuid);
             pe_set_action_bit(action, pe_action_optional);
 /*   			action->runnable = FALSE; */
 
         } else if (action->node->details->online == FALSE
                    && (!is_container_remote_node(action->node) || action->node->details->remote_requires_reset)) {
             pe_clear_action_bit(action, pe_action_runnable);
             do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
                        action->uuid, action->node->details->uname);
             if (is_set(action->rsc->flags, pe_rsc_managed)
                 && save_action && a_task == stop_rsc
                 && action->node->details->unclean == FALSE) {
                 pe_fence_node(data_set, action->node, "resource actions are unrunnable");
             }
 
         } else if (action->node->details->pending) {
             pe_clear_action_bit(action, pe_action_runnable);
             do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
                        action->uuid, action->node->details->uname);
 
         } else if (action->needs == rsc_req_nothing) {
             pe_rsc_trace(rsc, "Action %s does not require anything", action->uuid);
             pe_action_set_reason(action, NULL, TRUE);
             pe_set_action_bit(action, pe_action_runnable);
 #if 0
             /*
              * No point checking this
              * - if we don't have quorum we can't stonith anyway
              */
         } else if (action->needs == rsc_req_stonith) {
             crm_trace("Action %s requires only stonith", action->uuid);
             action->runnable = TRUE;
 #endif
         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
                    && data_set->no_quorum_policy == no_quorum_stop) {
             pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, NULL, "no quorum", pe_action_runnable, TRUE);
             crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid);
 
         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
                    && data_set->no_quorum_policy == no_quorum_freeze) {
             pe_rsc_trace(rsc, "Check resource is already active: %s %s %s %s", rsc->id, action->uuid, role2text(rsc->next_role), role2text(rsc->role));
             if (rsc->fns->active(rsc, TRUE) == FALSE || rsc->next_role > rsc->role) {
                 pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, NULL, "quorum freeze", pe_action_runnable, TRUE);
                 pe_rsc_debug(rsc, "%s\t%s (cancelled : quorum freeze)",
                              action->node->details->uname, action->uuid);
             }
 
         } else if(is_not_set(action->flags, pe_action_runnable)) {
             pe_rsc_trace(rsc, "Action %s is runnable", action->uuid);
             //pe_action_set_reason(action, NULL, TRUE);
             pe_set_action_bit(action, pe_action_runnable);
         }
 
         if (save_action) {
             switch (a_task) {
                 case stop_rsc:
                     set_bit(rsc->flags, pe_rsc_stopping);
                     break;
                 case start_rsc:
                     clear_bit(rsc->flags, pe_rsc_starting);
                     if (is_set(action->flags, pe_action_runnable)) {
                         set_bit(rsc->flags, pe_rsc_starting);
                     }
                     break;
                 default:
                     break;
             }
         }
     }
 
     free(key);
     return action;
 }
 
 static const char *
 unpack_operation_on_fail(action_t * action)
 {
 
     const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
 
     if (safe_str_eq(action->task, CRMD_ACTION_STOP) && safe_str_eq(value, "standby")) {
         crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
         return NULL;
     } else if (safe_str_eq(action->task, CRMD_ACTION_DEMOTE) && !value) {
         /* demote on_fail defaults to master monitor value if present */
         xmlNode *operation = NULL;
         const char *name = NULL;
         const char *role = NULL;
         const char *on_fail = NULL;
         const char *interval = NULL;
         const char *enabled = NULL;
 
         CRM_CHECK(action->rsc != NULL, return NULL);
 
         for (operation = __xml_first_child(action->rsc->ops_xml);
              operation && !value; operation = __xml_next_element(operation)) {
 
             if (!crm_str_eq((const char *)operation->name, "op", TRUE)) {
                 continue;
             }
             name = crm_element_value(operation, "name");
             role = crm_element_value(operation, "role");
             on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
             enabled = crm_element_value(operation, "enabled");
             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
             if (!on_fail) {
                 continue;
             } else if (enabled && !crm_is_true(enabled)) {
                 continue;
             } else if (safe_str_neq(name, "monitor") || safe_str_neq(role, "Master")) {
                 continue;
             } else if (crm_get_interval(interval) <= 0) {
                 continue;
             }
 
             value = on_fail;
         }
     }
 
     return value;
 }
 
 static xmlNode *
 find_min_interval_mon(resource_t * rsc, gboolean include_disabled)
 {
     int number = 0;
     int min_interval = -1;
     const char *name = NULL;
     const char *value = NULL;
     const char *interval = NULL;
     xmlNode *op = NULL;
     xmlNode *operation = NULL;
 
     for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
          operation = __xml_next_element(operation)) {
 
         if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
             name = crm_element_value(operation, "name");
             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
             value = crm_element_value(operation, "enabled");
             if (!include_disabled && value && crm_is_true(value) == FALSE) {
                 continue;
             }
 
             if (safe_str_neq(name, RSC_STATUS)) {
                 continue;
             }
 
             number = crm_get_interval(interval);
             if (number < 0) {
                 continue;
             }
 
             if (min_interval < 0 || number < min_interval) {
                 min_interval = number;
                 op = operation;
             }
         }
     }
 
     return op;
 }
 
 static int
 unpack_start_delay(const char *value, GHashTable *meta)
 {
     int start_delay = 0;
 
     if (value != NULL) {
         start_delay = crm_get_msec(value);
 
         if (start_delay < 0) {
             start_delay = 0;
         }
 
         if (meta) {
             g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY), crm_itoa(start_delay));
         }
     }
 
     return start_delay;
 }
 
 static int
 unpack_interval_origin(const char *value, GHashTable *meta, xmlNode *xml_obj,
                        unsigned long long interval, crm_time_t *now)
 {
     int start_delay = 0;
 
     if (interval > 0 && value) {
         crm_time_t *origin = crm_time_new(value);
 
         if (origin && now) {
             crm_time_t *delay = NULL;
             int rc = crm_time_compare(origin, now);
             long long delay_s = 0;
             int interval_s = (interval / 1000);
 
             crm_trace("Origin: %s, interval: %d", value, interval_s);
 
             /* If 'origin' is in the future, find the most recent "multiple" that occurred in the past */
             while(rc > 0) {
                 crm_time_add_seconds(origin, -interval_s);
                 rc = crm_time_compare(origin, now);
             }
 
             /* Now find the first "multiple" that occurs after 'now' */
             while (rc < 0) {
                 crm_time_add_seconds(origin, interval_s);
                 rc = crm_time_compare(origin, now);
             }
 
             delay = crm_time_calculate_duration(origin, now);
 
             crm_time_log(LOG_TRACE, "origin", origin,
                          crm_time_log_date | crm_time_log_timeofday |
                          crm_time_log_with_timezone);
             crm_time_log(LOG_TRACE, "now", now,
                          crm_time_log_date | crm_time_log_timeofday |
                          crm_time_log_with_timezone);
             crm_time_log(LOG_TRACE, "delay", delay, crm_time_log_duration);
 
             delay_s = crm_time_get_seconds(delay);
 
             CRM_CHECK(delay_s >= 0, delay_s = 0);
             start_delay = delay_s * 1000;
 
             if (xml_obj) {
                 crm_info("Calculated a start delay of %llds for %s", delay_s, ID(xml_obj));
             }
 
             if (meta) {
                 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
                                      crm_itoa(start_delay));
             }
 
             crm_time_free(origin);
             crm_time_free(delay);
         } else if (!origin && xml_obj) {
             crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
                            ID(xml_obj), value);
         }
     }
 
     return start_delay;
 }
 
 static int
 unpack_timeout(const char *value)
 {
     int timeout = 0;
 
     if (value == NULL) {
         value = CRM_DEFAULT_OP_TIMEOUT_S;
     }
 
     timeout = crm_get_msec(value);
     if (timeout < 0) {
         timeout = 0;
     }
 
     return timeout;
 }
 
 int
 pe_get_configured_timeout(resource_t *rsc, const char *action, pe_working_set_t *data_set)
 {
     xmlNode *child = NULL;
     const char *timeout = NULL;
     int timeout_ms = 0;
 
     for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
          child != NULL; child = crm_next_same_xml(child)) {
         if (safe_str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME))) {
             timeout = crm_element_value(child, XML_ATTR_TIMEOUT);
             break;
         }
     }
 
     if (timeout == NULL && data_set->op_defaults) {
         GHashTable *action_meta = crm_str_table_new();
         unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS,
                                    NULL, action_meta, NULL, FALSE, data_set->now);
         timeout = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
     }
 
     if (timeout == NULL) {
         timeout = CRM_DEFAULT_OP_TIMEOUT_S;
     }
 
     timeout_ms = crm_get_msec(timeout);
     if (timeout_ms < 0) {
         timeout_ms = 0;
     }
 
     return timeout_ms;
 }
 
 #if ENABLE_VERSIONED_ATTRS
 static void
 unpack_versioned_meta(xmlNode *versioned_meta, xmlNode *xml_obj, unsigned long long interval, crm_time_t *now)
 {
     xmlNode *attrs = NULL;
     xmlNode *attr = NULL;
 
     for (attrs = __xml_first_child(versioned_meta); attrs != NULL; attrs = __xml_next_element(attrs)) {
         for (attr = __xml_first_child(attrs); attr != NULL; attr = __xml_next_element(attr)) {
             const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
             const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
 
             if (safe_str_eq(name, XML_OP_ATTR_START_DELAY)) {
                 int start_delay = unpack_start_delay(value, NULL);
 
                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
             } else if (safe_str_eq(name, XML_OP_ATTR_ORIGIN)) {
                 int start_delay = unpack_interval_origin(value, NULL, xml_obj, interval, now);
 
                 crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, XML_OP_ATTR_START_DELAY);
                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
             } else if (safe_str_eq(name, XML_ATTR_TIMEOUT)) {
                 int timeout = unpack_timeout(value);
 
                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, timeout);
             }
         }
     }
 }
 #endif
 
 /*!
  * \brief Unpack operation XML into an action structure
  *
  * Unpack an operation's meta-attributes (normalizing the interval, timeout,
  * and start delay values as integer milliseconds), requirements, and
  * failure policy.
  *
  * \param[in,out] action     Action to unpack into
  * \param[in]     xml_obj    Operation XML (or NULL if all defaults)
  * \param[in]     container  Resource that contains affected resource, if any
  * \param[in]     data_set   Cluster state
  */
 void
 unpack_operation(action_t * action, xmlNode * xml_obj, resource_t * container,
                  pe_working_set_t * data_set)
 {
     unsigned long long interval = 0;
     int timeout = 0;
     char *value_ms = NULL;
     const char *value = NULL;
     const char *field = NULL;
 #if ENABLE_VERSIONED_ATTRS
     pe_rsc_action_details_t *rsc_details = NULL;
 #endif
 
     CRM_CHECK(action->rsc != NULL, return);
 
     // Probe timeouts default to minimum-interval monitor's
     if ((xml_obj == NULL) && action &&
         safe_str_eq(action->task, RSC_STATUS) && (interval == 0)) {
 
         xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
 
         if (min_interval_mon) {
             value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
             if (value) {
                 crm_trace("\t%s defaults to minimum-interval monitor's timeout '%s'",
                           action->uuid, value);
                 g_hash_table_insert(action->meta, strdup(XML_ATTR_TIMEOUT),
                                     strdup(value));
             }
         }
     }
 
     // Cluster-wide <op_defaults> <meta_attributes>
     unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
                                action->meta, NULL, FALSE, data_set->now);
 
     // <op> <meta_attributes> take precedence over defaults
     unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
                                NULL, action->meta, NULL, TRUE, data_set->now);
 
     /* Anything set as an <op> XML property has highest precedence.
      * This ensures we use the name and interval from the <op> tag.
      */
     if (xml_obj) {
         xmlAttrPtr xIter = NULL;
 
         for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
             const char *prop_name = (const char *)xIter->name;
             const char *prop_value = crm_element_value(xml_obj, prop_name);
 
             g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
         }
     }
 
 #if ENABLE_VERSIONED_ATTRS
     rsc_details = pe_rsc_action_details(action);
     pe_unpack_versioned_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL,
                                    rsc_details->versioned_parameters, data_set->now);
     pe_unpack_versioned_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, NULL,
                                    rsc_details->versioned_meta, data_set->now);
 #endif
 
     g_hash_table_remove(action->meta, "id");
 
     field = XML_LRM_ATTR_INTERVAL;
     value = g_hash_table_lookup(action->meta, field);
     if (value != NULL) {
         interval = crm_get_interval(value);
         if (interval > 0) {
             value_ms = crm_itoa(interval);
             g_hash_table_replace(action->meta, strdup(field), value_ms);
 
         } else {
             g_hash_table_remove(action->meta, field);
         }
     }
 
     if (safe_str_neq(action->task, RSC_START)
         && safe_str_neq(action->task, RSC_PROMOTE)) {
         action->needs = rsc_req_nothing;
         value = "nothing (not start/promote)";
 
     } else if (is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
         action->needs = rsc_req_stonith;
         value = "fencing (resource)";
 
     } else if (is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
         action->needs = rsc_req_quorum;
         value = "quorum (resource)";
 
     } else {
         action->needs = rsc_req_nothing;
         value = "nothing (resource)";
     }
 
     pe_rsc_trace(action->rsc, "\tAction %s requires: %s", action->uuid, value);
 
     value = unpack_operation_on_fail(action);
 
     if (value == NULL) {
 
     } else if (safe_str_eq(value, "block")) {
         action->on_fail = action_fail_block;
         g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
 
     } else if (safe_str_eq(value, "fence")) {
         action->on_fail = action_fail_fence;
         value = "node fencing";
 
         if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
             crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense");
             action->on_fail = action_fail_stop;
             action->fail_role = RSC_ROLE_STOPPED;
             value = "stop resource";
         }
 
     } else if (safe_str_eq(value, "standby")) {
         action->on_fail = action_fail_standby;
         value = "node standby";
 
     } else if (safe_str_eq(value, "ignore")
                || safe_str_eq(value, "nothing")) {
         action->on_fail = action_fail_ignore;
         value = "ignore";
 
     } else if (safe_str_eq(value, "migrate")) {
         action->on_fail = action_fail_migrate;
         value = "force migration";
 
     } else if (safe_str_eq(value, "stop")) {
         action->on_fail = action_fail_stop;
         action->fail_role = RSC_ROLE_STOPPED;
         value = "stop resource";
 
     } else if (safe_str_eq(value, "restart")) {
         action->on_fail = action_fail_recover;
         value = "restart (and possibly migrate)";
 
     } else if (safe_str_eq(value, "restart-container")) {
         if (container) {
             action->on_fail = action_fail_restart_container;
             value = "restart container (and possibly migrate)";
 
         } else {
             value = NULL;
         }
 
     } else {
         pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
         value = NULL;
     }
 
     /* defaults */
     if (value == NULL && container) {
         action->on_fail = action_fail_restart_container;
         value = "restart container (and possibly migrate) (default)";
 
     /* for baremetal remote nodes, ensure that any failure that results in
      * dropping an active connection to a remote node results in fencing of
      * the remote node.
      *
      * There are only two action failures that don't result in fencing.
      * 1. probes - probe failures are expected.
      * 2. start - a start failure indicates that an active connection does not already
      * exist. The user can set op on-fail=fence if they really want to fence start
      * failures. */
     } else if (((value == NULL) || !is_set(action->rsc->flags, pe_rsc_managed)) &&
                 (is_rsc_baremetal_remote_node(action->rsc, data_set) &&
                !(safe_str_eq(action->task, CRMD_ACTION_STATUS) && interval == 0) &&
                 (safe_str_neq(action->task, CRMD_ACTION_START)))) {
 
         if (!is_set(action->rsc->flags, pe_rsc_managed)) {
             action->on_fail = action_fail_stop;
             action->fail_role = RSC_ROLE_STOPPED;
             value = "stop unmanaged baremetal remote node (enforcing default)";
 
         } else {
             if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
                 value = "fence baremetal remote node (default)";
             } else {
                 value = "recover baremetal remote node connection (default)";
             }
 
             if (action->rsc->remote_reconnect_interval) {
                 action->fail_role = RSC_ROLE_STOPPED;
             }
             action->on_fail = action_fail_reset_remote;
         }
 
     } else if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
         if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
             action->on_fail = action_fail_fence;
             value = "resource fence (default)";
 
         } else {
             action->on_fail = action_fail_block;
             value = "resource block (default)";
         }
 
     } else if (value == NULL) {
         action->on_fail = action_fail_recover;
         value = "restart (and possibly migrate) (default)";
     }
 
     pe_rsc_trace(action->rsc, "\t%s failure handling: %s", action->task, value);
 
     value = NULL;
     if (xml_obj != NULL) {
         value = g_hash_table_lookup(action->meta, "role_after_failure");
         if (value) {
             pe_warn_once(pe_wo_role_after,
                         "Support for role_after_failure is deprecated and will be removed in a future release");
         }
     }
     if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
         action->fail_role = text2role(value);
     }
     /* defaults */
     if (action->fail_role == RSC_ROLE_UNKNOWN) {
         if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
             action->fail_role = RSC_ROLE_SLAVE;
         } else {
             action->fail_role = RSC_ROLE_STARTED;
         }
     }
     pe_rsc_trace(action->rsc, "\t%s failure results in: %s", action->task,
                  role2text(action->fail_role));
 
     field = XML_OP_ATTR_START_DELAY;
     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
     if (value) {
         unpack_start_delay(value, action->meta);
     } else {
         value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
         unpack_interval_origin(value, action->meta, xml_obj, interval, data_set->now);
     }
 
     field = XML_ATTR_TIMEOUT;
     value = g_hash_table_lookup(action->meta, field);
     timeout = unpack_timeout(value);
     g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT), crm_itoa(timeout));
 
 #if ENABLE_VERSIONED_ATTRS
     unpack_versioned_meta(rsc_details->versioned_meta, xml_obj, interval,
                           data_set->now);
 #endif
 }
 
 static xmlNode *
 find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled)
 {
     unsigned long long number = 0;
     gboolean do_retry = TRUE;
     char *local_key = NULL;
     const char *name = NULL;
     const char *value = NULL;
     const char *interval = NULL;
     char *match_key = NULL;
     xmlNode *op = NULL;
     xmlNode *operation = NULL;
 
   retry:
     for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
          operation = __xml_next_element(operation)) {
         if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
             name = crm_element_value(operation, "name");
             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
             value = crm_element_value(operation, "enabled");
             if (!include_disabled && value && crm_is_true(value) == FALSE) {
                 continue;
             }
 
             number = crm_get_interval(interval);
             match_key = generate_op_key(rsc->id, name, number);
             if (safe_str_eq(key, match_key)) {
                 op = operation;
             }
             free(match_key);
 
             if (rsc->clone_name) {
                 match_key = generate_op_key(rsc->clone_name, name, number);
                 if (safe_str_eq(key, match_key)) {
                     op = operation;
                 }
                 free(match_key);
             }
 
             if (op != NULL) {
                 free(local_key);
                 return op;
             }
         }
     }
 
     free(local_key);
     if (do_retry == FALSE) {
         return NULL;
     }
 
     do_retry = FALSE;
     if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
         local_key = generate_op_key(rsc->id, "migrate", 0);
         key = local_key;
         goto retry;
 
     } else if (strstr(key, "_notify_")) {
         local_key = generate_op_key(rsc->id, "notify", 0);
         key = local_key;
         goto retry;
     }
 
     return NULL;
 }
 
 xmlNode *
 find_rsc_op_entry(resource_t * rsc, const char *key)
 {
     return find_rsc_op_entry_helper(rsc, key, FALSE);
 }
 
 void
 print_node(const char *pre_text, node_t * node, gboolean details)
 {
     if (node == NULL) {
         crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
         return;
     }
 
     CRM_ASSERT(node->details);
     crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)",
               pre_text == NULL ? "" : pre_text,
               pre_text == NULL ? "" : ": ",
               node->details->online ? "" : "Unavailable/Unclean ",
               node->details->uname, node->weight, node->fixed ? "True" : "False");
 
     if (details) {
         char *pe_mutable = strdup("\t\t");
         GListPtr gIter = node->details->running_rsc;
 
         crm_trace("\t\t===Node Attributes");
         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
         free(pe_mutable);
 
         crm_trace("\t\t=== Resources");
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *rsc = (resource_t *) gIter->data;
 
             print_resource(LOG_TRACE, "\t\t", rsc, FALSE);
         }
     }
 }
 
 /*
  * Used by the HashTable for-loop
  */
 void
 print_str_str(gpointer key, gpointer value, gpointer user_data)
 {
     crm_trace("%s%s %s ==> %s",
               user_data == NULL ? "" : (char *)user_data,
               user_data == NULL ? "" : ": ", (char *)key, (char *)value);
 }
 
 void
 print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details)
 {
     long options = pe_print_log | pe_print_pending;
 
     if (rsc == NULL) {
         do_crm_log(log_level - 1, "%s%s: <NULL>",
                    pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
         return;
     }
     if (details) {
         options |= pe_print_details;
     }
     rsc->fns->print(rsc, pre_text, options, &log_level);
 }
 
 void
 pe_free_action(action_t * action)
 {
     if (action == NULL) {
         return;
     }
     g_list_free_full(action->actions_before, free);     /* action_wrapper_t* */
     g_list_free_full(action->actions_after, free);      /* action_wrapper_t* */
     if (action->extra) {
         g_hash_table_destroy(action->extra);
     }
     if (action->meta) {
         g_hash_table_destroy(action->meta);
     }
 #if ENABLE_VERSIONED_ATTRS
     if (action->rsc) {
         pe_free_rsc_action_details(action);
     }
 #endif
     free(action->cancel_task);
     free(action->reason);
     free(action->task);
     free(action->uuid);
     free(action->node);
     free(action);
 }
 
 GListPtr
 find_recurring_actions(GListPtr input, node_t * not_on_node)
 {
     const char *value = NULL;
     GListPtr result = NULL;
     GListPtr gIter = input;
 
     CRM_CHECK(input != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
         if (value == NULL) {
             /* skip */
         } else if (safe_str_eq(value, "0")) {
             /* skip */
         } else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
             /* skip */
         } else if (not_on_node == NULL) {
             crm_trace("(null) Found: %s", action->uuid);
             result = g_list_prepend(result, action);
 
         } else if (action->node == NULL) {
             /* skip */
         } else if (action->node->details != not_on_node->details) {
             crm_trace("Found: %s", action->uuid);
             result = g_list_prepend(result, action);
         }
     }
 
     return result;
 }
 
 enum action_tasks
 get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic)
 {
     enum action_tasks task = text2task(name);
 
     if (rsc == NULL) {
         return task;
 
     } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
         switch (task) {
             case stopped_rsc:
             case started_rsc:
             case action_demoted:
             case action_promoted:
                 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
                 return task - 1;
                 break;
             default:
                 break;
         }
     }
     return task;
 }
 
 action_t *
 find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node)
 {
     GListPtr gIter = NULL;
 
     CRM_CHECK(uuid || task, return NULL);
 
     for (gIter = input; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (uuid != NULL && safe_str_neq(uuid, action->uuid)) {
             continue;
 
         } else if (task != NULL && safe_str_neq(task, action->task)) {
             continue;
 
         } else if (on_node == NULL) {
             return action;
 
         } else if (action->node == NULL) {
             continue;
 
         } else if (on_node->details == action->node->details) {
             return action;
         }
     }
 
     return NULL;
 }
 
 GListPtr
 find_actions(GListPtr input, const char *key, const node_t *on_node)
 {
     GListPtr gIter = input;
     GListPtr result = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (safe_str_neq(key, action->uuid)) {
             crm_trace("%s does not match action %s", key, action->uuid);
             continue;
 
         } else if (on_node == NULL) {
             crm_trace("Action %s matches (ignoring node)", key);
             result = g_list_prepend(result, action);
 
         } else if (action->node == NULL) {
             crm_trace("Action %s matches (unallocated, assigning to %s)",
                       key, on_node->details->uname);
 
             action->node = node_copy(on_node);
             result = g_list_prepend(result, action);
 
         } else if (on_node->details == action->node->details) {
             crm_trace("Action %s on %s matches", key, on_node->details->uname);
             result = g_list_prepend(result, action);
 
         } else {
             crm_trace("Action %s on node %s does not match requested node %s",
                       key, action->node->details->uname,
                       on_node->details->uname);
         }
     }
 
     return result;
 }
 
 GListPtr
 find_actions_exact(GListPtr input, const char *key, node_t * on_node)
 {
     GListPtr gIter = input;
     GListPtr result = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         crm_trace("Matching %s against %s", key, action->uuid);
         if (safe_str_neq(key, action->uuid)) {
             crm_trace("Key mismatch: %s vs. %s", key, action->uuid);
             continue;
 
         } else if (on_node == NULL || action->node == NULL) {
             crm_trace("on_node=%p, action->node=%p", on_node, action->node);
             continue;
 
         } else if (safe_str_eq(on_node->details->id, action->node->details->id)) {
             result = g_list_prepend(result, action);
         }
         crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id);
     }
 
     return result;
 }
 
 static void
 resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag)
 {
     node_t *match = NULL;
 
     if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
         && safe_str_eq(tag, "symmetric_default")) {
         /* This string comparision may be fragile, but exclusive resources and
          * exclusive nodes should not have the symmetric_default constraint
          * applied to them.
          */
         return;
 
     } else if (rsc->children) {
         GListPtr gIter = rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             resource_node_score(child_rsc, node, score, tag);
         }
     }
 
     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
     if (match == NULL) {
         match = node_copy(node);
         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
     }
     match->weight = merge_weights(match->weight, score);
 }
 
 void
 resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
                   pe_working_set_t * data_set)
 {
     if (node != NULL) {
         resource_node_score(rsc, node, score, tag);
 
     } else if (data_set != NULL) {
         GListPtr gIter = data_set->nodes;
 
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *node_iter = (node_t *) gIter->data;
 
             resource_node_score(rsc, node_iter, score, tag);
         }
 
     } else {
         GHashTableIter iter;
         node_t *node_iter = NULL;
 
         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
             resource_node_score(rsc, node_iter, score, tag);
         }
     }
 
     if (node == NULL && score == -INFINITY) {
         if (rsc->allocated_to) {
             crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
             free(rsc->allocated_to);
             rsc->allocated_to = NULL;
         }
     }
 }
 
 #define sort_return(an_int, why) do {					\
 	free(a_uuid);						\
 	free(b_uuid);						\
 	crm_trace("%s (%d) %c %s (%d) : %s",				\
 		  a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',	\
 		  b_xml_id, b_call_id, why);				\
 	return an_int;							\
     } while(0)
 
 gint
 sort_op_by_callid(gconstpointer a, gconstpointer b)
 {
     int a_call_id = -1;
     int b_call_id = -1;
 
     char *a_uuid = NULL;
     char *b_uuid = NULL;
 
     const xmlNode *xml_a = a;
     const xmlNode *xml_b = b;
 
-    const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
-    const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
+    const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
+    const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
 
     if (safe_str_eq(a_xml_id, b_xml_id)) {
         /* We have duplicate lrm_rsc_op entries in the status
          *    section which is unliklely to be a good thing
          *    - we can handle it easily enough, but we need to get
          *    to the bottom of why it's happening.
          */
         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
         sort_return(0, "duplicate");
     }
 
-    crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
-    crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
+    crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
+    crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
 
     if (a_call_id == -1 && b_call_id == -1) {
         /* both are pending ops so it doesn't matter since
          *   stops are never pending
          */
         sort_return(0, "pending");
 
     } else if (a_call_id >= 0 && a_call_id < b_call_id) {
         sort_return(-1, "call id");
 
     } else if (b_call_id >= 0 && a_call_id > b_call_id) {
         sort_return(1, "call id");
 
     } else if (b_call_id >= 0 && a_call_id == b_call_id) {
         /*
          * The op and last_failed_op are the same
          * Order on last-rc-change
          */
         int last_a = -1;
         int last_b = -1;
 
-        crm_element_value_const_int(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
-        crm_element_value_const_int(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
+        crm_element_value_int(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
+        crm_element_value_int(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
 
         crm_trace("rc-change: %d vs %d", last_a, last_b);
         if (last_a >= 0 && last_a < last_b) {
             sort_return(-1, "rc-change");
 
         } else if (last_b >= 0 && last_a > last_b) {
             sort_return(1, "rc-change");
         }
         sort_return(0, "rc-change");
 
     } else {
         /* One of the inputs is a pending operation
          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
          */
 
         int a_id = -1;
         int b_id = -1;
         int dummy = -1;
 
-        const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
-        const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
+        const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
+        const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
 
         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
         if(!decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy)) {
             sort_return(0, "bad magic a");
         }
         if(!decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy)) {
             sort_return(0, "bad magic b");
         }
         /* try to determine the relative age of the operation...
          * some pending operations (e.g. a start) may have been superseded
          *   by a subsequent stop
          *
          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
          */
         if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
             /*
              * some of the logic in here may be redundant...
              *
              * if the UUID from the TE doesn't match then one better
              *   be a pending operation.
              * pending operations don't survive between elections and joins
              *   because we query the LRM directly
              */
 
             if (b_call_id == -1) {
                 sort_return(-1, "transition + call");
 
             } else if (a_call_id == -1) {
                 sort_return(1, "transition + call");
             }
 
         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
             sort_return(-1, "transition");
 
         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
             sort_return(1, "transition");
         }
     }
 
     /* we should never end up here */
     CRM_CHECK(FALSE, sort_return(0, "default"));
 
 }
 
 time_t
 get_effective_time(pe_working_set_t * data_set)
 {
     if(data_set) {
         if (data_set->now == NULL) {
             crm_trace("Recording a new 'now'");
             data_set->now = crm_time_new(NULL);
         }
         return crm_time_get_seconds_since_epoch(data_set->now);
     }
 
     crm_trace("Defaulting to 'now'");
     return time(NULL);
 }
 
 gboolean
 get_target_role(resource_t * rsc, enum rsc_role_e * role)
 {
     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
 
     CRM_CHECK(role != NULL, return FALSE);
 
     if (value == NULL || safe_str_eq("started", value)
         || safe_str_eq("default", value)) {
         return FALSE;
     }
 
     local_role = text2role(value);
     if (local_role == RSC_ROLE_UNKNOWN) {
         crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
         return FALSE;
 
     } else if (local_role > RSC_ROLE_STARTED) {
         if (is_set(uber_parent(rsc)->flags, pe_rsc_promotable)) {
             if (local_role > RSC_ROLE_SLAVE) {
                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
                 return FALSE;
             }
 
         } else {
             crm_config_err("%s is not part of a promotable clone resource, a %s of '%s' makes no sense",
                            rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
             return FALSE;
         }
     }
 
     *role = local_role;
     return TRUE;
 }
 
 gboolean
 order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order)
 {
     GListPtr gIter = NULL;
     action_wrapper_t *wrapper = NULL;
     GListPtr list = NULL;
 
     if (order == pe_order_none) {
         return FALSE;
     }
 
     if (lh_action == NULL || rh_action == NULL) {
         return FALSE;
     }
 
     crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
 
     /* Ensure we never create a dependency on ourselves... it's happened */
     CRM_ASSERT(lh_action != rh_action);
 
     /* Filter dups, otherwise update_action_states() has too much work to do */
     gIter = lh_action->actions_after;
     for (; gIter != NULL; gIter = gIter->next) {
         action_wrapper_t *after = (action_wrapper_t *) gIter->data;
 
         if (after->action == rh_action && (after->type & order)) {
             return FALSE;
         }
     }
 
     wrapper = calloc(1, sizeof(action_wrapper_t));
     wrapper->action = rh_action;
     wrapper->type = order;
 
     list = lh_action->actions_after;
     list = g_list_prepend(list, wrapper);
     lh_action->actions_after = list;
 
     wrapper = NULL;
 
 /* 	order |= pe_order_implies_then; */
 /* 	order ^= pe_order_implies_then; */
 
     wrapper = calloc(1, sizeof(action_wrapper_t));
     wrapper->action = lh_action;
     wrapper->type = order;
     list = rh_action->actions_before;
     list = g_list_prepend(list, wrapper);
     rh_action->actions_before = list;
     return TRUE;
 }
 
 action_t *
 get_pseudo_op(const char *name, pe_working_set_t * data_set)
 {
     action_t *op = NULL;
 
     if(data_set->singletons) {
         op = g_hash_table_lookup(data_set->singletons, name);
     }
     if (op == NULL) {
         op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
         set_bit(op->flags, pe_action_pseudo);
         set_bit(op->flags, pe_action_runnable);
     }
 
     return op;
 }
 
 void
 destroy_ticket(gpointer data)
 {
     ticket_t *ticket = data;
 
     if (ticket->state) {
         g_hash_table_destroy(ticket->state);
     }
     free(ticket->id);
     free(ticket);
 }
 
 ticket_t *
 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
 {
     ticket_t *ticket = NULL;
 
     if (ticket_id == NULL || strlen(ticket_id) == 0) {
         return NULL;
     }
 
     if (data_set->tickets == NULL) {
         data_set->tickets =
             g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
     }
 
     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
     if (ticket == NULL) {
 
         ticket = calloc(1, sizeof(ticket_t));
         if (ticket == NULL) {
             crm_err("Cannot allocate ticket '%s'", ticket_id);
             return NULL;
         }
 
         crm_trace("Creaing ticket entry for %s", ticket_id);
 
         ticket->id = strdup(ticket_id);
         ticket->granted = FALSE;
         ticket->last_granted = -1;
         ticket->standby = FALSE;
         ticket->state = crm_str_table_new();
 
         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
     }
 
     return ticket;
 }
 
 static void
 filter_parameters(xmlNode * param_set, const char *param_string, bool need_present)
 {
     if (param_set && param_string) {
         xmlAttrPtr xIter = param_set->properties;
 
         while (xIter) {
             const char *prop_name = (const char *)xIter->name;
             char *name = crm_strdup_printf(" %s ", prop_name);
             char *match = strstr(param_string, name);
 
             free(name);
 
             //  Do now, because current entry might get removed below
             xIter = xIter->next;
 
             if (need_present && match == NULL) {
                 crm_trace("%s not found in %s", prop_name, param_string);
                 xml_remove_prop(param_set, prop_name);
 
             } else if (need_present == FALSE && match) {
                 crm_trace("%s found in %s", prop_name, param_string);
                 xml_remove_prop(param_set, prop_name);
             }
         }
     }
 }
 
 #if ENABLE_VERSIONED_ATTRS
 static void
 append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNode *params)
 {
     GHashTable *hash = pe_unpack_versioned_parameters(versioned_params, ra_version);
     char *key = NULL;
     char *value = NULL;
     GHashTableIter iter;
 
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
         crm_xml_add(params, key, value);
     }
     g_hash_table_destroy(hash);
 }
 #endif
 
 static op_digest_cache_t *
 rsc_action_digest(resource_t * rsc, const char *task, const char *key,
                   node_t * node, xmlNode * xml_op, pe_working_set_t * data_set) 
 {
     op_digest_cache_t *data = NULL;
 
     data = g_hash_table_lookup(node->details->digest_cache, key);
     if (data == NULL) {
         GHashTable *local_rsc_params = crm_str_table_new();
         action_t *action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set);
 #if ENABLE_VERSIONED_ATTRS
         xmlNode *local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
         const char *ra_version = NULL;
 #endif
 
         const char *op_version;
         const char *restart_list = NULL;
         const char *secure_list = " passwd password ";
 
         data = calloc(1, sizeof(op_digest_cache_t));
         CRM_ASSERT(data != NULL);
 
         get_rsc_attributes(local_rsc_params, rsc, node, data_set);
 #if ENABLE_VERSIONED_ATTRS
         pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set);
 #endif
 
         data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
 
         // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
         if (container_fix_remote_addr_in(rsc, data->params_all, "addr")) {
             crm_trace("Fixed addr for %s on %s", rsc->id, node->details->uname);
         }
 
         g_hash_table_foreach(local_rsc_params, hash2field, data->params_all);
         g_hash_table_foreach(action->extra, hash2field, data->params_all);
         g_hash_table_foreach(rsc->parameters, hash2field, data->params_all);
         g_hash_table_foreach(action->meta, hash2metafield, data->params_all);
 
         if(xml_op) {
             secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE);
             restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART);
 
             op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
 #if ENABLE_VERSIONED_ATTRS
             ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION);
 #endif
 
         } else {
             op_version = CRM_FEATURE_SET;
         }
 
 #if ENABLE_VERSIONED_ATTRS
         append_versioned_params(local_versioned_params, ra_version, data->params_all);
         append_versioned_params(rsc->versioned_parameters, ra_version, data->params_all);
 
         {
             pe_rsc_action_details_t *details = pe_rsc_action_details(action);
             append_versioned_params(details->versioned_parameters, ra_version, data->params_all);
         }
 #endif
 
         filter_action_parameters(data->params_all, op_version);
 
         g_hash_table_destroy(local_rsc_params);
         pe_free_action(action);
 
         data->digest_all_calc = calculate_operation_digest(data->params_all, op_version);
 
         if (is_set(data_set->flags, pe_flag_sanitized)) {
             data->params_secure = copy_xml(data->params_all);
             if(secure_list) {
                 filter_parameters(data->params_secure, secure_list, FALSE);
             }
             data->digest_secure_calc = calculate_operation_digest(data->params_secure, op_version);
         }
 
         if(xml_op && crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) != NULL) {
             data->params_restart = copy_xml(data->params_all);
             if (restart_list) {
                 filter_parameters(data->params_restart, restart_list, TRUE);
             }
             data->digest_restart_calc = calculate_operation_digest(data->params_restart, op_version);
         }
 
         g_hash_table_insert(node->details->digest_cache, strdup(key), data);
     }
 
     return data;
 }
 
 op_digest_cache_t *
 rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, node_t * node,
                       pe_working_set_t * data_set)
 {
     op_digest_cache_t *data = NULL;
 
     char *key = NULL;
     int interval = 0;
 
     const char *op_version;
     const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
     const char *interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
 
     const char *digest_all;
     const char *digest_restart;
 
     CRM_ASSERT(node != NULL);
 
     op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
     digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
     digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
 
     interval = crm_parse_int(interval_s, "0");
     key = generate_op_key(rsc->id, task, interval);
     data = rsc_action_digest(rsc, task, key, node, xml_op, data_set);
 
     data->rc = RSC_DIGEST_MATCH;
     if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s",
                  key, node->details->uname,
                  crm_str(digest_restart), data->digest_restart_calc,
                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
         data->rc = RSC_DIGEST_RESTART;
 
     } else if (digest_all == NULL) {
         /* it is unknown what the previous op digest was */
         data->rc = RSC_DIGEST_UNKNOWN;
 
     } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (%s:%s) %s",
                  key, node->details->uname,
                  crm_str(digest_all), data->digest_all_calc,
                  (interval > 0)? "reschedule" : "reload",
                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
         data->rc = RSC_DIGEST_ALL;
     }
 
     free(key);
     return data;
 }
 
 #define STONITH_DIGEST_TASK "stonith-on"
 
 static op_digest_cache_t *
 fencing_action_digest_cmp(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
 {
     char *key = generate_op_key(rsc->id, STONITH_DIGEST_TASK, 0);
     op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key, node, NULL, data_set);
 
     const char *digest_all = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
     const char *digest_secure = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
 
     /* No 'reloads' for fencing device changes
      *
      * We use the resource id + agent + digest so that we can detect
      * changes to the agent and/or the parameters used
      */
     char *search_all = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_all_calc);
     char *search_secure = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_secure_calc);
 
     data->rc = RSC_DIGEST_ALL;
     if (digest_all == NULL) {
         /* it is unknown what the previous op digest was */
         data->rc = RSC_DIGEST_UNKNOWN;
 
     } else if (strstr(digest_all, search_all)) {
         data->rc = RSC_DIGEST_MATCH;
 
     } else if(digest_secure && data->digest_secure_calc) {
         if(strstr(digest_secure, search_secure)) {
             if (is_set(data_set->flags, pe_flag_sanitized)) {
                 printf("Only 'private' parameters to %s for unfencing %s changed\n",
                        rsc->id, node->details->uname);
             }
             data->rc = RSC_DIGEST_MATCH;
         }
     }
 
     if (data->rc == RSC_DIGEST_ALL && is_set(data_set->flags, pe_flag_sanitized) && data->digest_secure_calc) {
         if (is_set(data_set->flags, pe_flag_sanitized)) {
             printf("Parameters to %s for unfencing %s changed, try '%s:%s:%s'\n",
                    rsc->id, node->details->uname, rsc->id,
                    (const char *) g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE),
                    data->digest_secure_calc);
         }
     }
 
     free(key);
     free(search_all);
     free(search_secure);
 
     return data;
 }
 
 const char *rsc_printable_id(resource_t *rsc)
 {
     if (is_not_set(rsc->flags, pe_rsc_unique)) {
         return ID(rsc->xml);
     }
     return rsc->id;
 }
 
 void
 clear_bit_recursive(resource_t * rsc, unsigned long long flag)
 {
     GListPtr gIter = rsc->children;
 
     clear_bit(rsc->flags, flag);
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         clear_bit_recursive(child_rsc, flag);
     }
 }
 
 void
 set_bit_recursive(resource_t * rsc, unsigned long long flag)
 {
     GListPtr gIter = rsc->children;
 
     set_bit(rsc->flags, flag);
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         set_bit_recursive(child_rsc, flag);
     }
 }
 
 static GListPtr
 find_unfencing_devices(GListPtr candidates, GListPtr matches) 
 {
     for (GListPtr gIter = candidates; gIter != NULL; gIter = gIter->next) {
         resource_t *candidate = gIter->data;
         const char *provides = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_PROVIDES);
         const char *requires = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_REQUIRES);
 
         if(candidate->children) {
             matches = find_unfencing_devices(candidate->children, matches);
         } else if (is_not_set(candidate->flags, pe_rsc_fence_device)) {
             continue;
 
         } else if (crm_str_eq(provides, "unfencing", FALSE) || crm_str_eq(requires, "unfencing", FALSE)) {
             matches = g_list_prepend(matches, candidate);
         }
     }
     return matches;
 }
 
 
 action_t *
 pe_fence_op(node_t * node, const char *op, bool optional, const char *reason, pe_working_set_t * data_set)
 {
     char *op_key = NULL;
     action_t *stonith_op = NULL;
 
     if(op == NULL) {
         op = data_set->stonith_action;
     }
 
     op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
 
     if(data_set->singletons) {
         stonith_op = g_hash_table_lookup(data_set->singletons, op_key);
     }
 
     if(stonith_op == NULL) {
         stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
 
         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
         add_hash_param(stonith_op->meta, "stonith_action", op);
 
         if(is_remote_node(node) && is_set(data_set->flags, pe_flag_enable_unfencing)) {
             /* Extra work to detect device changes on remotes
              *
              * We may do this for all nodes in the future, but for now
              * the check_action_definition() based stuff works fine.
              *
              * Use "stonith-on" to avoid creating cache entries for
              * operations check_action_definition() would look for.
              */
             long max = 1024;
             long digests_all_offset = 0;
             long digests_secure_offset = 0;
 
             char *digests_all = malloc(max);
             char *digests_secure = malloc(max);
             GListPtr matches = find_unfencing_devices(data_set->resources, NULL);
 
             for (GListPtr gIter = matches; gIter != NULL; gIter = gIter->next) {
                 resource_t *match = gIter->data;
                 op_digest_cache_t *data = fencing_action_digest_cmp(match, node, data_set);
 
                 if(data->rc == RSC_DIGEST_ALL) {
                     optional = FALSE;
                     crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
                     if (is_set(data_set->flags, pe_flag_sanitized)) {
                         /* Extra detail for those running from the commandline */
                         fprintf(stdout, "  notice: Unfencing %s (remote): because the definition of %s changed\n", node->details->uname, match->id);
                     }
                 }
 
                 digests_all_offset += snprintf(
                     digests_all+digests_all_offset, max-digests_all_offset,
                     "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_all_calc);
 
                 digests_secure_offset += snprintf(
                     digests_secure+digests_secure_offset, max-digests_secure_offset,
                     "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_secure_calc);
             }
             g_hash_table_insert(stonith_op->meta,
                                 strdup(XML_OP_ATTR_DIGESTS_ALL),
                                 digests_all);
             g_hash_table_insert(stonith_op->meta,
                                 strdup(XML_OP_ATTR_DIGESTS_SECURE),
                                 digests_secure);
         }
 
     } else {
         free(op_key);
     }
 
     if(optional == FALSE && pe_can_fence(data_set, node)) {
         pe_action_required(stonith_op, NULL, reason);
     } else if(reason && stonith_op->reason == NULL) {
         stonith_op->reason = strdup(reason);
     }
 
     return stonith_op;
 }
 
 void
 trigger_unfencing(
     resource_t * rsc, node_t *node, const char *reason, action_t *dependency, pe_working_set_t * data_set) 
 {
     if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
         /* No resources require it */
         return;
 
     } else if (rsc != NULL && is_not_set(rsc->flags, pe_rsc_fence_device)) {
         /* Wasn't a stonith device */
         return;
 
     } else if(node
               && node->details->online
               && node->details->unclean == FALSE
               && node->details->shutdown == FALSE) {
         action_t *unfence = pe_fence_op(node, "on", FALSE, reason, data_set);
 
         if(dependency) {
             order_actions(unfence, dependency, pe_order_optional);
         }
 
     } else if(rsc) {
         GHashTableIter iter;
 
         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
             if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
                 trigger_unfencing(rsc, node, reason, dependency, data_set);
             }
         }
     }
 }
 
 gboolean
 add_tag_ref(GHashTable * tags, const char * tag_name,  const char * obj_ref)
 {
     tag_t *tag = NULL;
     GListPtr gIter = NULL;
     gboolean is_existing = FALSE;
 
     CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
 
     tag = g_hash_table_lookup(tags, tag_name);
     if (tag == NULL) {
         tag = calloc(1, sizeof(tag_t));
         if (tag == NULL) {
             return FALSE;
         }
         tag->id = strdup(tag_name);
         tag->refs = NULL;
         g_hash_table_insert(tags, strdup(tag_name), tag);
     }
 
     for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
         const char *existing_ref = (const char *) gIter->data;
 
         if (crm_str_eq(existing_ref, obj_ref, TRUE)){
             is_existing = TRUE;
             break;
         }
     }
 
     if (is_existing == FALSE) {
         tag->refs = g_list_append(tag->refs, strdup(obj_ref));
         crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
     }
 
     return TRUE;
 }
 
 void pe_action_set_flag_reason(const char *function, long line,
                                pe_action_t *action, pe_action_t *reason, const char *text,
                                enum pe_action_flags flags, bool overwrite)
 {
     bool unset = FALSE;
     bool update = FALSE;
     const char *change = NULL;
 
     if(is_set(flags, pe_action_runnable)) {
         unset = TRUE;
         change = "unrunnable";
     } else if(is_set(flags, pe_action_optional)) {
         unset = TRUE;
         change = "required";
     } else if(is_set(flags, pe_action_migrate_runnable)) {
         unset = TRUE;
         overwrite = TRUE;
         change = "unrunnable";
     } else if(is_set(flags, pe_action_dangle)) {
         change = "dangling";
     } else if(is_set(flags, pe_action_requires_any)) {
         change = "required";
     } else {
         crm_err("Unknown flag change to %s by %s: 0x%.16x",
                 flags, action->uuid, (reason? reason->uuid : 0));
     }
 
     if(unset) {
         if(is_set(action->flags, flags)) {
             action->flags = crm_clear_bit(function, line, action->uuid, action->flags, flags);
             update = TRUE;
         }
 
     } else {
         if(is_not_set(action->flags, flags)) {
             action->flags = crm_set_bit(function, line, action->uuid, action->flags, flags);
             update = TRUE;
         }
     }
 
     if((change && update) || text) {
         char *reason_text = NULL;
         if(reason == NULL) {
             pe_action_set_reason(action, text, overwrite);
 
         } else if(reason->rsc == NULL) {
             reason_text = crm_strdup_printf("%s %s%c %s", change, reason->task, text?':':0, text?text:"");
         } else {
             reason_text = crm_strdup_printf("%s %s %s%c %s", change, reason->rsc->id, reason->task, text?':':0, text?text:"NA");
         }
 
         if(reason_text && action->rsc != reason->rsc) {
             pe_action_set_reason(action, reason_text, overwrite);
         }
         free(reason_text);
     }
  }
 
 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite) 
 {
     if(action->reason && overwrite) {
         pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'", action->uuid, action->reason, reason);
         free(action->reason);
         action->reason = NULL;
     }
     if(action->reason == NULL) {
         if(reason) {
             pe_rsc_trace(action->rsc, "Set %s reason to '%s'", action->uuid, reason);
             action->reason = strdup(reason);
         } else {
             action->reason = NULL;
         }
     }
 }