diff --git a/lib/common/utils.c b/lib/common/utils.c
index 83072c5739..3e3abd3964 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,2126 +1,2128 @@
 /*
  * 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 <dlfcn.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <ctype.h>
 #include <pwd.h>
 #include <time.h>
 #include <libgen.h>
 #include <signal.h>
 
 #include <qb/qbdefs.h>
 
 #include <crm/crm.h>
 #include <crm/lrmd.h>
 #include <crm/services.h>
 #include <crm/msg_xml.h>
 #include <crm/cib/internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 #include <crm/common/ipc.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/mainloop.h>
 #include <crm/attrd.h>
 #include <libxml2/libxml/relaxng.h>
 
 #ifndef MAXLINE
 #  define MAXLINE 512
 #endif
 
 #ifdef HAVE_GETOPT_H
 #  include <getopt.h>
 #endif
 
 #ifndef PW_BUFFER_LEN
 #  define PW_BUFFER_LEN		500
 #endif
 
 CRM_TRACE_INIT_DATA(common);
 
 gboolean crm_config_error = FALSE;
 gboolean crm_config_warning = FALSE;
 char *crm_system_name = NULL;
 
 int node_score_red = 0;
 int node_score_green = 0;
 int node_score_yellow = 0;
 int node_score_infinity = INFINITY;
 
 static struct crm_option *crm_long_options = NULL;
 static const char *crm_app_description = NULL;
 static char *crm_short_options = NULL;
 static const char *crm_app_usage = NULL;
 
 int
 crm_exit(int rc)
 {
     mainloop_cleanup();
 
 #if HAVE_LIBXML2
     crm_trace("cleaning up libxml");
     crm_xml_cleanup();
 #endif
 
     crm_trace("exit %d", rc);
     qb_log_fini();
 
     free(crm_short_options);
     free(crm_system_name);
 
     exit(ABS(rc)); /* Always exit with a positive value so that it can be passed to crm_error
                     *
                     * Otherwise the system wraps it around and people
                     * have to jump through hoops figuring out what the
                     * error was
                     */
     return rc;     /* Can never happen, but allows return crm_exit(rc)
                     * where "return rc" was used previously - which
                     * keeps compilers happy.
                     */
 }
 
 gboolean
 check_time(const char *value)
 {
     if (crm_get_msec(value) < 5000) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_timer(const char *value)
 {
     if (crm_get_msec(value) < 0) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_boolean(const char *value)
 {
     int tmp = FALSE;
 
     if (crm_str_to_boolean(value, &tmp) != 1) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_number(const char *value)
 {
     errno = 0;
     if (value == NULL) {
         return FALSE;
 
     } else if (safe_str_eq(value, MINUS_INFINITY_S)) {
 
     } else if (safe_str_eq(value, INFINITY_S)) {
 
     } else {
         crm_int_helper(value, NULL);
     }
 
     if (errno != 0) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_quorum(const char *value)
 {
     if (safe_str_eq(value, "stop")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "freeze")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "ignore")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "suicide")) {
         return TRUE;
     }
     return FALSE;
 }
 
 gboolean
 check_script(const char *value)
 {
     struct stat st;
 
     if(safe_str_eq(value, "/dev/null")) {
         return TRUE;
     }
 
     if(stat(value, &st) != 0) {
         crm_err("Script %s does not exist", value);
         return FALSE;
     }
 
     if(S_ISREG(st.st_mode) == 0) {
         crm_err("Script %s is not a regular file", value);
         return FALSE;
     }
 
     if( (st.st_mode & (S_IXUSR | S_IXGRP )) == 0) {
         crm_err("Script %s is not executable", value);
         return FALSE;
     }
 
     return TRUE;
 }
 
 gboolean
 check_utilization(const char *value)
 {
     char *end = NULL;
     long number = strtol(value, &end, 10);
 
     if(end && end[0] != '%') {
         return FALSE;
     } else if(number < 0) {
         return FALSE;
     }
 
     return TRUE;
 }
 
 int
 char2score(const char *score)
 {
     int score_f = 0;
 
     if (score == NULL) {
 
     } else if (safe_str_eq(score, MINUS_INFINITY_S)) {
         score_f = -node_score_infinity;
 
     } else if (safe_str_eq(score, INFINITY_S)) {
         score_f = node_score_infinity;
 
     } else if (safe_str_eq(score, "+" INFINITY_S)) {
         score_f = node_score_infinity;
 
     } else if (safe_str_eq(score, "red")) {
         score_f = node_score_red;
 
     } else if (safe_str_eq(score, "yellow")) {
         score_f = node_score_yellow;
 
     } else if (safe_str_eq(score, "green")) {
         score_f = node_score_green;
 
     } else {
         score_f = crm_parse_int(score, NULL);
         if (score_f > 0 && score_f > node_score_infinity) {
             score_f = node_score_infinity;
 
         } else if (score_f < 0 && score_f < -node_score_infinity) {
             score_f = -node_score_infinity;
         }
     }
 
     return score_f;
 }
 
 char *
 score2char_stack(int score, char *buf, size_t len)
 {
     if (score >= node_score_infinity) {
         strncpy(buf, INFINITY_S, 9);
     } else if (score <= -node_score_infinity) {
         strncpy(buf, MINUS_INFINITY_S , 10);
     } else {
         return crm_itoa_stack(score, buf, len);
     }
 
     return buf;
 }
 
 char *
 score2char(int score)
 {
     if (score >= node_score_infinity) {
         return strdup(INFINITY_S);
 
     } else if (score <= -node_score_infinity) {
         return strdup("-" INFINITY_S);
     }
     return crm_itoa(score);
 }
 
 const char *
 cluster_option(GHashTable * options, gboolean(*validate) (const char *),
                const char *name, const char *old_name, const char *def_value)
 {
     const char *value = NULL;
 
     CRM_ASSERT(name != NULL);
 
     if (options != NULL) {
         value = g_hash_table_lookup(options, name);
     }
 
     if (value == NULL && old_name && options != NULL) {
         value = g_hash_table_lookup(options, old_name);
         if (value != NULL) {
             crm_config_warn("Using deprecated name '%s' for"
                             " cluster option '%s'", old_name, name);
             g_hash_table_insert(options, strdup(name), strdup(value));
             value = g_hash_table_lookup(options, old_name);
         }
     }
 
     if (value == NULL) {
         crm_trace("Using default value '%s' for cluster option '%s'", def_value, name);
 
         if (options == NULL) {
             return def_value;
 
         } else if(def_value == NULL) {
             return def_value;
         }
 
         g_hash_table_insert(options, strdup(name), strdup(def_value));
         value = g_hash_table_lookup(options, name);
     }
 
     if (validate && validate(value) == FALSE) {
         crm_config_err("Value '%s' for cluster option '%s' is invalid."
                        "  Defaulting to %s", value, name, def_value);
         g_hash_table_replace(options, strdup(name), strdup(def_value));
         value = g_hash_table_lookup(options, name);
     }
 
     return value;
 }
 
 const char *
 get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name)
 {
     int lpc = 0;
     const char *value = NULL;
     gboolean found = FALSE;
 
     for (lpc = 0; lpc < len; lpc++) {
         if (safe_str_eq(name, option_list[lpc].name)) {
             found = TRUE;
             value = cluster_option(options,
                                    option_list[lpc].is_valid,
                                    option_list[lpc].name,
                                    option_list[lpc].alt_name, option_list[lpc].default_value);
         }
     }
     CRM_CHECK(found, crm_err("No option named: %s", name));
     return value;
 }
 
 void
 config_metadata(const char *name, const char *version, const char *desc_short,
                 const char *desc_long, pe_cluster_option * option_list, int len)
 {
     int lpc = 0;
 
     fprintf(stdout, "<?xml version=\"1.0\"?>"
             "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
             "<resource-agent name=\"%s\">\n"
             "  <version>%s</version>\n"
             "  <longdesc lang=\"en\">%s</longdesc>\n"
             "  <shortdesc lang=\"en\">%s</shortdesc>\n"
             "  <parameters>\n", name, version, desc_long, desc_short);
 
     for (lpc = 0; lpc < len; lpc++) {
         if (option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) {
             continue;
         }
         fprintf(stdout, "    <parameter name=\"%s\" unique=\"0\">\n"
                 "      <shortdesc lang=\"en\">%s</shortdesc>\n"
                 "      <content type=\"%s\" default=\"%s\"/>\n"
                 "      <longdesc lang=\"en\">%s%s%s</longdesc>\n"
                 "    </parameter>\n",
                 option_list[lpc].name,
                 option_list[lpc].description_short,
                 option_list[lpc].type,
                 option_list[lpc].default_value,
                 option_list[lpc].description_long ? option_list[lpc].
                 description_long : option_list[lpc].description_short,
                 option_list[lpc].values ? "  Allowed values: " : "",
                 option_list[lpc].values ? option_list[lpc].values : "");
     }
     fprintf(stdout, "  </parameters>\n</resource-agent>\n");
 }
 
 void
 verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len)
 {
     int lpc = 0;
 
     for (lpc = 0; lpc < len; lpc++) {
         cluster_option(options,
                        option_list[lpc].is_valid,
                        option_list[lpc].name,
                        option_list[lpc].alt_name, option_list[lpc].default_value);
     }
 }
 
 char *
 generate_hash_key(const char *crm_msg_reference, const char *sys)
 {
     char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_');
 
     crm_trace("created hash key: (%s)", hash_key);
     return hash_key;
 }
 
 
 int
 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
 {
     int rc = -1;
     char *buffer = NULL;
     struct passwd pwd;
     struct passwd *pwentry = NULL;
 
     buffer = calloc(1, PW_BUFFER_LEN);
     getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
     if (pwentry) {
         rc = 0;
         if (uid) {
             *uid = pwentry->pw_uid;
         }
         if (gid) {
             *gid = pwentry->pw_gid;
         }
         crm_trace("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
 
     } else {
         crm_err("Cluster user %s does not exist", name);
     }
 
     free(buffer);
     return rc;
 }
 
 static int
 crm_version_helper(const char *text, char **end_text)
 {
     int atoi_result = -1;
 
     CRM_ASSERT(end_text != NULL);
 
     errno = 0;
 
     if (text != NULL && text[0] != 0) {
         atoi_result = (int)strtol(text, end_text, 10);
 
         if (errno == EINVAL) {
             crm_err("Conversion of '%s' %c failed", text, text[0]);
             atoi_result = -1;
         }
     }
     return atoi_result;
 }
 
 /*
  * version1 < version2 : -1
  * version1 = version2 :  0
  * version1 > version2 :  1
  */
 int
 compare_version(const char *version1, const char *version2)
 {
     int rc = 0;
     int lpc = 0;
     char *ver1_copy = NULL, *ver2_copy = NULL;
     char *rest1 = NULL, *rest2 = NULL;
 
     if (version1 == NULL && version2 == NULL) {
         return 0;
     } else if (version1 == NULL) {
         return -1;
     } else if (version2 == NULL) {
         return 1;
     }
 
     ver1_copy = strdup(version1);
     ver2_copy = strdup(version2);
     rest1 = ver1_copy;
     rest2 = ver2_copy;
 
     while (1) {
         int digit1 = 0;
         int digit2 = 0;
 
         lpc++;
 
         if (rest1 == rest2) {
             break;
         }
 
         if (rest1 != NULL) {
             digit1 = crm_version_helper(rest1, &rest1);
         }
 
         if (rest2 != NULL) {
             digit2 = crm_version_helper(rest2, &rest2);
         }
 
         if (digit1 < digit2) {
             rc = -1;
             break;
 
         } else if (digit1 > digit2) {
             rc = 1;
             break;
         }
 
         if (rest1 != NULL && rest1[0] == '.') {
             rest1++;
         }
         if (rest1 != NULL && rest1[0] == 0) {
             rest1 = NULL;
         }
 
         if (rest2 != NULL && rest2[0] == '.') {
             rest2++;
         }
         if (rest2 != NULL && rest2[0] == 0) {
             rest2 = NULL;
         }
     }
 
     free(ver1_copy);
     free(ver2_copy);
 
     if (rc == 0) {
         crm_trace("%s == %s (%d)", version1, version2, lpc);
     } else if (rc < 0) {
         crm_trace("%s < %s (%d)", version1, version2, lpc);
     } else if (rc > 0) {
         crm_trace("%s > %s (%d)", version1, version2, lpc);
     }
 
     return rc;
 }
 
 gboolean do_stderr = FALSE;
 
 #ifndef NUMCHARS
 #  define	NUMCHARS	"0123456789."
 #endif
 
 #ifndef WHITESPACE
 #  define	WHITESPACE	" \t\n\r\f"
 #endif
 
 unsigned long long
 crm_get_interval(const char *input)
 {
     unsigned long long msec = 0;
 
     if (input == NULL) {
         return msec;
 
     } else if (input[0] != 'P') {
         long long tmp = crm_get_msec(input);
 
         if(tmp > 0) {
             msec = tmp;
         }
 
     } else {
         crm_time_t *interval = crm_time_parse_duration(input);
 
         msec = 1000 * crm_time_get_seconds(interval);
         crm_time_free(interval);
     }
 
     return msec;
 }
 
 long long
 crm_get_msec(const char *input)
 {
     const char *cp = input;
     const char *units;
     long long multiplier = 1000;
     long long divisor = 1;
     long long msec = -1;
     char *end_text = NULL;
 
     /* double dret; */
 
     if (input == NULL) {
         return msec;
     }
 
     cp += strspn(cp, WHITESPACE);
     units = cp + strspn(cp, NUMCHARS);
     units += strspn(units, WHITESPACE);
 
     if (strchr(NUMCHARS, *cp) == NULL) {
         return msec;
     }
 
     if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) {
         multiplier = 1;
         divisor = 1;
     } else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) {
         multiplier = 1;
         divisor = 1000;
     } else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) {
         multiplier = 1000;
         divisor = 1;
     } else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) {
         multiplier = 60 * 1000;
         divisor = 1;
     } else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) {
         multiplier = 60 * 60 * 1000;
         divisor = 1;
     } else if (*units != EOS && *units != '\n' && *units != '\r') {
         return msec;
     }
 
     msec = crm_int_helper(cp, &end_text);
     if (msec > LLONG_MAX/multiplier) {
         /* arithmetics overflow while multiplier/divisor mutually exclusive */
         return LLONG_MAX;
     }
     msec *= multiplier;
     msec /= divisor;
     /* dret += 0.5; */
     /* msec = (long long)dret; */
     return msec;
 }
 
 char *
 generate_op_key(const char *rsc_id, const char *op_type, int interval)
 {
     int len = 35;
     char *op_id = NULL;
 
     CRM_CHECK(rsc_id != NULL, return NULL);
     CRM_CHECK(op_type != NULL, return NULL);
 
     len += strlen(op_type);
     len += strlen(rsc_id);
     op_id = malloc(len);
     CRM_CHECK(op_id != NULL, return NULL);
     sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
     return op_id;
 }
 
 gboolean
 parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
 {
     char *notify = NULL;
     char *mutable_key = NULL;
     char *mutable_key_ptr = NULL;
     int len = 0, offset = 0, ch = 0;
 
     CRM_CHECK(key != NULL, return FALSE);
 
     *interval = 0;
     len = strlen(key);
     offset = len - 1;
 
     crm_trace("Source: %s", key);
 
     while (offset > 0 && isdigit(key[offset])) {
         int digits = len - offset;
 
         ch = key[offset] - '0';
         CRM_CHECK(ch < 10, return FALSE);
         CRM_CHECK(ch >= 0, return FALSE);
         while (digits > 1) {
             digits--;
             ch = ch * 10;
         }
         *interval += ch;
         offset--;
     }
 
     crm_trace("  Interval: %d", *interval);
     CRM_CHECK(key[offset] == '_', return FALSE);
 
     mutable_key = strdup(key);
     mutable_key[offset] = 0;
     offset--;
 
     while (offset > 0 && key[offset] != '_') {
         offset--;
     }
 
     CRM_CHECK(key[offset] == '_', free(mutable_key);
               return FALSE);
 
     mutable_key_ptr = mutable_key + offset + 1;
 
     crm_trace("  Action: %s", mutable_key_ptr);
 
     *op_type = strdup(mutable_key_ptr);
 
     mutable_key[offset] = 0;
     offset--;
 
     CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key);
               return FALSE);
 
     notify = strstr(mutable_key, "_post_notify");
     if (notify && safe_str_eq(notify, "_post_notify")) {
         notify[0] = 0;
     }
 
     notify = strstr(mutable_key, "_pre_notify");
     if (notify && safe_str_eq(notify, "_pre_notify")) {
         notify[0] = 0;
     }
 
     crm_trace("  Resource: %s", mutable_key);
     *rsc_id = mutable_key;
 
     return TRUE;
 }
 
 char *
 generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
 {
     int len = 12;
     char *op_id = NULL;
 
     CRM_CHECK(rsc_id != NULL, return NULL);
     CRM_CHECK(op_type != NULL, return NULL);
     CRM_CHECK(notify_type != NULL, return NULL);
 
     len += strlen(op_type);
     len += strlen(rsc_id);
     len += strlen(notify_type);
     if(len > 0) {
         op_id = malloc(len);
     }
     if (op_id != NULL) {
         sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
     }
     return op_id;
 }
 
 char *
 generate_transition_magic_v202(const char *transition_key, int op_status)
 {
     int len = 80;
     char *fail_state = NULL;
 
     CRM_CHECK(transition_key != NULL, return NULL);
 
     len += strlen(transition_key);
 
     fail_state = malloc(len);
     if (fail_state != NULL) {
         snprintf(fail_state, len, "%d:%s", op_status, transition_key);
     }
     return fail_state;
 }
 
 char *
 generate_transition_magic(const char *transition_key, int op_status, int op_rc)
 {
     int len = 80;
     char *fail_state = NULL;
 
     CRM_CHECK(transition_key != NULL, return NULL);
 
     len += strlen(transition_key);
 
     fail_state = malloc(len);
     if (fail_state != NULL) {
         snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key);
     }
     return fail_state;
 }
 
 gboolean
 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
                         int *op_status, int *op_rc, int *target_rc)
 {
     int res = 0;
     char *key = NULL;
     gboolean result = TRUE;
 
     CRM_CHECK(magic != NULL, return FALSE);
     CRM_CHECK(op_rc != NULL, return FALSE);
     CRM_CHECK(op_status != NULL, return FALSE);
 
     key = calloc(1, strlen(magic) + 1);
     res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
     if (res != 3) {
         crm_warn("Only found %d items in: '%s'", res, magic);
         free(key);
         return FALSE;
     }
 
     CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE);
 
     free(key);
     return result;
 }
 
 char *
 generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
 {
     int len = 40;
     char *fail_state = NULL;
 
     CRM_CHECK(node != NULL, return NULL);
 
     len += strlen(node);
 
     fail_state = malloc(len);
     if (fail_state != NULL) {
         snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node);
     }
     return fail_state;
 }
 
 gboolean
 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
                       int *target_rc)
 {
     int res = 0;
     gboolean done = FALSE;
 
     CRM_CHECK(uuid != NULL, return FALSE);
     CRM_CHECK(target_rc != NULL, return FALSE);
     CRM_CHECK(action_id != NULL, return FALSE);
     CRM_CHECK(transition_id != NULL, return FALSE);
 
     *uuid = calloc(1, 37);
     res = sscanf(key, "%d:%d:%d:%36s", action_id, transition_id, target_rc, *uuid);
     switch (res) {
         case 4:
             /* Post Pacemaker 0.6 */
             done = TRUE;
             break;
         case 3:
         case 2:
             /* this can be tricky - the UUID might start with an integer */
 
             /* Until Pacemaker 0.6 */
             done = TRUE;
             *target_rc = -1;
             res = sscanf(key, "%d:%d:%36s", action_id, transition_id, *uuid);
             if (res == 2) {
                 *action_id = -1;
                 res = sscanf(key, "%d:%36s", transition_id, *uuid);
                 CRM_CHECK(res == 2, done = FALSE);
 
             } else if (res != 3) {
                 CRM_CHECK(res == 3, done = FALSE);
             }
             break;
 
         case 1:
             /* Prior to Heartbeat 2.0.8 */
             done = TRUE;
             *action_id = -1;
             *target_rc = -1;
             res = sscanf(key, "%d:%36s", transition_id, *uuid);
             CRM_CHECK(res == 2, done = FALSE);
             break;
         default:
             crm_crit("Unhandled sscanf result (%d) for %s", res, key);
     }
 
     if (strlen(*uuid) != 36) {
         crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
     }
 
     if (done == FALSE) {
         crm_err("Cannot decode '%s' rc=%d", key, res);
 
         free(*uuid);
         *uuid = NULL;
         *target_rc = -1;
         *action_id = -1;
         *transition_id = -1;
     }
 
     return done;
 }
 
 void
 filter_action_parameters(xmlNode * param_set, const char *version)
 {
     char *key = NULL;
     char *timeout = NULL;
     char *interval = NULL;
 
     const char *attr_filter[] = {
         XML_ATTR_ID,
         XML_ATTR_CRM_VERSION,
         XML_LRM_ATTR_OP_DIGEST,
+        XML_LRM_ATTR_TARGET,
+        XML_LRM_ATTR_TARGET_UUID,
     };
 
     gboolean do_delete = FALSE;
     int lpc = 0;
     static int meta_len = 0;
 
     if (meta_len == 0) {
         meta_len = strlen(CRM_META);
     }
 
     if (param_set == NULL) {
         return;
     }
 
     for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
         xml_remove_prop(param_set, attr_filter[lpc]);
     }
 
     key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
     interval = crm_element_value_copy(param_set, key);
     free(key);
 
     key = crm_meta_name(XML_ATTR_TIMEOUT);
     timeout = crm_element_value_copy(param_set, key);
 
     if (param_set) {
         xmlAttrPtr xIter = param_set->properties;
 
         while (xIter) {
             const char *prop_name = (const char *)xIter->name;
 
             xIter = xIter->next;
             do_delete = FALSE;
             if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
                 do_delete = TRUE;
             }
 
             if (do_delete) {
                 xml_remove_prop(param_set, prop_name);
             }
         }
     }
 
     if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
         /* Re-instate the operation's timeout value */
         if (timeout != NULL) {
             crm_xml_add(param_set, key, timeout);
         }
     }
 
     free(interval);
     free(timeout);
     free(key);
 }
 
 extern bool crm_is_daemon;
 
 /* coverity[+kill] */
 void
 crm_abort(const char *file, const char *function, int line,
           const char *assert_condition, gboolean do_core, gboolean do_fork)
 {
     int rc = 0;
     int pid = 0;
     int status = 0;
 
     /* Implied by the parent's error logging below */
     /* crm_write_blackbox(0); */
 
     if(crm_is_daemon == FALSE) {
         /* This is a command line tool - do not fork */
 
         /* crm_add_logfile(NULL);   * Record it to a file? */
         crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */
         do_fork = FALSE;         /* Just crash if needed */
     }
 
     if (do_core == FALSE) {
         crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
         return;
 
     } else if (do_fork) {
         pid = fork();
 
     } else {
         crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
     }
 
     if (pid == -1) {
         crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
                  function, file, line, assert_condition);
         return;
 
     } else if(pid == 0) {
         /* Child process */
         abort();
         return;
     }
 
     /* Parent process */
     crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
             function, pid, file, line, assert_condition);
     crm_write_blackbox(SIGTRAP, NULL);
 
     do {
         rc = waitpid(pid, &status, 0);
         if(rc == pid) {
             return; /* Job done */
         }
 
     } while(errno == EINTR);
 
     if (errno == ECHILD) {
         /* crm_mon does this */
         crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid);
         return;
     }
     crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid);
 }
 
 int
 crm_pid_active(long pid, const char *daemon)
 {
     static int have_proc_pid = 0;
 
     if(have_proc_pid == 0) {
         char proc_path[PATH_MAX], exe_path[PATH_MAX];
 
         /* check to make sure pid hasn't been reused by another process */
         snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
 
         have_proc_pid = 1;
         if(readlink(proc_path, exe_path, PATH_MAX - 1) < 0) {
             have_proc_pid = -1;
         }
     }
 
     if (pid <= 0) {
         return -1;
 
     } else if (kill(pid, 0) < 0 && errno == ESRCH) {
         return 0;
 
     } else if(daemon == NULL || have_proc_pid == -1) {
         return 1;
 
     } else {
         int rc = 0;
         char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
 
         /* check to make sure pid hasn't been reused by another process */
         snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
 
         rc = readlink(proc_path, exe_path, PATH_MAX - 1);
         if (rc < 0 && errno == EACCES) {
             crm_perror(LOG_INFO, "Could not read from %s", proc_path);
             return 1;
         } else if (rc < 0) {
             crm_perror(LOG_ERR, "Could not read from %s", proc_path);
             return 0;
         }
         
 
         exe_path[rc] = 0;
 
         if(daemon[0] != '/') {
             rc = snprintf(myexe_path, sizeof(proc_path), CRM_DAEMON_DIR"/%s", daemon);
             myexe_path[rc] = 0;
         } else {
             rc = snprintf(myexe_path, sizeof(proc_path), "%s", daemon);
             myexe_path[rc] = 0;
         }
         
         if (strcmp(exe_path, myexe_path) == 0) {
             return 1;
         }
     }
 
     return 0;
 }
 
 #define	LOCKSTRLEN	11
 
 long
 crm_read_pidfile(const char *filename)
 {
     int fd;
     struct stat sbuf;
     long pid = -ENOENT;
     char buf[LOCKSTRLEN + 1];
 
     if ((fd = open(filename, O_RDONLY)) < 0) {
         goto bail;
     }
 
     if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
         sleep(2);           /* if someone was about to create one,
                              * give'm a sec to do so
                              */
     }
 
     if (read(fd, buf, sizeof(buf)) < 1) {
         goto bail;
     }
 
     if (sscanf(buf, "%lu", &pid) > 0) {
         if (pid <= 0) {
             pid = -ESRCH;
         } else {
             crm_trace("Got pid %lu from %s\n", pid, filename);
         }
     }
 
   bail:
     if (fd >= 0) {
         close(fd);
     }
     return pid;
 }
 
 long
 crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
 {
     long pid = crm_read_pidfile(filename);
 
     if (pid < 2) {
         /* Invalid pid */
         pid = -ENOENT;
         unlink(filename);
 
     } else if (mypid && pid == mypid) {
         /* In use by us */
         pid = pcmk_ok;
 
     } else if (crm_pid_active(pid, daemon) == FALSE) {
         /* Contains a stale value */
         unlink(filename);
         pid = -ENOENT;
 
     } else if (mypid && pid != mypid) {
         /* locked by existing process - give up */
         pid = -EEXIST;
     }
 
     return pid;
 }
 
 static int
 crm_lock_pidfile(const char *filename, const char *name)
 {
     long mypid = 0;
     int fd = 0, rc = 0;
     char buf[LOCKSTRLEN + 1];
 
     mypid = (unsigned long)getpid();
 
     rc = crm_pidfile_inuse(filename, 0, name);
     if (rc == -ENOENT) {
         /* exists but the process is not active */
 
     } else if (rc != pcmk_ok) {
         /* locked by existing process - give up */
         return rc;
     }
 
     if ((fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
         /* Hmmh, why did we fail? Anyway, nothing we can do about it */
         return -errno;
     }
 
     snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN - 1, mypid);
     rc = write(fd, buf, LOCKSTRLEN);
     close(fd);
 
     if (rc != LOCKSTRLEN) {
         crm_perror(LOG_ERR, "Incomplete write to %s", filename);
         return -errno;
     }
 
     return crm_pidfile_inuse(filename, mypid, name);
 }
 
 void
 crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
 {
     int rc;
     long pid;
     const char *devnull = "/dev/null";
 
     if (daemonize == FALSE) {
         return;
     }
 
     /* Check before we even try... */
     rc = crm_pidfile_inuse(pidfile, 1, name);
     if(rc < pcmk_ok && rc != -ENOENT) {
         pid = crm_read_pidfile(pidfile);
         crm_err("%s: already running [pid %ld in %s]", name, pid, pidfile);
         printf("%s: already running [pid %ld in %s]\n", name, pid, pidfile);
         crm_exit(rc);
     }
 
     pid = fork();
     if (pid < 0) {
         fprintf(stderr, "%s: could not start daemon\n", name);
         crm_perror(LOG_ERR, "fork");
         crm_exit(EINVAL);
 
     } else if (pid > 0) {
         crm_exit(pcmk_ok);
     }
 
     rc = crm_lock_pidfile(pidfile, name);
     if(rc < pcmk_ok) {
         crm_err("Could not lock '%s' for %s: %s (%d)", pidfile, name, pcmk_strerror(rc), rc);
         printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_strerror(rc), rc);
         crm_exit(rc);
     }
 
     umask(S_IWGRP | S_IWOTH | S_IROTH);
 
     close(STDIN_FILENO);
     (void)open(devnull, O_RDONLY);      /* Stdin:  fd 0 */
     close(STDOUT_FILENO);
     (void)open(devnull, O_WRONLY);      /* Stdout: fd 1 */
     close(STDERR_FILENO);
     (void)open(devnull, O_WRONLY);      /* Stderr: fd 2 */
 }
 
 char *
 crm_meta_name(const char *field)
 {
     int lpc = 0;
     int max = 0;
     char *crm_name = NULL;
 
     CRM_CHECK(field != NULL, return NULL);
     crm_name = crm_concat(CRM_META, field, '_');
 
     /* Massage the names so they can be used as shell variables */
     max = strlen(crm_name);
     for (; lpc < max; lpc++) {
         switch (crm_name[lpc]) {
             case '-':
                 crm_name[lpc] = '_';
                 break;
         }
     }
     return crm_name;
 }
 
 const char *
 crm_meta_value(GHashTable * hash, const char *field)
 {
     char *key = NULL;
     const char *value = NULL;
 
     key = crm_meta_name(field);
     if (key) {
         value = g_hash_table_lookup(hash, key);
         free(key);
     }
 
     return value;
 }
 
 static struct option *
 crm_create_long_opts(struct crm_option *long_options)
 {
     struct option *long_opts = NULL;
 
 #ifdef HAVE_GETOPT_H
     int index = 0, lpc = 0;
 
     /*
      * A previous, possibly poor, choice of '?' as the short form of --help
      * means that getopt_long() returns '?' for both --help and for "unknown option"
      *
      * This dummy entry allows us to differentiate between the two in crm_get_option()
      * and exit with the correct error code
      */
     long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = "__dummmy__";
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = '_';
     index++;
 
     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
         if (long_options[lpc].name[0] == '-') {
             continue;
         }
 
         long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
         /*fprintf(stderr, "Creating %d %s = %c\n", index,
          * long_options[lpc].name, long_options[lpc].val);      */
         long_opts[index].name = long_options[lpc].name;
         long_opts[index].has_arg = long_options[lpc].has_arg;
         long_opts[index].flag = long_options[lpc].flag;
         long_opts[index].val = long_options[lpc].val;
         index++;
     }
 
     /* Now create the list terminator */
     long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = NULL;
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = 0;
 #endif
 
     return long_opts;
 }
 
 void
 crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options,
                 const char *app_desc)
 {
     if (short_options) {
         crm_short_options = strdup(short_options);
 
     } else if (long_options) {
         int lpc = 0;
         int opt_string_len = 0;
         char *local_short_options = NULL;
 
         for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
             if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
                 local_short_options = realloc_safe(local_short_options, opt_string_len + 4);
                 local_short_options[opt_string_len++] = long_options[lpc].val;
                 /* getopt(3) says: Two colons mean an option takes an optional arg; */
                 if (long_options[lpc].has_arg == optional_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 if (long_options[lpc].has_arg >= required_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 local_short_options[opt_string_len] = 0;
             }
         }
         crm_short_options = local_short_options;
         crm_trace("Generated short option string: '%s'", local_short_options);
     }
 
     if (long_options) {
         crm_long_options = long_options;
     }
     if (app_desc) {
         crm_app_description = app_desc;
     }
     if (app_usage) {
         crm_app_usage = app_usage;
     }
 }
 
 int
 crm_get_option(int argc, char **argv, int *index)
 {
     return crm_get_option_long(argc, argv, index, NULL);
 }
 
 int
 crm_get_option_long(int argc, char **argv, int *index, const char **longname)
 {
 #ifdef HAVE_GETOPT_H
     static struct option *long_opts = NULL;
 
     if (long_opts == NULL && crm_long_options) {
         long_opts = crm_create_long_opts(crm_long_options);
     }
 
     *index = 0;
     if (long_opts) {
         int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
 
         switch (flag) {
             case 0:
                 if (long_opts[*index].val) {
                     return long_opts[*index].val;
                 } else if (longname) {
                     *longname = long_opts[*index].name;
                 } else {
                     crm_notice("Unhandled option --%s", long_opts[*index].name);
                     return flag;
                 }
             case -1:           /* End of option processing */
                 break;
             case ':':
                 crm_trace("Missing argument");
                 crm_help('?', 1);
                 break;
             case '?':
                 crm_help('?', *index ? 0 : 1);
                 break;
         }
         return flag;
     }
 #endif
 
     if (crm_short_options) {
         return getopt(argc, argv, crm_short_options);
     }
 
     return -1;
 }
 
 int
 crm_help(char cmd, int exit_code)
 {
     int i = 0;
     FILE *stream = (exit_code ? stderr : stdout);
 
     if (cmd == 'v' || cmd == '$') {
         fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
         fprintf(stream, "Written by Andrew Beekhof\n");
         goto out;
     }
 
     if (cmd == '!') {
         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
         goto out;
     }
 
     fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
 
     if (crm_app_usage) {
         fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
     }
 
     if (crm_long_options) {
         fprintf(stream, "Options:\n");
         for (i = 0; crm_long_options[i].name != NULL; i++) {
             if (crm_long_options[i].flags & pcmk_option_hidden) {
 
             } else if (crm_long_options[i].flags & pcmk_option_paragraph) {
                 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].flags & pcmk_option_example) {
                 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
                 fprintf(stream, "%s\n", crm_long_options[i].desc);
 
             } else {
                 /* is val printable as char ? */
                 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
                     fprintf(stream, " -%c,", crm_long_options[i].val);
                 } else {
                     fputs("    ", stream);
                 }
                 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
                         crm_long_options[i].has_arg == optional_argument ? "[=value]" :
                         crm_long_options[i].has_arg == required_argument ? "=value" : "",
                         crm_long_options[i].desc ? crm_long_options[i].desc : "");
             }
         }
 
     } else if (crm_short_options) {
         fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
         for (i = 0; crm_short_options[i] != 0; i++) {
             int has_arg = no_argument /* 0 */;
 
             if (crm_short_options[i + 1] == ':') {
                 if (crm_short_options[i + 2] == ':')
                     has_arg = optional_argument /* 2 */;
                 else
                     has_arg = required_argument /* 1 */;
             }
 
             fprintf(stream, " -%c %s\n", crm_short_options[i],
                     has_arg == optional_argument ? "[value]" :
                     has_arg == required_argument ? "{value}" : "");
             i += has_arg;
         }
     }
 
     fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
 
   out:
     return crm_exit(exit_code);
 }
 
 void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro,
         qb_ipcs_service_t **ipcs_rw,
         qb_ipcs_service_t **ipcs_shm,
         struct qb_ipcs_service_handlers *ro_cb,
         struct qb_ipcs_service_handlers *rw_cb)
 {
     *ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, ro_cb);
     *ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, rw_cb);
     *ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, rw_cb);
 
     if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
         crm_err("Failed to create cib servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(DAEMON_RESPAWN_STOP);
     }
 }
 
 void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
         qb_ipcs_service_t *ipcs_rw,
         qb_ipcs_service_t *ipcs_shm)
 {
     qb_ipcs_destroy(ipcs_ro);
     qb_ipcs_destroy(ipcs_rw);
     qb_ipcs_destroy(ipcs_shm);
 }
 
 qb_ipcs_service_t *
 crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb)
 {
     return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
 }
 
 void
 attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
 {
     *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
 
     if (*ipcs == NULL) {
         crm_err("Failed to create attrd servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(DAEMON_RESPAWN_STOP);
     }
 }
 
 void
 stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
 {
     *ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, cb);
 
     if (*ipcs == NULL) {
         crm_err("Failed to create stonith-ng servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(DAEMON_RESPAWN_STOP);
     }
 }
 
 int
 attrd_update_delegate(crm_ipc_t * ipc, char command, const char *host, const char *name,
                       const char *value, const char *section, const char *set, const char *dampen,
                       const char *user_name, int options)
 {
     int rc = -ENOTCONN;
     int max = 5;
     const char *task = NULL;
     const char *name_as = NULL;
     const char *display_host = (host ? host : "localhost");
     const char *display_command = NULL; /* for commands without name/value */
     xmlNode *update = create_xml_node(NULL, __FUNCTION__);
 
     static gboolean connected = TRUE;
     static crm_ipc_t *local_ipc = NULL;
     static enum crm_ipc_flags flags = crm_ipc_flags_none;
 
     if (ipc == NULL && local_ipc == NULL) {
         local_ipc = crm_ipc_new(T_ATTRD, 0);
         flags |= crm_ipc_client_response;
         connected = FALSE;
     }
 
     if (ipc == NULL) {
         ipc = local_ipc;
     }
 
     /* remap common aliases */
     if (safe_str_eq(section, "reboot")) {
         section = XML_CIB_TAG_STATUS;
 
     } else if (safe_str_eq(section, "forever")) {
         section = XML_CIB_TAG_NODES;
     }
 
     crm_xml_add(update, F_TYPE, T_ATTRD);
     crm_xml_add(update, F_ORIG, crm_system_name?crm_system_name:"unknown");
 
     if (name == NULL && command == 'U') {
         command = 'R';
     }
 
     switch (command) {
         case 'u':
             task = ATTRD_OP_UPDATE;
             name_as = F_ATTRD_REGEX;
             break;
         case 'D':
         case 'U':
         case 'v':
             task = ATTRD_OP_UPDATE;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'R':
             task = ATTRD_OP_REFRESH;
             display_command = "refresh";
             break;
         case 'B':
             task = ATTRD_OP_UPDATE_BOTH;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'Y':
             task = ATTRD_OP_UPDATE_DELAY;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'Q':
             task = ATTRD_OP_QUERY;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'C':
             task = ATTRD_OP_PEER_REMOVE;
             display_command = "purge";
             break;
     }
 
     if (name_as != NULL) {
         if (name == NULL) {
             rc = -EINVAL;
             goto done;
         }
         crm_xml_add(update, name_as, name);
     }
 
     crm_xml_add(update, F_ATTRD_TASK, task);
     crm_xml_add(update, F_ATTRD_VALUE, value);
     crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
     crm_xml_add(update, F_ATTRD_SECTION, section);
     crm_xml_add(update, F_ATTRD_HOST, host);
     crm_xml_add(update, F_ATTRD_SET, set);
     crm_xml_add_int(update, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
     crm_xml_add_int(update, F_ATTRD_IS_PRIVATE, is_set(options, attrd_opt_private));
 #if ENABLE_ACL
     if (user_name) {
         crm_xml_add(update, F_ATTRD_USER, user_name);
     }
 #endif
 
     while (max > 0) {
         if (connected == FALSE) {
             crm_info("Connecting to cluster... %d retries remaining", max);
             connected = crm_ipc_connect(ipc);
         }
 
         if (connected) {
             rc = crm_ipc_send(ipc, update, flags, 0, NULL);
         } else {
             crm_perror(LOG_INFO, "Connection to cluster attribute manager failed");
         }
 
         if (ipc != local_ipc) {
             break;
 
         } else if (rc > 0) {
             break;
 
         } else if (rc == -EAGAIN || rc == -EALREADY) {
             sleep(5 - max);
             max--;
 
         } else {
             crm_ipc_close(ipc);
             connected = FALSE;
             sleep(5 - max);
             max--;
         }
     }
 
 done:
     free_xml(update);
     if (rc > 0) {
         rc = pcmk_ok;
     }
 
     if (display_command) {
         crm_debug("Asked attrd to %s %s: %s (%d)",
                   display_command, display_host, pcmk_strerror(rc), rc);
     } else {
         crm_debug("Asked attrd to update %s=%s for %s: %s (%d)",
                   name, value, display_host, pcmk_strerror(rc), rc);
     }
     return rc;
 }
 
 #define FAKE_TE_ID	"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 static void
 append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic,
               int level)
 {
     /* this will enable us to later determine that the
      *   resource's parameters have changed and we should force
      *   a restart
      */
     char *digest = NULL;
     xmlNode *args_xml = NULL;
 
     if (op->params == NULL) {
         return;
     }
 
     args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
     g_hash_table_foreach(op->params, hash2field, args_xml);
     filter_action_parameters(args_xml, version);
     crm_summarize_versioned_params(args_xml, op->versioned_params);
     digest = calculate_operation_digest(args_xml, version);
 
 #if 0
     if (level < get_crm_log_level()
         && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
         char *digest_source = dump_xml_unformatted(args_xml);
 
         do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
                    digest, ID(update), magic, digest_source);
         free(digest_source);
     }
 #endif
     crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
 
     free_xml(args_xml);
     free(digest);
 }
 
 int
 rsc_op_expected_rc(lrmd_event_data_t * op)
 {
     int rc = 0;
 
     if (op && op->user_data) {
         int dummy = 0;
         char *uuid = NULL;
 
         decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc);
         free(uuid);
     }
     return rc;
 }
 
 gboolean
 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
 {
     switch (op->op_status) {
         case PCMK_LRM_OP_CANCELLED:
         case PCMK_LRM_OP_PENDING:
             return FALSE;
             break;
 
         case PCMK_LRM_OP_NOTSUPPORTED:
         case PCMK_LRM_OP_TIMEOUT:
         case PCMK_LRM_OP_ERROR:
             return TRUE;
             break;
 
         default:
             if (target_rc != op->rc) {
                 return TRUE;
             }
     }
 
     return FALSE;
 }
 
 xmlNode *
 create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char * caller_version,
                         int target_rc, const char * node, const char * origin, int level)
 {
     char *key = NULL;
     char *magic = NULL;
     char *op_id = NULL;
     char *op_id_additional = NULL;
     char *local_user_data = NULL;
     const char *exit_reason = NULL;
 
     xmlNode *xml_op = NULL;
     const char *task = NULL;
     gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0);
     gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0);
 
     CRM_CHECK(op != NULL, return NULL);
     do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%d)",
                origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status),
                op->interval);
 
     crm_trace("DC version: %s", caller_version);
 
     task = op->op_type;
     /* remap the task name under various scenarios
      * this makes life easier for the PE when trying determine the current state
      */
     if (crm_str_eq(task, "reload", TRUE)) {
         if (op->op_status == PCMK_LRM_OP_DONE) {
             task = CRMD_ACTION_START;
         } else {
             task = CRMD_ACTION_STATUS;
         }
 
     } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
         /* if the migrate_from fails it will have enough info to do the right thing */
         if (op->op_status == PCMK_LRM_OP_DONE) {
             task = CRMD_ACTION_STOP;
         } else {
             task = CRMD_ACTION_STATUS;
         }
 
     } else if (dc_munges_migrate_ops
                && op->op_status == PCMK_LRM_OP_DONE
                && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
         task = CRMD_ACTION_START;
     }
 
     key = generate_op_key(op->rsc_id, task, op->interval);
     if (dc_needs_unique_ops && op->interval > 0) {
         op_id = strdup(key);
 
     } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
         const char *n_type = crm_meta_value(op->params, "notify_type");
         const char *n_task = crm_meta_value(op->params, "notify_operation");
 
         CRM_LOG_ASSERT(n_type != NULL);
         CRM_LOG_ASSERT(n_task != NULL);
         op_id = generate_notify_key(op->rsc_id, n_type, n_task);
 
         /* these are not yet allowed to fail */
         op->op_status = PCMK_LRM_OP_DONE;
         op->rc = 0;
 
     } else if (did_rsc_op_fail(op, target_rc)) {
         op_id = generate_op_key(op->rsc_id, "last_failure", 0);
         if (op->interval == 0) {
             /* Ensure 'last' gets updated too in case recording-pending="true" */
             op_id_additional = generate_op_key(op->rsc_id, "last", 0);
         }
         exit_reason = op->exit_reason;
 
     } else if (op->interval > 0) {
         op_id = strdup(key);
 
     } else {
         op_id = generate_op_key(op->rsc_id, "last", 0);
     }
 
   again:
     xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id);
     if (xml_op == NULL) {
         xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
     }
 
     if (op->user_data == NULL) {
         crm_debug("Generating fake transition key for:"
                   " %s_%s_%d %d from %s",
                   op->rsc_id, op->op_type, op->interval, op->call_id, origin);
         local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID);
         op->user_data = local_user_data;
     }
 
     if(magic == NULL) {
         magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
     }
 
     crm_xml_add(xml_op, XML_ATTR_ID, op_id);
     crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
     crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
     crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
     crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
     crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
     crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
     crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason);
     crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
 
     crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval);
 
     if (compare_version("2.1", caller_version) <= 0) {
         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
             crm_trace("Timing data (%s_%s_%d): last=%u change=%u exec=%u queue=%u",
                       op->rsc_id, op->op_type, op->interval,
                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
 
             if (op->interval == 0) {
                 /* The values are the same for non-recurring ops */
                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_RUN, op->t_run);
                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
 
             } else if(op->t_rcchange) {
                 /* last-run is not accurate for recurring ops */
                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_rcchange);
 
             } else {
                 /* ...but is better than nothing otherwise */
                 crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
             }
 
             crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
             crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
         }
     }
 
     if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE)
         || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
         /*
          * Record migrate_source and migrate_target always for migrate ops.
          */
         const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
 
         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 
         name = XML_LRM_ATTR_MIGRATE_TARGET;
         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
     }
 
     append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
 
     if (op_id_additional) {
         free(op_id);
         op_id = op_id_additional;
         op_id_additional = NULL;
         goto again;
     }
 
     if (local_user_data) {
         free(local_user_data);
         op->user_data = NULL;
     }
     free(magic);
     free(op_id);
     free(key);
     return xml_op;
 }
 
 bool
 pcmk_acl_required(const char *user) 
 {
 #if ENABLE_ACL
     if(user == NULL || strlen(user) == 0) {
         crm_trace("no user set");
         return FALSE;
 
     } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
         return FALSE;
 
     } else if (strcmp(user, "root") == 0) {
         return FALSE;
     }
     crm_trace("acls required for %s", user);
     return TRUE;
 #else
     crm_trace("acls not supported");
     return FALSE;
 #endif
 }
 
 #if ENABLE_ACL
 char *
 uid2username(uid_t uid)
 {
     struct passwd *pwent = getpwuid(uid);
 
     if (pwent == NULL) {
         crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid);
         return NULL;
 
     } else {
         return strdup(pwent->pw_name);
     }
 }
 
 const char *
 crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user)
 {
     /* field is only checked for backwards compatibility */
     static const char *effective_user = NULL;
     const char *requested_user = NULL;
     const char *user = NULL;
 
     if(effective_user == NULL) {
         effective_user = uid2username(geteuid());
     }
 
     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
     if(requested_user == NULL) {
         requested_user = crm_element_value(request, field);
     }
 
     if (is_privileged(effective_user) == FALSE) {
         /* We're not running as a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
         user = effective_user;
 
     } else if(peer_user == NULL && requested_user == NULL) {
         /* No user known or requested, use 'effective_user' and make sure one is set for the request */
         user = effective_user;
 
     } else if(peer_user == NULL) {
         /* No user known, trusting 'requested_user' */
         user = requested_user;
 
     } else if (is_privileged(peer_user) == FALSE) {
         /* The peer is not a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
         user = peer_user;
 
     } else if (requested_user == NULL) {
         /* Even if we're privileged, make sure there is always a value set */
         user = peer_user;
 
     } else {
         /* Legal delegation to 'requested_user' */
         user = requested_user;
     }
 
     /* Yes, pointer comparision */
     if(user != crm_element_value(request, XML_ACL_TAG_USER)) {
         crm_xml_add(request, XML_ACL_TAG_USER, user);
     }
 
     if(field != NULL && user != crm_element_value(request, field)) {
         crm_xml_add(request, field, user);
     }
 
     return requested_user;
 }
 
 void
 determine_request_user(const char *user, xmlNode * request, const char *field)
 {
     /* Get our internal validation out of the way first */
     CRM_CHECK(user != NULL && request != NULL && field != NULL, return);
 
     /* If our peer is a privileged user, we might be doing something on behalf of someone else */
     if (is_privileged(user) == FALSE) {
         /* We're not a privileged user, set or overwrite any existing value for $field */
         crm_xml_replace(request, field, user);
 
     } else if (crm_element_value(request, field) == NULL) {
         /* Even if we're privileged, make sure there is always a value set */
         crm_xml_replace(request, field, user);
 
 /*  } else { Legal delegation */
     }
 
     crm_trace("Processing msg as user '%s'", crm_element_value(request, field));
 }
 #endif
 
 void *
 find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
 {
     char *error;
     void *a_function;
 
     if (*handle == NULL) {
         *handle = dlopen(lib, RTLD_LAZY);
     }
 
     if (!(*handle)) {
         crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror());
         if (fatal) {
             crm_exit(DAEMON_RESPAWN_STOP);
         }
         return NULL;
     }
 
     a_function = dlsym(*handle, fn);
     if (a_function == NULL) {
         error = dlerror();
         crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error);
         if (fatal) {
             crm_exit(DAEMON_RESPAWN_STOP);
         }
     }
 
     return a_function;
 }
 
 void *
 convert_const_pointer(const void *ptr)
 {
     /* Worst function ever */
     return (void *)ptr;
 }
 
 #ifdef HAVE_UUID_UUID_H
 #  include <uuid/uuid.h>
 #endif
 
 char *
 crm_generate_uuid(void)
 {
     unsigned char uuid[16];
     char *buffer = malloc(37);  /* Including NUL byte */
 
     uuid_generate(uuid);
     uuid_unparse(uuid, buffer);
     return buffer;
 }
 
 #include <md5.h>
 
 char *
 crm_md5sum(const char *buffer)
 {
     int lpc = 0, len = 0;
     char *digest = NULL;
     unsigned char raw_digest[MD5_DIGEST_SIZE];
 
     if (buffer == NULL) {
         buffer = "";
     }
     len = strlen(buffer);
 
     crm_trace("Beginning digest of %d bytes", len);
     digest = malloc(2 * MD5_DIGEST_SIZE + 1);
     if(digest) {
         md5_buffer(buffer, len, raw_digest);
         for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
             sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
         }
         digest[(2 * MD5_DIGEST_SIZE)] = 0;
         crm_trace("Digest %s.", digest);
 
     } else {
         crm_err("Could not create digest");
     }
     return digest;
 }
 
 #ifdef HAVE_GNUTLS_GNUTLS_H
 void
 crm_gnutls_global_init(void)
 {
     signal(SIGPIPE, SIG_IGN);
     gnutls_global_init();
 }
 #endif
diff --git a/pengine/graph.c b/pengine/graph.c
index 569cf6eb64..81d8355a3e 100644
--- a/pengine/graph.c
+++ b/pengine/graph.c
@@ -1,1555 +1,1558 @@
 /*
  * 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 <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 #include <glib.h>
 
 #include <allocate.h>
 #include <utils.h>
 
 void update_colo_start_chain(action_t * action);
 gboolean rsc_update_action(action_t * first, action_t * then, enum pe_ordering type);
 
 static enum pe_action_flags
 get_action_flags(action_t * action, node_t * node)
 {
     enum pe_action_flags flags = action->flags;
 
     if (action->rsc) {
         flags = action->rsc->cmds->action_flags(action, NULL);
 
         if (action->rsc->variant >= pe_clone && node) {
 
             /* We only care about activity on $node */
             enum pe_action_flags clone_flags = action->rsc->cmds->action_flags(action, node);
 
             /* Go to great lengths to ensure the correct value for pe_action_runnable...
              *
              * If we are a clone, then for _ordering_ constraints, it's only relevant
              * if we are runnable _anywhere_.
              *
              * This only applies to _runnable_ though, and only for ordering constraints.
              * If this function is ever used during colocation, then we'll need additional logic
              *
              * Not very satisfying, but it's logical and appears to work well.
              */
             if (is_not_set(clone_flags, pe_action_runnable)
                 && is_set(flags, pe_action_runnable)) {
                 pe_rsc_trace(action->rsc, "Fixing up runnable flag for %s", action->uuid);
                 set_bit(clone_flags, pe_action_runnable);
             }
             flags = clone_flags;
         }
     }
     return flags;
 }
 
 static char *
 convert_non_atomic_uuid(char *old_uuid, resource_t * rsc, gboolean allow_notify,
                         gboolean free_original)
 {
     int interval = 0;
     char *uuid = NULL;
     char *rid = NULL;
     char *raw_task = NULL;
     int task = no_action;
 
     CRM_ASSERT(rsc);
     pe_rsc_trace(rsc, "Processing %s", old_uuid);
     if (old_uuid == NULL) {
         return NULL;
 
     } else if (strstr(old_uuid, "notify") != NULL) {
         goto done;              /* no conversion */
 
     } else if (rsc->variant < pe_group) {
         goto done;              /* no conversion */
     }
 
     CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval));
     if (interval > 0) {
         goto done;              /* no conversion */
     }
 
     task = text2task(raw_task);
     switch (task) {
         case stop_rsc:
         case start_rsc:
         case action_notify:
         case action_promote:
         case action_demote:
             break;
         case stopped_rsc:
         case started_rsc:
         case action_notified:
         case action_promoted:
         case action_demoted:
             task--;
             break;
         case monitor_rsc:
         case shutdown_crm:
         case stonith_node:
             task = no_action;
             break;
         default:
             crm_err("Unknown action: %s", raw_task);
             task = no_action;
             break;
     }
 
     if (task != no_action) {
         if (is_set(rsc->flags, pe_rsc_notify) && allow_notify) {
             uuid = generate_notify_key(rid, "confirmed-post", task2text(task + 1));
 
         } else {
             uuid = generate_op_key(rid, task2text(task + 1), 0);
         }
         pe_rsc_trace(rsc, "Converted %s -> %s", old_uuid, uuid);
     }
 
   done:
     if (uuid == NULL) {
         uuid = strdup(old_uuid);
     }
 
     if (free_original) {
         free(old_uuid);
     }
 
     free(raw_task);
     free(rid);
     return uuid;
 }
 
 static action_t *
 rsc_expand_action(action_t * action)
 {
     action_t *result = action;
 
     if (action->rsc && action->rsc->variant >= pe_group) {
         /* Expand 'start' -> 'started' */
         char *uuid = NULL;
         gboolean notify = FALSE;
 
         if (action->rsc->parent == NULL) {
             /* Only outermost resources have notification actions */
             notify = is_set(action->rsc->flags, pe_rsc_notify);
         }
 
         uuid = convert_non_atomic_uuid(action->uuid, action->rsc, notify, FALSE);
         if (uuid) {
             pe_rsc_trace(action->rsc, "Converting %s to %s %d", action->uuid, uuid,
                          is_set(action->rsc->flags, pe_rsc_notify));
             result = find_first_action(action->rsc->actions, uuid, NULL, NULL);
             if (result == NULL) {
                 crm_err("Couldn't expand %s", action->uuid);
                 result = action;
             }
             free(uuid);
         }
     }
     return result;
 }
 
 static enum pe_graph_flags
 graph_update_action(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
                     enum pe_ordering type)
 {
     enum pe_graph_flags changed = pe_graph_none;
     gboolean processed = FALSE;
 
     /* TODO: Do as many of these in parallel as possible */
 
     if (type & pe_order_implies_then) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags & pe_action_optional,
                                                 pe_action_optional, pe_order_implies_then);
 
         } else if (is_set(flags, pe_action_optional) == FALSE) {
             if (update_action_flags(then, pe_action_optional | pe_action_clear, __FUNCTION__)) {
                 changed |= pe_graph_updated_then;
             }
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "implies right: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("implies right: %s then %s %p", first->uuid, then->uuid, then->rsc);
         }
     }
 
     if ((type & pe_order_restart) && then->rsc) {
         enum pe_action_flags restart = (pe_action_optional | pe_action_runnable);
 
         processed = TRUE;
         changed |=
             then->rsc->cmds->update_actions(first, then, node, flags, restart, pe_order_restart);
         if (changed) {
             pe_rsc_trace(then->rsc, "restart: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("restart: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_implies_first) {
         processed = TRUE;
         if (first->rsc) {
             changed |=
                 first->rsc->cmds->update_actions(first, then, node, flags,
                                                  pe_action_optional, pe_order_implies_first);
 
         } else if (is_set(flags, pe_action_optional) == FALSE) {
             pe_rsc_trace(first->rsc, "first unrunnable: %s then %s", first->uuid, then->uuid);
             if (update_action_flags(first, pe_action_runnable | pe_action_clear, __FUNCTION__)) {
                 changed |= pe_graph_updated_first;
             }
         }
 
         if (changed) {
             pe_rsc_trace(then->rsc, "implies left: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("implies left: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_implies_first_master) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags & pe_action_optional,
                                                 pe_action_optional, pe_order_implies_first_master);
         }
 
         if (changed) {
             pe_rsc_trace(then->rsc,
                          "implies left when right rsc is Master role: %s then %s: changed",
                          first->uuid, then->uuid);
         } else {
             crm_trace("implies left when right rsc is Master role: %s then %s", first->uuid,
                       then->uuid);
         }
     }
 
     if (type & pe_order_one_or_more) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_runnable, pe_order_one_or_more);
 
         } else if (is_set(flags, pe_action_runnable)) {
             /* alright. a "first" action is considered runnable, incremente
              * the 'runnable_before' counter */
             then->runnable_before++;
 
             /* if the runnable before count for then exceeds the required number
              * of "before" runnable actions... mark then as runnable */
             if (then->runnable_before >= then->required_runnable_before) {
                 if (update_action_flags(then, pe_action_runnable, __FUNCTION__)) {
                     changed |= pe_graph_updated_then;
                 }
             }
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "runnable_one_or_more: %s then %s: changed", first->uuid,
                          then->uuid);
         } else {
             crm_trace("runnable_one_or_more: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_runnable_left) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_runnable, pe_order_runnable_left);
 
         } else if (is_set(flags, pe_action_runnable) == FALSE) {
             pe_rsc_trace(then->rsc, "then unrunnable: %s then %s", first->uuid, then->uuid);
             if (update_action_flags(then, pe_action_runnable | pe_action_clear, __FUNCTION__)) {
                 changed |= pe_graph_updated_then;
             }
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "runnable: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("runnable: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_implies_first_migratable) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_optional, pe_order_implies_first_migratable);
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("optional: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_pseudo_left) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_optional, pe_order_pseudo_left);
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("optional: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_optional) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_runnable, pe_order_optional);
         }
         if (changed) {
             pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("optional: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (type & pe_order_asymmetrical) {
         processed = TRUE;
         if (then->rsc) {
             changed |=
                 then->rsc->cmds->update_actions(first, then, node, flags,
                                                 pe_action_runnable, pe_order_asymmetrical);
         }
 
         if (changed) {
             pe_rsc_trace(then->rsc, "asymmetrical: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("asymmetrical: %s then %s", first->uuid, then->uuid);
         }
 
     }
 
     if ((first->flags & pe_action_runnable) && (type & pe_order_implies_then_printed)
         && (flags & pe_action_optional) == 0) {
         processed = TRUE;
         crm_trace("%s implies %s printed", first->uuid, then->uuid);
         update_action_flags(then, pe_action_print_always, __FUNCTION__);  /* don't care about changed */
     }
 
     if ((type & pe_order_implies_first_printed) && (flags & pe_action_optional) == 0) {
         processed = TRUE;
         crm_trace("%s implies %s printed", then->uuid, first->uuid);
         update_action_flags(first, pe_action_print_always, __FUNCTION__); /* don't care about changed */
     }
 
     if ((type & pe_order_implies_then
          || type & pe_order_implies_first
          || type & pe_order_restart)
         && first->rsc
         && safe_str_eq(first->task, RSC_STOP)
         && is_not_set(first->rsc->flags, pe_rsc_managed)
         && is_set(first->rsc->flags, pe_rsc_block)
         && is_not_set(first->flags, pe_action_runnable)) {
 
         if (update_action_flags(then, pe_action_runnable | pe_action_clear, __FUNCTION__)) {
             changed |= pe_graph_updated_then;
         }
 
         if (changed) {
             pe_rsc_trace(then->rsc, "unmanaged left: %s then %s: changed", first->uuid, then->uuid);
         } else {
             crm_trace("unmanaged left: %s then %s", first->uuid, then->uuid);
         }
     }
 
     if (processed == FALSE) {
         crm_trace("Constraint 0x%.6x not applicable", type);
     }
 
     return changed;
 }
 
 static void
 mark_start_blocked(resource_t *rsc)
 {
     GListPtr gIter = rsc->actions;
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (safe_str_neq(action->task, RSC_START)) {
             continue;
         }
         if (is_set(action->flags, pe_action_runnable)) {
             clear_bit(action->flags, pe_action_runnable);
             update_colo_start_chain(action);
             update_action(action);
         }
     }
 }
 
 void
 update_colo_start_chain(action_t *action)
 {
     GListPtr gIter = NULL;
     resource_t *rsc = NULL;
 
     if (is_not_set(action->flags, pe_action_runnable) && safe_str_eq(action->task, RSC_START)) {
         rsc = uber_parent(action->rsc);
     }
 
     if (rsc == NULL || rsc->rsc_cons_lhs == NULL) {
         return;
     }
 
     /* if rsc has children, all the children need to have start set to
      * unrunnable before we follow the colo chain for the parent. */
     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *)gIter->data;
         action_t *start = find_first_action(child->actions, NULL, RSC_START, NULL);
         if (start == NULL || is_set(start->flags, pe_action_runnable)) {
             return;
         }
     }
 
     for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *colocate_with = (rsc_colocation_t *)gIter->data;
         if (colocate_with->score == INFINITY) {
             mark_start_blocked(colocate_with->rsc_lh);
         }
     }
 }
 
 gboolean
 update_action(action_t * then)
 {
     GListPtr lpc = NULL;
     enum pe_graph_flags changed = pe_graph_none;
     int last_flags = then->flags;
 
     crm_trace("Processing %s (%s %s %s)",
               then->uuid,
               is_set(then->flags, pe_action_optional) ? "optional" : "required",
               is_set(then->flags, pe_action_runnable) ? "runnable" : "unrunnable",
               is_set(then->flags,
                      pe_action_pseudo) ? "pseudo" : then->node ? then->node->details->uname : "");
 
     if (is_set(then->flags, pe_action_requires_any)) {
         /* initialize current known runnable before actions to 0
          * from here as graph_update_action is called for each of
          * then's before actions, this number will increment as
          * runnable 'first' actions are encountered */
         then->runnable_before = 0;
 
         /* for backwards compatibility with previous options that use
          * the 'requires_any' flag, initialize required to 1 if it is
          * not set. */ 
         if (then->required_runnable_before == 0) {
             then->required_runnable_before = 1;
         }
         clear_bit(then->flags, pe_action_runnable);
         /* We are relying on the pe_order_one_or_more clause of
          * graph_update_action(), called as part of the:
          *
          *    'if (first == other->action)'
          *
          * block below, to set this back if appropriate
          */
     }
 
     for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
         action_wrapper_t *other = (action_wrapper_t *) lpc->data;
         action_t *first = other->action;
 
         node_t *then_node = then->node;
         node_t *first_node = first->node;
 
         enum pe_action_flags then_flags = 0;
         enum pe_action_flags first_flags = 0;
 
         if (first->rsc && first->rsc->variant == pe_group && safe_str_eq(first->task, RSC_START)) {
             first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
             if (first_node) {
                 crm_trace("First: Found node %s for %s", first_node->details->uname, first->uuid);
             }
         }
 
         if (then->rsc && then->rsc->variant == pe_group && safe_str_eq(then->task, RSC_START)) {
             then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
             if (then_node) {
                 crm_trace("Then: Found node %s for %s", then_node->details->uname, then->uuid);
             }
         }
 
         /* Disable constraint if it only applies when on same node, but isn't */
         if (is_set(other->type, pe_order_same_node) && first_node && then_node
             && (first_node->details != then_node->details)) {
 
             crm_trace("Disabled constraint %s on %s -> %s on %s",
                        other->action->uuid, first_node->details->uname,
                        then->uuid, then_node->details->uname);
             other->type = pe_order_none;
             continue;
         }
 
         clear_bit(changed, pe_graph_updated_first);
 
         if (first->rsc != then->rsc
             && first->rsc != NULL && then->rsc != NULL && first->rsc != then->rsc->parent) {
             first = rsc_expand_action(first);
         }
         if (first != other->action) {
             crm_trace("Ordering %s after %s instead of %s", then->uuid, first->uuid,
                       other->action->uuid);
         }
 
         first_flags = get_action_flags(first, then_node);
         then_flags = get_action_flags(then, first_node);
 
         crm_trace("Checking %s (%s %s %s) against %s (%s %s %s) filter=0x%.6x type=0x%.6x",
                   then->uuid,
                   is_set(then_flags, pe_action_optional) ? "optional" : "required",
                   is_set(then_flags, pe_action_runnable) ? "runnable" : "unrunnable",
                   is_set(then_flags,
                          pe_action_pseudo) ? "pseudo" : then->node ? then->node->details->
                   uname : "", first->uuid, is_set(first_flags,
                                                   pe_action_optional) ? "optional" : "required",
                   is_set(first_flags, pe_action_runnable) ? "runnable" : "unrunnable",
                   is_set(first_flags,
                          pe_action_pseudo) ? "pseudo" : first->node ? first->node->details->
                   uname : "", first_flags, other->type);
 
         if (first == other->action) {
             /*
              * 'first' was not expanded (ie. from 'start' to 'running'), which could mean it:
              * - has no associated resource,
              * - was a primitive,
              * - was pre-expanded (ie. 'running' instead of 'start')
              *
              * The third argument here to graph_update_action() is a node which is used under two conditions:
              * - Interleaving, in which case first->node and
              *   then->node are equal (and NULL)
              * - If 'then' is a clone, to limit the scope of the
              *   constraint to instances on the supplied node
              *
              */
             int otype = other->type;
             node_t *node = then->node;
 
             if(is_set(otype, pe_order_implies_then_on_node)) {
                 /* Normally we want the _whole_ 'then' clone to
                  * restart if 'first' is restarted, so then->node is
                  * needed.
                  *
                  * However for unfencing, we want to limit this to
                  * instances on the same node as 'first' (the
                  * unfencing operation), so first->node is supplied.
                  *
                  * Swap the node, from then on we can can treat it
                  * like any other 'pe_order_implies_then'
                  */
 
                 clear_bit(otype, pe_order_implies_then_on_node);
                 set_bit(otype, pe_order_implies_then);
                 node = first->node;
             }
             clear_bit(first_flags, pe_action_pseudo);
             changed |= graph_update_action(first, then, node, first_flags, otype);
 
             /* 'first' was for a complex resource (clone, group, etc),
              * create a new dependency if necessary
              */
         } else if (order_actions(first, then, other->type)) {
             /* This was the first time 'first' and 'then' were associated,
              * start again to get the new actions_before list
              */
             changed |= (pe_graph_updated_then | pe_graph_disable);
         }
 
         if (changed & pe_graph_disable) {
             crm_trace("Disabled constraint %s -> %s", other->action->uuid, then->uuid);
             clear_bit(changed, pe_graph_disable);
             other->type = pe_order_none;
         }
 
         if (changed & pe_graph_updated_first) {
             GListPtr lpc2 = NULL;
 
             crm_trace("Updated %s (first %s %s %s), processing dependents ",
                       first->uuid,
                       is_set(first->flags, pe_action_optional) ? "optional" : "required",
                       is_set(first->flags, pe_action_runnable) ? "runnable" : "unrunnable",
                       is_set(first->flags,
                              pe_action_pseudo) ? "pseudo" : first->node ? first->node->details->
                       uname : "");
             for (lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) {
                 action_wrapper_t *other = (action_wrapper_t *) lpc2->data;
 
                 update_action(other->action);
             }
             update_action(first);
         }
     }
 
     if (is_set(then->flags, pe_action_requires_any)) {
         if (last_flags != then->flags) {
             changed |= pe_graph_updated_then;
         } else {
             clear_bit(changed, pe_graph_updated_then);
         }
     }
 
     if (changed & pe_graph_updated_then) {
         crm_trace("Updated %s (then %s %s %s), processing dependents ",
                   then->uuid,
                   is_set(then->flags, pe_action_optional) ? "optional" : "required",
                   is_set(then->flags, pe_action_runnable) ? "runnable" : "unrunnable",
                   is_set(then->flags,
                          pe_action_pseudo) ? "pseudo" : then->node ? then->node->details->
                   uname : "");
 
         if (is_set(last_flags, pe_action_runnable) && is_not_set(then->flags, pe_action_runnable)) {
             update_colo_start_chain(then);
         }
         update_action(then);
         for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
             action_wrapper_t *other = (action_wrapper_t *) lpc->data;
 
             update_action(other->action);
         }
     }
 
     return FALSE;
 }
 
 gboolean
 shutdown_constraints(node_t * node, action_t * shutdown_op, pe_working_set_t * data_set)
 {
     /* add the stop to the before lists so it counts as a pre-req
      * for the shutdown
      */
     GListPtr lpc = NULL;
 
     for (lpc = data_set->actions; lpc != NULL; lpc = lpc->next) {
         action_t *action = (action_t *) lpc->data;
 
         if (action->rsc == NULL || action->node == NULL) {
             continue;
         } else if (action->node->details != node->details) {
             continue;
         } else if (is_set(action->rsc->flags, pe_rsc_maintenance)) {
             pe_rsc_trace(action->rsc, "Skipping %s: maintenance mode", action->uuid);
             continue;
         } else if (node->details->maintenance) {
             pe_rsc_trace(action->rsc, "Skipping %s: node %s is in maintenance mode",
                          action->uuid, node->details->uname);
             continue;
         } else if (safe_str_neq(action->task, RSC_STOP)) {
             continue;
         } else if (is_not_set(action->rsc->flags, pe_rsc_managed)
                    && is_not_set(action->rsc->flags, pe_rsc_block)) {
             /*
              * If another action depends on this one, we may still end up blocking
              */
             pe_rsc_trace(action->rsc, "Skipping %s: unmanaged", action->uuid);
             continue;
         }
 
         pe_rsc_trace(action->rsc, "Ordering %s before shutdown on %s", action->uuid,
                      node->details->uname);
         pe_clear_action_bit(action, pe_action_optional);
         custom_action_order(action->rsc, NULL, action,
                             NULL, strdup(CRM_OP_SHUTDOWN), shutdown_op,
                             pe_order_optional | pe_order_runnable_left, data_set);
     }
 
     return TRUE;
 }
 
 /*!
  * \internal
  * \brief Order all actions appropriately relative to a fencing operation
  *
  * Ensure start operations of affected resources are ordered after fencing,
  * imply stop and demote operations of affected resources by marking them as
  * pseudo-actions, etc.
  *
  * \param[in]     node        Node to be fenced
  * \param[in]     stonith_op  Fencing operation
  * \param[in/out] data_set    Working set of cluster
  */
 gboolean
 stonith_constraints(node_t * node, action_t * stonith_op, pe_working_set_t * data_set)
 {
     GListPtr r = NULL;
 
     CRM_CHECK(stonith_op != NULL, return FALSE);
     for (r = data_set->resources; r != NULL; r = r->next) {
         rsc_stonith_ordering((resource_t *) r->data, stonith_op, data_set);
     }
     return TRUE;
 }
 
 static node_t *
 get_router_node(action_t *action)
 {
     node_t *began_on = NULL;
     node_t *ended_on = NULL;
     node_t *router_node = NULL;
 
     if (safe_str_eq(action->task, CRM_OP_FENCE) || is_remote_node(action->node) == FALSE) {
         return NULL;
     }
 
     CRM_ASSERT(action->node->details->remote_rsc != NULL);
 
     if (action->node->details->remote_rsc->running_on) {
         began_on = action->node->details->remote_rsc->running_on->data;
     }
     ended_on = action->node->details->remote_rsc->allocated_to;
 
     /* if there is only one location to choose from,
      * this is easy. Check for those conditions first */
     if (!began_on || !ended_on) {
         /* remote rsc is either shutting down or starting up */
         return began_on ? began_on : ended_on;
     } else if (began_on->details == ended_on->details) {
         /* remote rsc didn't move nodes. */
         return began_on;
     }
 
     /* If we have get here, we know the remote resource
      * began on one node and is moving to another node.
      *
      * This means some actions will get routed through the cluster
      * node the connection rsc began on, and others are routed through
      * the cluster node the connection rsc ends up on.
      *
      * 1. stop, demote, migrate actions of resources living in the remote
      *    node _MUST_ occur _BEFORE_ the connection can move (these actions
      *    are all required before the remote rsc stop action can occur.) In
      *    this case, we know these actions have to be routed through the initial
      *    cluster node the connection resource lived on before the move takes place.
      *
      * 2. Everything else (start, promote, monitor, probe, refresh, clear failcount
      *    delete ....) must occur after the resource starts on the node it is
      *    moving to.
      */
 
     /* 1. before connection rsc moves. */
     if (safe_str_eq(action->task, "stop") ||
         safe_str_eq(action->task, "demote") ||
         safe_str_eq(action->task, "migrate_from") ||
         safe_str_eq(action->task, "migrate_to")) {
 
         router_node = began_on;
 
     /* 2. after connection rsc moves. */
     } else {
         router_node = ended_on;
     }
     return router_node;
 }
 
 /*!
  * \internal
  * \brief Add an XML node tag for a specified ID
  *
  * \param[in]     id      Node UUID to add
  * \param[in,out] xml     Parent XML tag to add to
  */
 static void
 add_node_to_xml_by_id(const char *id, xmlNode *xml)
 {
     xmlNode *node_xml;
 
     node_xml = create_xml_node(xml, XML_CIB_TAG_NODE);
     crm_xml_add(node_xml, XML_ATTR_UUID, id);
 }
 
 /*!
  * \internal
  * \brief Add an XML node tag for a specified node
  *
  * \param[in]     node  Node to add
  * \param[in/out] xml   XML to add node to
  */
 static void
 add_node_to_xml(const node_t *node, void *xml)
 {
     add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
 }
 
 /*!
  * \internal
  * \brief Add XML with nodes that an action is expected to bring down
  *
  * If a specified action is expected to bring any nodes down, add an XML block
  * with their UUIDs. When a node is lost, this allows the crmd to determine
  * whether it was expected.
  *
  * \param[in,out] xml       Parent XML tag to add to
  * \param[in]     action    Action to check for downed nodes
  * \param[in]     data_set  Working set for cluster
  */
 static void
 add_downed_nodes(xmlNode *xml, const action_t *action,
                  const pe_working_set_t *data_set)
 {
     CRM_CHECK(xml && action && action->node && data_set, return);
 
     if (safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
 
         /* Shutdown makes the action's node down */
         xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
         add_node_to_xml_by_id(action->node->details->id, downed);
 
     } else if (safe_str_eq(action->task, CRM_OP_FENCE)) {
 
         /* Fencing makes the action's node and any hosted guest nodes down */
         const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
 
         if (safe_str_eq(fence, "off") || safe_str_eq(fence, "reboot")) {
             xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
             add_node_to_xml_by_id(action->node->details->id, downed);
             pe_foreach_guest_node(data_set, action->node, add_node_to_xml, downed);
         }
 
     } else if (action->rsc && action->rsc->is_remote_node
                && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
 
         /* Stopping a remote connection resource makes connected node down,
          * unless it's part of a migration
          */
         GListPtr iter;
         action_t *input;
         gboolean migrating = FALSE;
 
         for (iter = action->actions_before; iter != NULL; iter = iter->next) {
             input = ((action_wrapper_t *) iter->data)->action;
             if (input->rsc && safe_str_eq(action->rsc->id, input->rsc->id)
                && safe_str_eq(input->task, CRMD_ACTION_MIGRATED)) {
                 migrating = TRUE;
                 break;
             }
         }
         if (!migrating) {
             xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
             add_node_to_xml_by_id(action->rsc->id, downed);
         }
     }
 }
 
 static xmlNode *
 action2xml(action_t * action, gboolean as_input, pe_working_set_t *data_set)
 {
     gboolean needs_node_info = TRUE;
     xmlNode *action_xml = NULL;
     xmlNode *args_xml = NULL;
 
     if (action == NULL) {
         return NULL;
     }
 
     if (safe_str_eq(action->task, CRM_OP_FENCE)) {
         /* All fences need node info; guest node fences are pseudo-events */
         action_xml = create_xml_node(NULL,
                                      is_set(action->flags, pe_action_pseudo)?
                                      XML_GRAPH_TAG_PSEUDO_EVENT :
                                      XML_GRAPH_TAG_CRM_EVENT);
 
     } else if (safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
         action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
 
     } else if (safe_str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT)) {
         action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
 
     } else if (safe_str_eq(action->task, CRM_OP_LRM_REFRESH)) {
         action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
 
 /* 	} else if(safe_str_eq(action->task, RSC_PROBED)) { */
 /* 		action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */
 
     } else if (is_set(action->flags, pe_action_pseudo)) {
         action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT);
         needs_node_info = FALSE;
 
     } else {
         action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
     }
 
     crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
 
     crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
     if (action->rsc != NULL && action->rsc->clone_name != NULL) {
         char *clone_key = NULL;
         const char *interval_s = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
         int interval = crm_parse_int(interval_s, "0");
 
         if (safe_str_eq(action->task, RSC_NOTIFY)) {
             const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
             const char *n_task = g_hash_table_lookup(action->meta, "notify_operation");
 
             CRM_CHECK(n_type != NULL, crm_err("No notify type value found for %s", action->uuid));
             CRM_CHECK(n_task != NULL,
                       crm_err("No notify operation value found for %s", action->uuid));
             clone_key = generate_notify_key(action->rsc->clone_name, n_type, n_task);
 
         } else if(action->cancel_task) {
             clone_key = generate_op_key(action->rsc->clone_name, action->cancel_task, interval);
         } else {
             clone_key = generate_op_key(action->rsc->clone_name, action->task, interval);
         }
 
         CRM_CHECK(clone_key != NULL, crm_err("Could not generate a key for %s", action->uuid));
         crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
         crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid);
         free(clone_key);
 
     } else {
         crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
     }
 
     if (needs_node_info && action->node != NULL) {
         node_t *router_node = get_router_node(action);
 
         crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
         crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
         if (router_node) {
             crm_xml_add(action_xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname);
         }
+
+        g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET), strdup(action->node->details->uname));
+        g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID), strdup(action->node->details->id));
     }
 
     /* No details if this action is only being listed in the inputs section */
     if (as_input) {
         return action_xml;
     }
 
     /* List affected resource */
     if (action->rsc) {
         if (is_set(action->flags, pe_action_pseudo) == FALSE) {
             int lpc = 0;
 
             xmlNode *rsc_xml = create_xml_node(action_xml, crm_element_name(action->rsc->xml));
 
             const char *attr_list[] = {
                 XML_AGENT_ATTR_CLASS,
                 XML_AGENT_ATTR_PROVIDER,
                 XML_ATTR_TYPE
             };
 
             if (is_set(action->rsc->flags, pe_rsc_orphan) && action->rsc->clone_name) {
                 /* Do not use the 'instance free' name here as that
                  * might interfere with the instance we plan to keep.
                  * Ie. if there are more than two named /anonymous/
                  * instances on a given node, we need to make sure the
                  * command goes to the right one.
                  *
                  * Keep this block, even when everyone is using
                  * 'instance free' anonymous clone names - it means
                  * we'll do the right thing if anyone toggles the
                  * unique flag to 'off'
                  */
                 crm_debug("Using orphan clone name %s instead of %s", action->rsc->id,
                           action->rsc->clone_name);
                 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
                 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
 
             } else if (is_not_set(action->rsc->flags, pe_rsc_unique)) {
                 const char *xml_id = ID(action->rsc->xml);
 
                 crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id,
                           action->rsc->clone_name);
 
                 /* ID is what we'd like client to use
                  * ID_LONG is what they might know it as instead
                  *
                  * ID_LONG is only strictly needed /here/ during the
                  * transition period until all nodes in the cluster
                  * are running the new software /and/ have rebooted
                  * once (meaning that they've only ever spoken to a DC
                  * supporting this feature).
                  *
                  * If anyone toggles the unique flag to 'on', the
                  * 'instance free' name will correspond to an orphan
                  * and fall into the claus above instead
                  */
                 crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
                 if (action->rsc->clone_name && safe_str_neq(xml_id, action->rsc->clone_name)) {
                     crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
                 } else {
                     crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
                 }
 
             } else {
                 CRM_ASSERT(action->rsc->clone_name == NULL);
                 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
             }
 
             for (lpc = 0; lpc < DIMOF(attr_list); lpc++) {
                 crm_xml_add(rsc_xml, attr_list[lpc],
                             g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
             }
         }
     }
 
     /* List any attributes in effect */
     args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
     crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 
     g_hash_table_foreach(action->extra, hash2field, args_xml);
     if (action->rsc != NULL && action->node) {
         GHashTable *p = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
         xmlNode *versioned_parameters = create_xml_node(NULL, XML_TAG_VER_ATTRS);
 
         get_rsc_attributes(p, action->rsc, action->node, data_set);
         g_hash_table_foreach(p, hash2smartfield, args_xml);
 
         pe_get_versioned_attributes(versioned_parameters, action->rsc, action->node, data_set);
         if (xml_has_children(versioned_parameters)) {
             add_node_copy(action_xml, versioned_parameters);
         }
 
         g_hash_table_destroy(p);
         free_xml(versioned_parameters);
     } else if(action->rsc && action->rsc->variant <= pe_native) {
         g_hash_table_foreach(action->rsc->parameters, hash2smartfield, args_xml);
         
         if (xml_has_children(action->rsc->versioned_parameters)) {
             add_node_copy(action_xml, action->rsc->versioned_parameters);
         }
     }
 
     g_hash_table_foreach(action->meta, hash2metafield, args_xml);
     if (action->rsc != NULL) {
         int isolated = 0;
         resource_t *parent = action->rsc;
 
         while (parent != NULL) {
             isolated |= parent->isolation_wrapper ? 1 : 0;
             parent->cmds->append_meta(parent, args_xml);
             parent = parent->parent;
         }
 
         if (isolated && action->node) {
             char *nodeattr = crm_meta_name(XML_RSC_ATTR_ISOLATION_HOST);
             crm_xml_add(args_xml, nodeattr, action->node->details->uname);
             free(nodeattr);
         }
 
     } else if (safe_str_eq(action->task, CRM_OP_FENCE) && action->node) {
         g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
     }
 
     sorted_xml(args_xml, action_xml, FALSE);
     free_xml(args_xml);
 
     /* List any nodes this action is expected to make down */
     if (needs_node_info && (action->node != NULL)) {
         add_downed_nodes(action_xml, action, data_set);
     }
 
     crm_log_xml_trace(action_xml, "dumped action");
     return action_xml;
 }
 
 static gboolean
 should_dump_action(action_t * action)
 {
     CRM_CHECK(action != NULL, return FALSE);
 
     if (is_set(action->flags, pe_action_dumped)) {
         crm_trace("action %d (%s) was already dumped", action->id, action->uuid);
         return FALSE;
 
     } else if (is_set(action->flags, pe_action_pseudo) && safe_str_eq(action->task, CRM_OP_PROBED)) {
         GListPtr lpc = NULL;
 
         /* This is a horrible but convenient hack
          *
          * It mimimizes the number of actions with unsatisfied inputs
          * (ie. not included in the graph)
          *
          * This in turn, means we can be more concise when printing
          * aborted/incomplete graphs.
          *
          * It also makes it obvious which node is preventing
          * probe_complete from running (presumably because it is only
          * partially up)
          *
          * For these reasons we tolerate such perversions
          */
 
         for (lpc = action->actions_after; lpc != NULL; lpc = lpc->next) {
             action_wrapper_t *wrapper = (action_wrapper_t *) lpc->data;
 
             if (is_not_set(wrapper->action->flags, pe_action_runnable)) {
                 /* Only interested in runnable operations */
             } else if (safe_str_neq(wrapper->action->task, RSC_START)) {
                 /* Only interested in start operations */
             } else if (is_set(wrapper->action->flags, pe_action_dumped)) {
                 crm_trace("action %d (%s) dependency of %s",
                           action->id, action->uuid, wrapper->action->uuid);
                 return TRUE;
 
             } else if (should_dump_action(wrapper->action)) {
                 crm_trace("action %d (%s) dependency of %s",
                           action->id, action->uuid, wrapper->action->uuid);
                 return TRUE;
             }
         }
     }
 
     if (is_set(action->flags, pe_action_runnable) == FALSE) {
         crm_trace("action %d (%s) was not runnable", action->id, action->uuid);
         return FALSE;
 
     } else if (is_set(action->flags, pe_action_optional)
                && is_set(action->flags, pe_action_print_always) == FALSE) {
         crm_trace("action %d (%s) was optional", action->id, action->uuid);
         return FALSE;
 
     } else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) {
         const char *interval = NULL;
 
         interval = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
 
         /* make sure probes and recurring monitors go through */
         if (safe_str_neq(action->task, RSC_STATUS) && interval == NULL) {
             crm_trace("action %d (%s) was for an unmanaged resource (%s)",
                       action->id, action->uuid, action->rsc->id);
             return FALSE;
         }
     }
 
     if (is_set(action->flags, pe_action_pseudo)
         || safe_str_eq(action->task, CRM_OP_FENCE)
         || safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
         /* skip the next checks */
         return TRUE;
     }
 
     if (action->node == NULL) {
         pe_err("action %d (%s) was not allocated", action->id, action->uuid);
         log_action(LOG_DEBUG, "Unallocated action", action, FALSE);
         return FALSE;
 
     } else if (action->node->details->online == FALSE) {
         pe_err("action %d was (%s) scheduled for offline node", action->id, action->uuid);
         log_action(LOG_DEBUG, "Action for offline node", action, FALSE);
         return FALSE;
 #if 0
         /* but this would also affect resources that can be safely
          *  migrated before a fencing op
          */
     } else if (action->node->details->unclean == FALSE) {
         pe_err("action %d was (%s) scheduled for unclean node", action->id, action->uuid);
         log_action(LOG_DEBUG, "Action for unclean node", action, FALSE);
         return FALSE;
 #endif
     }
     return TRUE;
 }
 
 /* lowest to highest */
 static gint
 sort_action_id(gconstpointer a, gconstpointer b)
 {
     const action_wrapper_t *action_wrapper2 = (const action_wrapper_t *)a;
     const action_wrapper_t *action_wrapper1 = (const action_wrapper_t *)b;
 
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     if (action_wrapper1->action->id > action_wrapper2->action->id) {
         return -1;
     }
 
     if (action_wrapper1->action->id < action_wrapper2->action->id) {
         return 1;
     }
     return 0;
 }
 
 static gboolean
 check_dump_input(int last_action, action_t * action, action_wrapper_t * wrapper)
 {
     int type = wrapper->type;
 
     if (wrapper->state == pe_link_dumped) {
         return TRUE;
 
     } else if (wrapper->state == pe_link_dup) {
         return FALSE;
     }
 
     type &= ~pe_order_implies_first_printed;
     type &= ~pe_order_implies_then_printed;
     type &= ~pe_order_optional;
 
     if (wrapper->action->node
         && action->rsc && action->rsc->fillers
         && is_not_set(type, pe_order_preserve)
         && wrapper->action->node->details->remote_rsc
         && uber_parent(action->rsc) != uber_parent(wrapper->action->rsc)
         ) {
         /* This prevents user-defined ordering constraints between
          * resources in remote nodes and the resources that
          * define/represent a remote node.
          *
          * There is no known valid reason to allow this sort of thing
          * but if one arises, we'd need to change the
          * action->rsc->fillers clause to be more specific, possibly
          * to check that it contained wrapper->action->rsc
          */
         crm_warn("Invalid ordering constraint between %s and %s",
                  wrapper->action->rsc->id, action->rsc->id);
         wrapper->type = pe_order_none;
         return FALSE;
     }
 
     if (last_action == wrapper->action->id) {
         crm_trace("Input (%d) %s duplicated for %s",
                   wrapper->action->id, wrapper->action->uuid, action->uuid);
         wrapper->state = pe_link_dup;
         return FALSE;
 
     } else if (wrapper->type == pe_order_none) {
         crm_trace("Input (%d) %s suppressed for %s",
                   wrapper->action->id, wrapper->action->uuid, action->uuid);
         return FALSE;
 
     } else if (is_set(wrapper->action->flags, pe_action_runnable) == FALSE
                && type == pe_order_none && safe_str_neq(wrapper->action->uuid, CRM_OP_PROBED)) {
         crm_trace("Input (%d) %s optional (ordering) for %s",
                   wrapper->action->id, wrapper->action->uuid, action->uuid);
         return FALSE;
 
     } else if (is_set(wrapper->action->flags, pe_action_runnable) == FALSE
                && is_set(type, pe_order_one_or_more)) {
         crm_trace("Input (%d) %s optional (one-or-more) for %s",
                   wrapper->action->id, wrapper->action->uuid, action->uuid);
         return FALSE;
 
     } else if (is_set(action->flags, pe_action_pseudo)
                && (wrapper->type & pe_order_stonith_stop)) {
         crm_trace("Input (%d) %s suppressed for %s",
                   wrapper->action->id, wrapper->action->uuid, action->uuid);
         return FALSE;
 
     } else if ((wrapper->type & pe_order_implies_first_migratable) && (is_set(wrapper->action->flags, pe_action_runnable) == FALSE)) {
         return FALSE;
 
     } else if ((wrapper->type & pe_order_apply_first_non_migratable)
                 && (is_set(wrapper->action->flags, pe_action_migrate_runnable))) {
         return FALSE;
 
     } else if ((wrapper->type == pe_order_optional)
                && crm_ends_with(wrapper->action->uuid, "_stop_0")
                && is_set(wrapper->action->flags, pe_action_migrate_runnable)) {
 
         /* for optional only ordering, ordering is not preserved for
          * a stop action that is actually involved with a migration. */
         return FALSE;
 
     } else if (wrapper->type == pe_order_load) {
         crm_trace("check load filter %s.%s -> %s.%s",
                   wrapper->action->uuid,
                   wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid,
                   action->node ? action->node->details->uname : "");
 
         if (action->rsc && safe_str_eq(action->task, RSC_MIGRATE)) {
             /* Remove the orders like the following if not relevant:
              *     "load_stopped_node2" -> "rscA_migrate_to node1"
              * which were created also from: pengine/native.c: MigrateRsc()
              *     order_actions(other, then, other_w->type);
              */
 
             /* For migrate_to ops, we care about where it has been
              * allocated to, not where the action will be executed
              */
             if (wrapper->action->node == NULL || action->rsc->allocated_to == NULL
                 || wrapper->action->node->details != action->rsc->allocated_to->details) {
                 /* Check if the actions are for the same node, ignore otherwise */
                 crm_trace("load filter - migrate");
                 wrapper->type = pe_order_none;
                 return FALSE;
             }
 
         } else if (wrapper->action->node == NULL || action->node == NULL
                    || wrapper->action->node->details != action->node->details) {
             /* Check if the actions are for the same node, ignore otherwise */
             crm_trace("load filter - node");
             wrapper->type = pe_order_none;
             return FALSE;
 
         } else if (is_set(wrapper->action->flags, pe_action_optional)) {
             /* Check if the pre-req is optional, ignore if so */
             crm_trace("load filter - optional");
             wrapper->type = pe_order_none;
             return FALSE;
         }
 
     } else if (wrapper->type == pe_order_anti_colocation) {
         crm_trace("check anti-colocation filter %s.%s -> %s.%s",
                   wrapper->action->uuid,
                   wrapper->action->node ? wrapper->action->node->details->uname : "",
                   action->uuid,
                   action->node ? action->node->details->uname : "");
 
         if (wrapper->action->node && action->node
             && wrapper->action->node->details != action->node->details) {
             /* Check if the actions are for the same node, ignore otherwise */
             crm_trace("anti-colocation filter - node");
             wrapper->type = pe_order_none;
             return FALSE;
 
         } else if (is_set(wrapper->action->flags, pe_action_optional)) {
             /* Check if the pre-req is optional, ignore if so */
             crm_trace("anti-colocation filter - optional");
             wrapper->type = pe_order_none;
             return FALSE;
         }
 
     } else if (wrapper->action->rsc
                && wrapper->action->rsc != action->rsc
                && is_set(wrapper->action->rsc->flags, pe_rsc_failed)
                && is_not_set(wrapper->action->rsc->flags, pe_rsc_managed)
                && crm_ends_with(wrapper->action->uuid, "_stop_0")
                && action->rsc && action->rsc->variant >= pe_clone) {
         crm_warn("Ignoring requirement that %s complete before %s:"
                  " unmanaged failed resources cannot prevent clone shutdown",
                  wrapper->action->uuid, action->uuid);
         return FALSE;
 
     } else if (is_set(wrapper->action->flags, pe_action_dumped)
                || should_dump_action(wrapper->action)) {
         crm_trace("Input (%d) %s should be dumped for %s", wrapper->action->id,
                   wrapper->action->uuid, action->uuid);
         goto dump;
 
 #if 0
     } else if (is_set(wrapper->action->flags, pe_action_runnable)
                && is_set(wrapper->action->flags, pe_action_pseudo)
                && wrapper->action->rsc->variant != pe_native) {
         crm_crit("Input (%d) %s should be dumped for %s",
                  wrapper->action->id, wrapper->action->uuid, action->uuid);
         goto dump;
 #endif
     } else if (is_set(wrapper->action->flags, pe_action_optional) == TRUE
                && is_set(wrapper->action->flags, pe_action_print_always) == FALSE) {
         crm_trace("Input (%d) %s optional for %s", wrapper->action->id,
                   wrapper->action->uuid, action->uuid);
         crm_trace("Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x",
                   wrapper->action->id, wrapper->action->uuid, wrapper->action->node,
                   is_set(wrapper->action->flags, pe_action_pseudo),
                   is_set(wrapper->action->flags, pe_action_runnable),
                   is_set(wrapper->action->flags, pe_action_optional),
                   is_set(wrapper->action->flags, pe_action_print_always), wrapper->type);
         return FALSE;
 
     }
 
   dump:
     return TRUE;
 }
 
 static gboolean
 graph_has_loop(action_t * init_action, action_t * action, action_wrapper_t * wrapper)
 {
     GListPtr lpc = NULL;
     gboolean has_loop = FALSE;
 
     if (is_set(wrapper->action->flags, pe_action_tracking)) {
         crm_trace("Breaking tracking loop: %s.%s -> %s.%s (0x%.6x)",
                   wrapper->action->uuid,
                   wrapper->action->node ? wrapper->action->node->details->uname : "",
                   action->uuid,
                   action->node ? action->node->details->uname : "",
                   wrapper->type);
         return FALSE;
     }
 
     if (check_dump_input(-1, action, wrapper) == FALSE) {
         return FALSE;
     }
 
     /* If there's any order like:
      * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
      * rscA is being migrated from node1 to node2,
      * while rscB is being migrated from node2 to node1.
      * There will be potential graph loop.
      * Break the order "load_stopped_node2" -> "rscA_migrate_to node1".
      */
 
     crm_trace("Checking graph loop: %s.%s -> %s.%s (0x%.6x)",
               wrapper->action->uuid,
               wrapper->action->node ? wrapper->action->node->details->uname : "",
               action->uuid,
               action->node ? action->node->details->uname : "",
               wrapper->type);
 
     if (wrapper->action == init_action) {
         crm_debug("Found graph loop: %s.%s ->...-> %s.%s",
                   action->uuid,
                   action->node ? action->node->details->uname : "",
                   init_action->uuid,
                   init_action->node ? init_action->node->details->uname : "");
 
         return TRUE;
     }
 
     set_bit(wrapper->action->flags, pe_action_tracking);
 
     for (lpc = wrapper->action->actions_before; lpc != NULL; lpc = lpc->next) {
         action_wrapper_t *wrapper_before = (action_wrapper_t *) lpc->data;
 
         if (graph_has_loop(init_action, wrapper->action, wrapper_before)) {
             has_loop = TRUE;
             goto done;
         }
     }
 
 done:
     clear_bit(wrapper->action->flags, pe_action_tracking);
 
     return has_loop;
 }
 
 static gboolean
 should_dump_input(int last_action, action_t * action, action_wrapper_t * wrapper)
 {
     wrapper->state = pe_link_not_dumped;
 
     if (check_dump_input(last_action, action, wrapper) == FALSE) {
         return FALSE;
     }
 
     if (wrapper->type == pe_order_load
         && action->rsc
         && safe_str_eq(action->task, RSC_MIGRATE)) {
         crm_trace("Checking graph loop - load migrate: %s.%s -> %s.%s",
                   wrapper->action->uuid,
                   wrapper->action->node ? wrapper->action->node->details->uname : "",
                   action->uuid,
                   action->node ? action->node->details->uname : "");
 
         if (graph_has_loop(action, action, wrapper)) {
             /* Remove the orders like the following if they are introducing any graph loops:
              *     "load_stopped_node2" -> "rscA_migrate_to node1"
              * which were created also from: pengine/native.c: MigrateRsc()
              *     order_actions(other, then, other_w->type);
              */
             crm_debug("Breaking graph loop - load migrate: %s.%s -> %s.%s",
                       wrapper->action->uuid,
                       wrapper->action->node ? wrapper->action->node->details->uname : "",
                       action->uuid,
                       action->node ? action->node->details->uname : "");
 
             wrapper->type = pe_order_none;
             return FALSE;
         }
     }
 
     crm_trace("Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x dumped for %s",
               wrapper->action->id,
               wrapper->action->uuid,
               wrapper->action->node,
               is_set(wrapper->action->flags, pe_action_pseudo),
               is_set(wrapper->action->flags, pe_action_runnable),
               is_set(wrapper->action->flags, pe_action_optional),
               is_set(wrapper->action->flags, pe_action_print_always), wrapper->type, action->uuid);
     return TRUE;
 }
 
 void
 graph_element_from_action(action_t * action, pe_working_set_t * data_set)
 {
     GListPtr lpc = NULL;
     int last_action = -1;
     int synapse_priority = 0;
     xmlNode *syn = NULL;
     xmlNode *set = NULL;
     xmlNode *in = NULL;
     xmlNode *input = NULL;
     xmlNode *xml_action = NULL;
 
     if (should_dump_action(action) == FALSE) {
         return;
     }
 
     set_bit(action->flags, pe_action_dumped);
 
     syn = create_xml_node(data_set->graph, "synapse");
     set = create_xml_node(syn, "action_set");
     in = create_xml_node(syn, "inputs");
 
     crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse);
     data_set->num_synapse++;
 
     if (action->rsc != NULL) {
         synapse_priority = action->rsc->priority;
     }
     if (action->priority > synapse_priority) {
         synapse_priority = action->priority;
     }
     if (synapse_priority > 0) {
         crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
     }
 
     xml_action = action2xml(action, FALSE, data_set);
     add_node_nocopy(set, crm_element_name(xml_action), xml_action);
 
     action->actions_before = g_list_sort(action->actions_before, sort_action_id);
 
     for (lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
         action_wrapper_t *wrapper = (action_wrapper_t *) lpc->data;
 
         if (should_dump_input(last_action, action, wrapper) == FALSE) {
             continue;
         }
 
         wrapper->state = pe_link_dumped;
         CRM_CHECK(last_action < wrapper->action->id,;
             );
         last_action = wrapper->action->id;
         input = create_xml_node(in, "trigger");
 
         xml_action = action2xml(wrapper->action, TRUE, data_set);
         add_node_nocopy(input, crm_element_name(xml_action), xml_action);
     }
 }
diff --git a/tools/crm_node.c b/tools/crm_node.c
index d927f31764..7092db4309 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -1,1002 +1,1006 @@
 /* 
  * 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 <libgen.h>             /* for basename() */
 
 #include <crm/crm.h>
 #include <crm/cluster/internal.h>
 #include <crm/common/mainloop.h>
 #include <crm/msg_xml.h>
 #include <crm/cib.h>
 #include <crm/attrd.h>
 
 int command = 0;
 int ccm_fd = 0;
 gboolean do_quiet = FALSE;
 
 char *target_uuid = NULL;
 char *target_uname = NULL;
 const char *standby_value = NULL;
 const char *standby_scope = NULL;
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",       0, 0, '?', "\tThis text"},
     {"version",    0, 0, '$', "\tVersion information"  },
     {"verbose",    0, 0, 'V', "\tIncrease debug output"},
     {"quiet",      0, 0, 'Q', "\tEssential output only"},
 
     {"-spacer-",   1, 0, '-', "\nStack:"},
 #if SUPPORT_CMAN
     {"cman",       0, 0, 'c', "\tOnly try connecting to a cman-based cluster"},
 #endif
 #if SUPPORT_COROSYNC
     {"openais",    0, 0, 'A', "\tOnly try connecting to an OpenAIS-based cluster"},
 #endif
 #ifdef SUPPORT_CS_QUORUM
     {"corosync",   0, 0, 'C', "\tOnly try connecting to an Corosync-based cluster"},
 #endif
 #ifdef SUPPORT_HEARTBEAT
     {"heartbeat",  0, 0, 'H', "Only try connecting to a Heartbeat-based cluster"},
 #endif
     
     {"-spacer-",      1, 0, '-', "\nCommands:"},
     {"name",	      0, 0, 'n', "\tDisplay the name used by the cluster for this node"},
     {"name-for-id",   1, 0, 'N', "\tDisplay the name used by the cluster for the node with the specified id"},
     {"epoch",	      0, 0, 'e', "\tDisplay the epoch during which this node joined the cluster"},
     {"quorum",        0, 0, 'q', "\tDisplay a 1 if our partition has quorum, 0 if not"},
     {"list",          0, 0, 'l', "\tDisplay all known members (past and present) of this cluster (Not available for heartbeat clusters)"},
     {"partition",     0, 0, 'p', "Display the members of this partition"},
     {"cluster-id",    0, 0, 'i', "Display this node's cluster id"},
     {"remove",        1, 0, 'R', "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's configuration and caches"},
     {"-spacer-",      1, 0, '-', "In the case of Heartbeat, CMAN and Corosync 2.0, requires that the node has already been removed from the underlying cluster"},
 
     {"-spacer-", 1, 0, '-', "\nAdditional Options:"},
     {"force",	 0, 0, 'f'},
 
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 static int
 cib_remove_node(uint32_t id, const char *name)
 {
     int rc;
     cib_t *cib = NULL;
     xmlNode *node = NULL;
     xmlNode *node_state = NULL;
 
     crm_trace("Removing %s from the CIB", name);
 
     if(name == NULL && id == 0) {
         return -ENOTUNIQ;
     }
 
     node = create_xml_node(NULL, XML_CIB_TAG_NODE);
     node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
 
     crm_xml_add(node, XML_ATTR_UNAME, name);
     crm_xml_add(node_state, XML_ATTR_UNAME, name);
     if(id) {
         char buffer[64];
         if(snprintf(buffer, 63, "%u", id) > 0) {
             crm_xml_add(node, XML_ATTR_ID, buffer);
             crm_xml_add(node_state, XML_ATTR_ID, buffer);
         }
     }
 
     cib = cib_new();
     cib->cmds->signon(cib, crm_system_name, cib_command);
 
     rc = cib->cmds->delete(cib, XML_CIB_TAG_NODES, node, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s/%u from " XML_CIB_TAG_NODES ": %s", name, id, pcmk_strerror(rc));
     }
     rc = cib->cmds->delete(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s/%u from " XML_CIB_TAG_STATUS ": %s", name, id, pcmk_strerror(rc));
     }
 
     cib->cmds->signoff(cib);
     cib_delete(cib);
     return rc;
 }
 
 int tools_remove_node_cache(const char *node, const char *target);
 
 int tools_remove_node_cache(const char *node, const char *target)
 {
     int n = 0;
     int rc = -1;
     char *name = NULL;
     char *admin_uuid = NULL;
     crm_ipc_t *conn = crm_ipc_new(target, 0);
     xmlNode *cmd = NULL;
     xmlNode *hello = NULL;
     char *endptr = NULL;
 
     if (!conn) {
         return -ENOTCONN;
     }
 
     if (!crm_ipc_connect(conn)) {
         crm_perror(LOG_ERR, "Connection to %s failed", target);
         crm_ipc_destroy(conn);
         return -ENOTCONN;
     }
 
     if(safe_str_eq(target, CRM_SYSTEM_CRMD)) {
         admin_uuid = calloc(1, 11);
         snprintf(admin_uuid, 10, "%d", getpid());
         admin_uuid[10] = '\0';
 
         hello = create_hello_message(admin_uuid, "crm_node", "0", "1");
         rc = crm_ipc_send(conn, hello, 0, 0, NULL);
 
         free_xml(hello);
         if (rc < 0) {
             free(admin_uuid);
             return rc;
         }
     }
 
 
     errno = 0;
     n = strtol(node, &endptr, 10);
     if (errno != 0 || endptr == node || *endptr != '\0') {
         /* Argument was not a nodeid */
         n = 0;
         name = strdup(node);
     } else {
         name = get_node_name(n);
     }
 
     crm_trace("Removing %s aka. %s (%u) from the membership cache", name, node, n);
 
     if(safe_str_eq(target, T_ATTRD)) {
         cmd = create_xml_node(NULL, __FUNCTION__);
 
         crm_xml_add(cmd, F_TYPE, T_ATTRD);
         crm_xml_add(cmd, F_ORIG, crm_system_name);
 
         crm_xml_add(cmd, F_ATTRD_TASK, ATTRD_OP_PEER_REMOVE);
         crm_xml_add(cmd, F_ATTRD_HOST, name);
 
         if (n) {
             char buffer[64];
             if(snprintf(buffer, 63, "%u", n) > 0) {
                 crm_xml_add(cmd, F_ATTRD_HOST_ID, buffer);
             }
         }
 
     } else {
         cmd = create_request(CRM_OP_RM_NODE_CACHE,
                              NULL, NULL, target, crm_system_name, admin_uuid);
         if (n) {
             char buffer[64];
             if(snprintf(buffer, 63, "%u", n) > 0) {
                 crm_xml_add(cmd, XML_ATTR_ID, buffer);
             }
         }
         crm_xml_add(cmd, XML_ATTR_UNAME, name);
     }
 
     rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
     crm_debug("%s peer cache cleanup for %s (%u): %d", target, name, n, rc);
 
     if (rc > 0) {
         rc = cib_remove_node(n, name);
     }
 
     if (conn) {
         crm_ipc_close(conn);
         crm_ipc_destroy(conn);
     }
     free(admin_uuid);
     free_xml(cmd);
     free(name);
     return rc > 0 ? 0 : rc;
 }
 
 static gint
 compare_node_uname(gconstpointer a, gconstpointer b)
 {
     const crm_node_t *a_node = a;
     const crm_node_t *b_node = b;
     return strcmp(a_node->uname?a_node->uname:"", b_node->uname?b_node->uname:"");
 }
 
 static int
 node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
 {
     xmlNode *msg = string2xml(buffer);
 
     if (msg) {
         xmlNode *node = NULL;
         GListPtr nodes = NULL;
         GListPtr iter = NULL;
         const char *quorate = crm_element_value(msg, "quorate");
 
         crm_log_xml_trace(msg, "message");
         if (command == 'q' && quorate != NULL) {
             fprintf(stdout, "%s\n", quorate);
             crm_exit(pcmk_ok);
 
         } else if(command == 'q') {
             crm_exit(1);
         }
 
         for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
             crm_node_t *peer = calloc(1, sizeof(crm_node_t));
 
             nodes = g_list_insert_sorted(nodes, peer, compare_node_uname);
             peer->uname = (char*)crm_element_value_copy(node, "uname");
             peer->state = (char*)crm_element_value_copy(node, "state");
             crm_element_value_int(node, "id", (int*)&peer->id);
         }
 
         for(iter = nodes; iter; iter = iter->next) {
             crm_node_t *peer = iter->data;
             if (command == 'l') {
                 fprintf(stdout, "%u %s %s\n", peer->id, peer->uname, peer->state?peer->state:"");
 
             } else if (command == 'p') {
                 if(safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
                     fprintf(stdout, "%s ", peer->uname);
                 }
 
             } else if (command == 'i') {
                 if(safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
                     fprintf(stdout, "%u ", peer->id);
                 }
             }
         }
 
         g_list_free_full(nodes, free);
         free_xml(msg);
 
         if (command == 'p') {
             fprintf(stdout, "\n");
         }
 
         crm_exit(pcmk_ok);
     }
 
     return 0;
 }
 
 static void
 node_mcp_destroy(gpointer user_data)
 {
     crm_exit(ENOTCONN);
 }
 
 static gboolean
 try_pacemaker(int command, enum cluster_type_e stack)
 {
     struct ipc_client_callbacks node_callbacks = {
         .dispatch = node_mcp_dispatch,
         .destroy = node_mcp_destroy
     };
 
     if (stack == pcmk_cluster_heartbeat) {
         /* Nothing to do for them */
         return FALSE;
     }
 
     switch (command) {
         case 'e':
             /* Age only applies to heartbeat clusters */
             fprintf(stdout, "1\n");
             crm_exit(pcmk_ok);
 
         case 'R':
             {
                 int lpc = 0;
                 const char *daemons[] = {
                     CRM_SYSTEM_CRMD,
                     "stonith-ng",
                     T_ATTRD,
                     CRM_SYSTEM_MCP,
                 };
 
                 for(lpc = 0; lpc < DIMOF(daemons); lpc++) {
                     if (tools_remove_node_cache(target_uname, daemons[lpc])) {
                         crm_err("Failed to connect to %s to remove node '%s'", daemons[lpc], target_uname);
                         crm_exit(pcmk_err_generic);
                     }
                 }
                 crm_exit(pcmk_ok);
             }
             break;
 
         case 'i':
         case 'l':
         case 'q':
         case 'p':
             /* Go to pacemakerd */
             {
                 GMainLoop *amainloop = g_main_loop_new(NULL, FALSE);
                 mainloop_io_t *ipc =
                     mainloop_add_ipc_client(CRM_SYSTEM_MCP, G_PRIORITY_DEFAULT, 0, NULL, &node_callbacks);
                 if (ipc != NULL) {
                     /* Sending anything will get us a list of nodes */
                     xmlNode *poke = create_xml_node(NULL, "poke");
 
                     crm_ipc_send(mainloop_get_ipc_client(ipc), poke, 0, 0, NULL);
                     free_xml(poke);
                     g_main_run(amainloop);
                 }
             }
             break;
     }
     return FALSE;
 }
 
 #if SUPPORT_HEARTBEAT
 #  include <ocf/oc_event.h>
 #  include <ocf/oc_membership.h>
 #  include <clplumbing/cl_uuid.h>
 
 #  define UUID_LEN 16
 
 oc_ev_t *ccm_token = NULL;
 static void *ccm_library = NULL;
 void oc_ev_special(const oc_ev_t *, oc_ev_class_t, int);
 
 static gboolean
 read_local_hb_uuid(void)
 {
     cl_uuid_t uuid;
     char *buffer = NULL;
     long start = 0, read_len = 0;
 
     FILE *input = fopen(UUID_FILE, "r");
 
     if (input == NULL) {
         crm_info("Could not open UUID file %s", UUID_FILE);
         return FALSE;
     }
 
     /* see how big the file is */
     start = ftell(input);
     fseek(input, 0L, SEEK_END);
     if (UUID_LEN != ftell(input)) {
         fprintf(stderr, "%s must contain exactly %d bytes\n", UUID_FILE, UUID_LEN);
         abort();
     }
 
     fseek(input, 0L, start);
     if (start != ftell(input)) {
         fprintf(stderr, "fseek not behaving: %ld vs. %ld\n", start, ftell(input));
         crm_exit(pcmk_err_generic);
     }
 
     buffer = malloc(50);
     read_len = fread(uuid.uuid, 1, UUID_LEN, input);
     fclose(input);
 
     if (read_len != UUID_LEN) {
         fprintf(stderr, "Expected and read bytes differ: %d vs. %ld\n", UUID_LEN, read_len);
         crm_exit(pcmk_err_generic);
 
     } else if (buffer != NULL) {
         cl_uuid_unparse(&uuid, buffer);
         fprintf(stdout, "%s\n", buffer);
         return TRUE;
 
     } else {
         fprintf(stderr, "No buffer to unparse\n");
         crm_exit(ENODATA);
     }
 
     free(buffer);
     return FALSE;
 }
 
 static void
 ccm_age_callback(oc_ed_t event, void *cookie, size_t size, const void *data)
 {
     int lpc;
     int node_list_size;
     const oc_ev_membership_t *oc = (const oc_ev_membership_t *)data;
 
     int (*ccm_api_callback_done) (void *cookie) =
         find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_callback_done", 1);
 
     node_list_size = oc->m_n_member;
     if (command == 'q') {
         crm_debug("Processing \"%s\" event.",
                   event == OC_EV_MS_NEW_MEMBERSHIP ? "NEW MEMBERSHIP" :
                   event == OC_EV_MS_NOT_PRIMARY ? "NOT PRIMARY" :
                   event == OC_EV_MS_PRIMARY_RESTORED ? "PRIMARY RESTORED" :
                   event == OC_EV_MS_EVICTED ? "EVICTED" : "NO QUORUM MEMBERSHIP");
         if (ccm_have_quorum(event)) {
             fprintf(stdout, "1\n");
         } else {
             fprintf(stdout, "0\n");
         }
 
     } else if (command == 'e') {
         crm_debug("Searching %d members for our birth", oc->m_n_member);
     }
     for (lpc = 0; lpc < node_list_size; lpc++) {
         if (command == 'p') {
             fprintf(stdout, "%s ", oc->m_array[oc->m_memb_idx + lpc].node_uname);
 
         } else if (command == 'e') {
             int (*ccm_api_is_my_nodeid) (const oc_ev_t * token, const oc_node_t * node) =
                 find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_is_my_nodeid", 1);
             if ((*ccm_api_is_my_nodeid) (ccm_token, &(oc->m_array[lpc]))) {
                 crm_debug("MATCH: nodeid=%d, uname=%s, born=%d",
                           oc->m_array[oc->m_memb_idx + lpc].node_id,
                           oc->m_array[oc->m_memb_idx + lpc].node_uname,
                           oc->m_array[oc->m_memb_idx + lpc].node_born_on);
                 fprintf(stdout, "%d\n", oc->m_array[oc->m_memb_idx + lpc].node_born_on);
             }
         }
     }
 
     (*ccm_api_callback_done) (cookie);
 
     if (command == 'p') {
         fprintf(stdout, "\n");
     }
     fflush(stdout);
     crm_exit(pcmk_ok);
 }
 
 static gboolean
 ccm_age_connect(int *ccm_fd)
 {
     gboolean did_fail = FALSE;
     int ret = 0;
 
     int (*ccm_api_register) (oc_ev_t ** token) =
         find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_register", 1);
 
     int (*ccm_api_set_callback) (const oc_ev_t * token,
                                  oc_ev_class_t class,
                                  oc_ev_callback_t * fn,
                                  oc_ev_callback_t ** prev_fn) =
         find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_set_callback", 1);
 
     void (*ccm_api_special) (const oc_ev_t *, oc_ev_class_t, int) =
         find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_special", 1);
     int (*ccm_api_activate) (const oc_ev_t * token, int *fd) =
         find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_activate", 1);
 
     crm_debug("Registering with CCM");
     ret = (*ccm_api_register) (&ccm_token);
     if (ret != 0) {
         crm_info("CCM registration failed: %d", ret);
         did_fail = TRUE;
     }
 
     if (did_fail == FALSE) {
         crm_debug("Setting up CCM callbacks");
         ret = (*ccm_api_set_callback) (ccm_token, OC_EV_MEMB_CLASS, ccm_age_callback, NULL);
         if (ret != 0) {
             crm_warn("CCM callback not set: %d", ret);
             did_fail = TRUE;
         }
     }
     if (did_fail == FALSE) {
         (*ccm_api_special) (ccm_token, OC_EV_MEMB_CLASS, 0 /*don't care */ );
 
         crm_debug("Activating CCM token");
         ret = (*ccm_api_activate) (ccm_token, ccm_fd);
         if (ret != 0) {
             crm_warn("CCM Activation failed: %d", ret);
             did_fail = TRUE;
         }
     }
 
     return !did_fail;
 }
 
 static gboolean
 try_heartbeat(int command, enum cluster_type_e stack)
 {
     crm_debug("Attempting to process %c command", command);
 
     if (command == 'i') {
         if (read_local_hb_uuid()) {
             crm_exit(pcmk_ok);
         }
 
     } else if (command == 'R') {
         if (tools_remove_node_cache(target_uname, CRM_SYSTEM_CRMD)) {
             crm_err("Failed to connect to "CRM_SYSTEM_CRMD" to remove node '%s'", target_uname);
             crm_exit(pcmk_err_generic);
         }
         crm_exit(pcmk_ok);
 
     } else if (ccm_age_connect(&ccm_fd)) {
         int rc = 0;
         fd_set rset;
         int (*ccm_api_handle_event) (const oc_ev_t * token) =
             find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_handle_event", 1);
 
         while (1) {
 
             sleep(1);
             FD_ZERO(&rset);
             FD_SET(ccm_fd, &rset);
 
             errno = 0;
             rc = select(ccm_fd + 1, &rset, NULL, NULL, NULL);
 
             if (rc > 0 && (*ccm_api_handle_event) (ccm_token) != 0) {
                 crm_err("oc_ev_handle_event failed");
                 return FALSE;
 
             } else if (rc < 0 && errno != EINTR) {
                 crm_perror(LOG_ERR, "select failed: %d", rc);
                 return FALSE;
             }
         }
     }
     return FALSE;
 }
 #endif
 
 #if SUPPORT_CMAN
 #  include <libcman.h>
 #  define MAX_NODES 256
 static bool valid_cman_name(const char *name, uint32_t nodeid) 
 {
     bool rc = TRUE;
 
     /* Yes, %d, because that's what CMAN does */
     char *fakename = crm_strdup_printf("Node%d", nodeid);
 
     if(crm_str_eq(fakename, name, TRUE)) {
         rc = FALSE;
         crm_notice("Ignoring inferred name from cman: %s", fakename);
     }
     free(fakename);
     return rc;
 }
 
 static gboolean
 try_cman(int command, enum cluster_type_e stack)
 {
 
     int rc = -1, lpc = 0, node_count = 0;
     cman_node_t node;
     cman_cluster_t cluster;
     cman_handle_t cman_handle = NULL;
     cman_node_t cman_nodes[MAX_NODES];
 
     memset(&cluster, 0, sizeof(cluster));
 
     cman_handle = cman_init(NULL);
     if (cman_handle == NULL || cman_is_active(cman_handle) == FALSE) {
         crm_info("Couldn't connect to cman");
         return FALSE;
     }
 
     switch (command) {
         case 'R':
             try_pacemaker(command, stack);
             break;
 
         case 'e':
             /* Age makes no sense (yet?) in a cman cluster */
             fprintf(stdout, "1\n");
             break;
 
         case 'q':
             fprintf(stdout, "%d\n", cman_is_quorate(cman_handle));
             break;
 
         case 'l':
         case 'p':
             memset(cman_nodes, 0, MAX_NODES * sizeof(cman_node_t));
             rc = cman_get_nodes(cman_handle, MAX_NODES, &node_count, cman_nodes);
             if (rc != 0) {
                 fprintf(stderr, "Couldn't query cman node list: %d %d", rc, errno);
                 goto cman_bail;
             }
 
             for (lpc = 0; lpc < node_count; lpc++) {
                 if(valid_cman_name(cman_nodes[lpc].cn_name, cman_nodes[lpc].cn_nodeid) == FALSE) {
                     /* The name was invented, but we need to print something, make it the id instead */
                     printf("%u ", cman_nodes[lpc].cn_nodeid);
 
                 } if (command == 'l') {
                     printf("%s ", cman_nodes[lpc].cn_name);
 
                 } else if (cman_nodes[lpc].cn_nodeid != 0 && cman_nodes[lpc].cn_member) {
                     /* Never allow node ID 0 to be considered a member #315711 */
                     printf("%s ", cman_nodes[lpc].cn_name);
                 }
             }
             printf("\n");
             break;
 
         case 'i':
             memset(&node, 0, sizeof(cman_node_t));
             rc = cman_get_node(cman_handle, CMAN_NODEID_US, &node);
             if (rc != 0) {
                 fprintf(stderr, "Couldn't query cman node id: %d %d", rc, errno);
                 goto cman_bail;
             }
             fprintf(stdout, "%u\n", node.cn_nodeid);
             break;
 
         default:
             fprintf(stderr, "Unknown option '%c'\n", command);
             crm_help('?', EX_USAGE);
     }
     cman_finish(cman_handle);
     crm_exit(pcmk_ok);
 
   cman_bail:
     cman_finish(cman_handle);
     return crm_exit(EINVAL);
 }
 #endif
 
 #if HAVE_CONFDB
 static void
 ais_membership_destroy(gpointer user_data)
 {
     crm_err("AIS connection terminated");
     ais_fd_sync = -1;
     crm_exit(ENOTCONN);
 }
 
 static gint
 member_sort(gconstpointer a, gconstpointer b)
 {
     const crm_node_t *node_a = a;
     const crm_node_t *node_b = b;
 
     return strcmp(node_a->uname, node_b->uname);
 }
 
 static void
 crm_add_member(gpointer key, gpointer value, gpointer user_data)
 {
     GList **list = user_data;
     crm_node_t *node = value;
 
     if (node->uname != NULL) {
         *list = g_list_insert_sorted(*list, node, member_sort);
     }
 }
 
 static void
 ais_membership_dispatch(cpg_handle_t handle,
                           const struct cpg_name *groupName,
                           uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 {
     uint32_t kind = 0;
     const char *from = NULL;
     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 
     switch (kind) {
         case crm_class_members:
         case crm_class_notify:
         case crm_class_quorum:
             break;
         default:
             free(data);
             return;
 
             break;
     }
 
     if (command == 'q') {
         if (crm_have_quorum) {
             fprintf(stdout, "1\n");
         } else {
             fprintf(stdout, "0\n");
         }
 
     } else if (command == 'l') {
         GList *nodes = NULL;
         GListPtr lpc = NULL;
 
         g_hash_table_foreach(crm_peer_cache, crm_add_member, &nodes);
         for (lpc = nodes; lpc != NULL; lpc = lpc->next) {
             crm_node_t *node = (crm_node_t *) lpc->data;
 
             fprintf(stdout, "%u %s %s\n", node->id, node->uname, node->state);
         }
         fprintf(stdout, "\n");
 
     } else if (command == 'p') {
         GList *nodes = NULL;
         GListPtr lpc = NULL;
 
         g_hash_table_foreach(crm_peer_cache, crm_add_member, &nodes);
         for (lpc = nodes; lpc != NULL; lpc = lpc->next) {
             crm_node_t *node = (crm_node_t *) lpc->data;
 
             if (node->uname && safe_str_eq(node->state, CRM_NODE_MEMBER)) {
                 fprintf(stdout, "%s ", node->uname);
             }
         }
         fprintf(stdout, "\n");
     }
 
     free(data);
     crm_exit(pcmk_ok);
 
     return;
 }
 #endif
 
 #ifdef SUPPORT_CS_QUORUM
 #  include <corosync/quorum.h>
 #  include <corosync/cpg.h>
 
 static gboolean
 try_corosync(int command, enum cluster_type_e stack)
 {
     int rc = 0;
     int quorate = 0;
     uint32_t quorum_type = 0;
     unsigned int nodeid = 0;
     cpg_handle_t c_handle = 0;
     quorum_handle_t q_handle = 0;
 
     switch (command) {
         case 'q':
             /* Go direct to the Quorum API */
             rc = quorum_initialize(&q_handle, NULL, &quorum_type);
             if (rc != CS_OK) {
                 crm_err("Could not connect to the Quorum API: %d", rc);
                 return FALSE;
             }
 
             rc = quorum_getquorate(q_handle, &quorate);
             if (rc != CS_OK) {
                 crm_err("Could not obtain the current Quorum API state: %d", rc);
                 return FALSE;
             }
 
             if (quorate) {
                 fprintf(stdout, "1\n");
             } else {
                 fprintf(stdout, "0\n");
             }
             quorum_finalize(q_handle);
             crm_exit(pcmk_ok);
 
         case 'i':
             /* Go direct to the CPG API */
             rc = cpg_initialize(&c_handle, NULL);
             if (rc != CS_OK) {
                 crm_err("Could not connect to the Cluster Process Group API: %d", rc);
                 return FALSE;
             }
 
             rc = cpg_local_get(c_handle, &nodeid);
             if (rc != CS_OK) {
                 crm_err("Could not get local node id from the CPG API");
                 return FALSE;
             }
 
             fprintf(stdout, "%u\n", nodeid);
             cpg_finalize(c_handle);
             crm_exit(pcmk_ok);
 
         default:
             try_pacemaker(command, stack);
             break;
     }
     return FALSE;
 }
 #endif
 
 #if HAVE_CONFDB
 static gboolean
 try_openais(int command, enum cluster_type_e stack)
 {
     static crm_cluster_t cluster;
 
     cluster.destroy = ais_membership_destroy;
     cluster.cpg.cpg_deliver_fn = ais_membership_dispatch;
     cluster.cpg.cpg_confchg_fn = NULL;
 
     if (init_cs_connection_once(&cluster)) {
 
         GMainLoop *amainloop = NULL;
 
         switch (command) {
             case 'R':
                 send_cluster_text(crm_class_rmpeer, target_uname, TRUE, NULL, crm_msg_ais);
                 cib_remove_node(0, target_uname);
                 crm_exit(pcmk_ok);
 
             case 'e':
                 /* Age makes no sense (yet) in an AIS cluster */
                 fprintf(stdout, "1\n");
                 crm_exit(pcmk_ok);
 
             case 'q':
                 send_cluster_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais);
                 break;
 
             case 'l':
             case 'p':
                 crm_info("Requesting the list of configured nodes");
                 send_cluster_text(crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais);
                 break;
 
             case 'i':
                 printf("%u\n", cluster.nodeid);
                 crm_exit(pcmk_ok);
 
             default:
                 fprintf(stderr, "Unknown option '%c'\n", command);
                 crm_help('?', EX_USAGE);
         }
         amainloop = g_main_new(FALSE);
         g_main_run(amainloop);
     }
     return FALSE;
 }
 #endif
 
 int set_cluster_type(enum cluster_type_e type);
 
 int
 main(int argc, char **argv)
 {
     int flag = 0;
     int argerr = 0;
     uint32_t nodeid = 0;
     gboolean force_flag = FALSE;
     gboolean dangerous_cmd = FALSE;
     enum cluster_type_e try_stack = pcmk_cluster_unknown;
 
     int option_index = 0;
 
     crm_peer_init();
     crm_log_cli_init("crm_node");
     crm_set_options(NULL, "command [options]", long_options,
                     "Tool for displaying low-level node information");
 
     while (flag >= 0) {
         flag = crm_get_option(argc, argv, &option_index);
         switch (flag) {
             case -1:
                 break;
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '$':
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             case 'Q':
                 do_quiet = TRUE;
                 break;
             case 'H':
                 set_cluster_type(pcmk_cluster_heartbeat);
                 break;
             case 'A':
                 set_cluster_type(pcmk_cluster_classic_ais);
                 break;
             case 'C':
                 set_cluster_type(pcmk_cluster_corosync);
                 break;
             case 'c':
                 set_cluster_type(pcmk_cluster_cman);
                 break;
             case 'f':
                 force_flag = TRUE;
                 break;
             case 'R':
                 command = flag;
                 dangerous_cmd = TRUE;
                 target_uname = optarg;
                 break;
             case 'N':
                 command = flag;
                 nodeid = crm_parse_int(optarg, NULL);
                 break;
             case 'p':
             case 'e':
             case 'q':
             case 'i':
             case 'l':
             case 'n':
                 command = flag;
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
 
     if (command == 'n') {
-        fprintf(stdout, "%s\n", get_local_node_name());
+        const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
+        if(name == NULL) {
+            name = get_local_node_name();
+        }
+        fprintf(stdout, "%s\n", name);
         crm_exit(pcmk_ok);
 
     } else if (command == 'N') {
         fprintf(stdout, "%s\n", get_node_name(nodeid));
         crm_exit(pcmk_ok);
     }
 
     if (dangerous_cmd && force_flag == FALSE) {
         fprintf(stderr, "The supplied command is considered dangerous."
                 "  To prevent accidental destruction of the cluster,"
                 " the --force flag is required in order to proceed.\n");
         fflush(stderr);
         crm_exit(EINVAL);
     }
 
     try_stack = get_cluster_type();
     crm_debug("Attempting to process -%c command for cluster type: %s", command,
               name_for_cluster_type(try_stack));
 
 #if SUPPORT_CMAN
     if (try_stack == pcmk_cluster_cman) {
         try_cman(command, try_stack);
     }
 #endif
 
 #ifdef SUPPORT_CS_QUORUM
     if (try_stack == pcmk_cluster_corosync) {
         try_corosync(command, try_stack);
     }
 #endif
 
 #if HAVE_CONFDB
     /* Only an option if we're using the plugins */
     if (try_stack == pcmk_cluster_classic_ais) {
         try_openais(command, try_stack);
     }
 #endif
 
 #if SUPPORT_HEARTBEAT
     if (try_stack == pcmk_cluster_heartbeat) {
         try_heartbeat(command, try_stack);
     }
 #endif
 
     try_pacemaker(command, try_stack);
 
     return (1);
 }