diff --git a/include/crm_internal.h b/include/crm_internal.h
index 8bc1188b1b..652f9c46b1 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -1,305 +1,305 @@
 /*
- * Copyright 2006-2018 the Pacemaker project contributors
+ * Copyright 2006-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef CRM_INTERNAL__H
 #  define CRM_INTERNAL__H
 
 #  include <config.h>
 #  include <portability.h>
 
 #  include <glib.h>
 #  include <stdbool.h>
 #  include <libxml/tree.h>
 
 #  include <crm/lrmd.h>
 #  include <crm/common/logging.h>
 #  include <crm/common/ipcs.h>
 #  include <crm/common/internal.h>
 
 /* Dynamic loading of libraries */
 void *find_library_function(void **handle, const char *lib, const char *fn, int fatal);
 
 /* For ACLs */
 char *uid2username(uid_t uid);
 const char *crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user);
 
 #  if ENABLE_ACL
 #    include <string.h>
 static inline gboolean
 is_privileged(const char *user)
 {
     if (user == NULL) {
         return FALSE;
     } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
         return TRUE;
     } else if (strcmp(user, "root") == 0) {
         return TRUE;
     }
     return FALSE;
 }
 #  endif
 
 /* CLI option processing*/
 #  ifdef HAVE_GETOPT_H
 #    include <getopt.h>
 #  else
 #    define no_argument 0
 #    define required_argument 1
 #  endif
 
 #  define pcmk_option_default	0x00000
 #  define pcmk_option_hidden	0x00001
 #  define pcmk_option_paragraph	0x00002
 #  define pcmk_option_example	0x00004
 
 struct crm_option {
     /* Fields from 'struct option' in getopt.h */
     /* name of long option */
     const char *name;
     /*
      * one of no_argument, required_argument, and optional_argument:
      * whether option takes an argument
      */
     int has_arg;
     /* if not NULL, set *flag to val when option found */
     int *flag;
     /* if flag not NULL, value to set *flag to; else return value */
     int val;
 
     /* Custom fields */
     const char *desc;
     long flags;
 };
 
 void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options,
                      const char *app_desc);
 int crm_get_option(int argc, char **argv, int *index);
 int crm_get_option_long(int argc, char **argv, int *index, const char **longname);
-crm_exit_t crm_help(char cmd, crm_exit_t exit_code);
+_Noreturn void crm_help(char cmd, crm_exit_t exit_code);
 
 /* Cluster Option Processing */
 typedef struct pe_cluster_option_s {
     const char *name;
     const char *alt_name;
     const char *type;
     const char *values;
     const char *default_value;
 
      gboolean(*is_valid) (const char *);
 
     const char *description_short;
     const char *description_long;
 
 } pe_cluster_option;
 
 const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *),
                            const char *name, const char *old_name, const char *def_value);
 
 const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len,
                              const char *name);
 
 void config_metadata(const char *name, const char *version, const char *desc_short,
                      const char *desc_long, pe_cluster_option * option_list, int len);
 
 void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len);
 gboolean check_time(const char *value);
 gboolean check_timer(const char *value);
 gboolean check_boolean(const char *value);
 gboolean check_number(const char *value);
 gboolean check_positive_number(const char *value);
 gboolean check_quorum(const char *value);
 gboolean check_script(const char *value);
 gboolean check_utilization(const char *value);
 long crm_get_sbd_timeout(void);
 long crm_auto_watchdog_timeout(void);
 gboolean check_sbd_timeout(const char *value);
 void crm_args_fini(void);
 
 /* char2score */
 extern int node_score_red;
 extern int node_score_green;
 extern int node_score_yellow;
 
 /* Assorted convenience functions */
 void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile);
 
 // printf-style format to create operation ID from resource, action, interval
 #define CRM_OP_FMT "%s_%s_%u"
 
 static inline long long
 crm_clear_bit(const char *function, int line, const char *target, long long word, long long bit)
 {
     long long rc = (word & ~bit);
 
     if (rc == word) {
         /* Unchanged */
     } else if (target) {
         crm_trace("Bit 0x%.8llx for %s cleared by %s:%d", bit, target, function, line);
     } else {
         crm_trace("Bit 0x%.8llx cleared by %s:%d", bit, function, line);
     }
 
     return rc;
 }
 
 static inline long long
 crm_set_bit(const char *function, int line, const char *target, long long word, long long bit)
 {
     long long rc = (word | bit);
 
     if (rc == word) {
         /* Unchanged */
     } else if (target) {
         crm_trace("Bit 0x%.8llx for %s set by %s:%d", bit, target, function, line);
     } else {
         crm_trace("Bit 0x%.8llx set by %s:%d", bit, function, line);
     }
 
     return rc;
 }
 
 #  define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, __LINE__, NULL, word, bit)
 #  define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, __LINE__, NULL, word, bit)
 
 char *generate_hash_key(const char *crm_msg_reference, const char *sys);
 
 const char *daemon_option(const char *option);
 void set_daemon_option(const char *option, const char *value);
 gboolean daemon_option_enabled(const char *daemon, const char *option);
 void strip_text_nodes(xmlNode * xml);
 void pcmk_panic(const char *origin);
 pid_t pcmk_locate_sbd(void);
 
 #  define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); }
 #  define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); }
 
 #  define F_ATTRD_KEY		"attr_key"
 #  define F_ATTRD_ATTRIBUTE	"attr_name"
 #  define F_ATTRD_REGEX 	"attr_regex"
 #  define F_ATTRD_TASK		"task"
 #  define F_ATTRD_VALUE		"attr_value"
 #  define F_ATTRD_SET		"attr_set"
 #  define F_ATTRD_IS_REMOTE	"attr_is_remote"
 #  define F_ATTRD_IS_PRIVATE     "attr_is_private"
 #  define F_ATTRD_SECTION	"attr_section"
 #  define F_ATTRD_DAMPEN	"attr_dampening"
 #  define F_ATTRD_HOST		"attr_host"
 #  define F_ATTRD_HOST_ID	"attr_host_id"
 #  define F_ATTRD_USER		"attr_user"
 #  define F_ATTRD_WRITER	"attr_writer"
 #  define F_ATTRD_VERSION	"attr_version"
 #  define F_ATTRD_RESOURCE          "attr_resource"
 #  define F_ATTRD_OPERATION         "attr_clear_operation"
 #  define F_ATTRD_INTERVAL          "attr_clear_interval"
 #  define F_ATTRD_IS_FORCE_WRITE "attrd_is_force_write"
 
 /* attrd operations */
 #  define ATTRD_OP_PEER_REMOVE   "peer-remove"
 #  define ATTRD_OP_UPDATE        "update"
 #  define ATTRD_OP_UPDATE_BOTH   "update-both"
 #  define ATTRD_OP_UPDATE_DELAY  "update-delay"
 #  define ATTRD_OP_QUERY         "query"
 #  define ATTRD_OP_REFRESH       "refresh"
 #  define ATTRD_OP_FLUSH         "flush"
 #  define ATTRD_OP_SYNC          "sync"
 #  define ATTRD_OP_SYNC_RESPONSE "sync-response"
 #  define ATTRD_OP_CLEAR_FAILURE "clear-failure"
 
 #  define PCMK_ENV_PHYSICAL_HOST "physical_host"
 
 
 #  if SUPPORT_COROSYNC
 #    include <qb/qbipc_common.h>
 #    include <corosync/corotypes.h>
 typedef struct qb_ipc_request_header cs_ipc_header_request_t;
 typedef struct qb_ipc_response_header cs_ipc_header_response_t;
 #  else
 typedef struct {
     int size __attribute__ ((aligned(8)));
     int id __attribute__ ((aligned(8)));
 } __attribute__ ((aligned(8))) cs_ipc_header_request_t;
 
 typedef struct {
     int size __attribute__ ((aligned(8)));
     int id __attribute__ ((aligned(8)));
     int error __attribute__ ((aligned(8)));
 } __attribute__ ((aligned(8))) cs_ipc_header_response_t;
 
 #  endif
 
 void
 attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb);
 void
 stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb);
 
 qb_ipcs_service_t *
 crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb);
 
 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);
 
 void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
         qb_ipcs_service_t *ipcs_rw,
         qb_ipcs_service_t *ipcs_shm);
 
 static inline void *realloc_safe(void *ptr, size_t size)
 {
     void *ret = realloc(ptr, size);
 
     if (ret == NULL) {
         free(ptr); /* make coverity happy */
         abort();
     }
 
     return ret;
 }
 
 const char *crm_xml_add_last_written(xmlNode *xml_node);
 void crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth);
 void crm_buffer_add_char(char **buffer, int *offset, int *max, char c);
 
 gboolean crm_digest_verify(xmlNode *input, const char *expected);
 
 /* cross-platform compatibility functions */
 char *crm_compat_realpath(const char *path);
 
 /* IPC Proxy Backend Shared Functions */
 typedef struct remote_proxy_s {
     char *node_name;
     char *session_id;
 
     gboolean is_local;
 
     crm_ipc_t *ipc;
     mainloop_io_t *source;
     uint32_t last_request_id;
     lrmd_t *lrm;
 
 } remote_proxy_t;
 
 remote_proxy_t *remote_proxy_new(
     lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks,
     const char *node_name, const char *session_id, const char *channel);
 
 int  remote_proxy_check(lrmd_t *lrmd, GHashTable *hash);
 void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg);
 void remote_proxy_ack_shutdown(lrmd_t *lrmd);
 void remote_proxy_nack_shutdown(lrmd_t *lrmd);
 
 int  remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata);
 void remote_proxy_disconnected(gpointer data);
 void remote_proxy_free(gpointer data);
 
 void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg);
 void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id);
 
 #endif                          /* CRM_INTERNAL__H */
diff --git a/lib/common/utils.c b/lib/common/utils.c
index 6a81d95a74..24da58f622 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,1197 +1,1200 @@
 /*
- * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2019 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 #include <dlfcn.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #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 <pwd.h>
 #include <time.h>
 #include <libgen.h>
 #include <signal.h>
 
 #include <qb/qbdefs.h>
 
 #include <crm/crm.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 <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;
 
 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;
 
 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, CRM_MINUS_INFINITY_S)) {
 
     } else if (safe_str_eq(value, CRM_INFINITY_S)) {
 
     } else {
         crm_int_helper(value, NULL);
     }
 
     if (errno != 0) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_positive_number(const char* value)
 {
     if (safe_str_eq(value, CRM_INFINITY_S) || (crm_int_helper(value, NULL))) {
         return TRUE;
     }
     return FALSE;
 }
 
 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;
 }
 
 void
 crm_args_fini()
 {
     free(crm_short_options);
     crm_short_options = NULL;
 }
 
 int
 char2score(const char *score)
 {
     int score_f = 0;
 
     if (score == NULL) {
 
     } else if (safe_str_eq(score, CRM_MINUS_INFINITY_S)) {
         score_f = -CRM_SCORE_INFINITY;
 
     } else if (safe_str_eq(score, CRM_INFINITY_S)) {
         score_f = CRM_SCORE_INFINITY;
 
     } else if (safe_str_eq(score, CRM_PLUS_INFINITY_S)) {
         score_f = CRM_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 > CRM_SCORE_INFINITY) {
             score_f = CRM_SCORE_INFINITY;
 
         } else if (score_f < 0 && score_f < -CRM_SCORE_INFINITY) {
             score_f = -CRM_SCORE_INFINITY;
         }
     }
 
     return score_f;
 }
 
 char *
 score2char_stack(int score, char *buf, size_t len)
 {
     if (score >= CRM_SCORE_INFINITY) {
         strncpy(buf, CRM_INFINITY_S, 9);
     } else if (score <= -CRM_SCORE_INFINITY) {
         strncpy(buf, CRM_MINUS_INFINITY_S , 10);
     } else {
         return crm_itoa_stack(score, buf, len);
     }
 
     return buf;
 }
 
 char *
 score2char(int score)
 {
     if (score >= CRM_SCORE_INFINITY) {
         return strdup(CRM_INFINITY_S);
 
     } else if (score <= -CRM_SCORE_INFINITY) {
         return strdup(CRM_MINUS_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;
     char *new_value = NULL;
 
     CRM_ASSERT(name != NULL);
 
     if (options) {
         value = g_hash_table_lookup(options, name);
 
         if ((value == NULL) && old_name) {
             value = g_hash_table_lookup(options, old_name);
             if (value != NULL) {
                 crm_config_warn("Support for legacy name '%s' for cluster option '%s'"
                                 " is deprecated and will be removed in a future release",
                                 old_name, name);
 
                 // Inserting copy with current name ensures we only warn once
                 new_value = strdup(value);
                 g_hash_table_insert(options, strdup(name), new_value);
                 value = new_value;
             }
         }
 
         if (value && validate && (validate(value) == FALSE)) {
             crm_config_err("Resetting cluster option '%s' to default: value '%s' is invalid",
                            name, value);
             value = NULL;
         }
 
         if (value) {
             return value;
         }
     }
 
     // No value found, use default
     value = def_value;
 
     if (value == NULL) {
         crm_trace("No value or default provided for cluster option '%s'",
                   name);
         return NULL;
     }
 
     if (validate) {
         CRM_CHECK(validate(value) != FALSE,
                   crm_err("Bug: default value for cluster option '%s' is invalid", name);
                   return NULL);
     }
 
     crm_trace("Using default value '%s' for cluster option '%s'",
               value, name);
     if (options) {
         new_value = strdup(value);
         g_hash_table_insert(options, strdup(name), new_value);
         value = new_value;
     }
     return value;
 }
 
 const char *
 get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name)
 {
     const char *value = NULL;
 
     for (int lpc = 0; lpc < len; lpc++) {
         if (safe_str_eq(name, option_list[lpc].name)) {
             value = cluster_option(options,
                                    option_list[lpc].is_valid,
                                    option_list[lpc].name,
                                    option_list[lpc].alt_name,
                                    option_list[lpc].default_value);
             return value;
         }
     }
     CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
     return NULL;
 }
 
 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 = pcmk_ok;
     char *buffer = NULL;
     struct passwd pwd;
     struct passwd *pwentry = NULL;
 
     buffer = calloc(1, PW_BUFFER_LEN);
     rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
     if (pwentry) {
         if (uid) {
             *uid = pwentry->pw_uid;
         }
         if (gid) {
             *gid = pwentry->pw_gid;
         }
         crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
 
     } else {
         rc = rc? -rc : -EINVAL;
         crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
     }
 
     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
 
 guint
 crm_parse_interval_spec(const char *input)
 {
     long long msec = 0;
 
     if (input == NULL) {
         return 0;
 
     } else if (input[0] != 'P') {
         long long tmp = crm_get_msec(input);
 
         if(tmp > 0) {
             msec = tmp;
         }
 
     } else {
         crm_time_t *period_s = crm_time_parse_duration(input);
 
         msec = 1000 * crm_time_get_seconds(period_s);
         crm_time_free(period_s);
     }
 
     return (msec <= 0)? 0 : ((msec >= G_MAXUINT)? G_MAXUINT : (guint) 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;
 }
 
 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);
 }
 
 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(CRM_EX_ERROR);
     }
 
     pid = fork();
     if (pid < 0) {
         fprintf(stderr, "%s: could not start daemon\n", name);
         crm_perror(LOG_ERR, "fork");
         crm_exit(CRM_EX_OSERR);
 
     } else if (pid > 0) {
         crm_exit(CRM_EX_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(CRM_EX_ERROR);
     }
 
     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('?', CRM_EX_USAGE);
                 break;
             case '?':
                 crm_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
                 break;
         }
         return flag;
     }
 #endif
 
     if (crm_short_options) {
         return getopt(argc, argv, crm_short_options);
     }
 
     return -1;
 }
 
-crm_exit_t
+void
 crm_help(char cmd, crm_exit_t 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);
+    crm_exit(exit_code);
+    while(1); // above does not return
 }
 
 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 the CIB manager: exiting and inhibiting respawn");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 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 pacemaker-attrd server: exiting and inhibiting respawn");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 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 fencer: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 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(CRM_EX_FATAL);
         }
         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(CRM_EX_FATAL);
         }
     }
 
     return a_function;
 }
 
 #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;
 }
 
 /*!
  * \brief Get name to be used as identifier for cluster messages
  *
  * \param[in] name  Actual system name to check
  *
  * \return Non-NULL cluster message identifier corresponding to name
  *
  * \note The Pacemaker daemons were renamed in version 2.0.0, but the old names
  *       must continue to be used as the identifier for cluster messages, so
  *       that mixed-version clusters are possible during a rolling upgrade.
  */
 const char *
 pcmk_message_name(const char *name)
 {
     if (name == NULL) {
         return "unknown";
 
     } else if (!strcmp(name, "pacemaker-attrd")) {
         return "attrd";
 
     } else if (!strcmp(name, "pacemaker-based")) {
         return CRM_SYSTEM_CIB;
 
     } else if (!strcmp(name, "pacemaker-controld")) {
         return CRM_SYSTEM_CRMD;
 
     } else if (!strcmp(name, "pacemaker-execd")) {
         return CRM_SYSTEM_LRMD;
 
     } else if (!strcmp(name, "pacemaker-fenced")) {
         return "stonith-ng";
 
     } else if (!strcmp(name, "pacemaker-schedulerd")) {
         return CRM_SYSTEM_PENGINE;
 
     } else {
         return name;
     }
 }
 
 /*!
  * \brief Check whether a string represents a cluster daemon name
  *
  * \param[in] name  String to check
  *
  * \return TRUE if name is standard client name used by daemons, FALSE otherwise
  */
 bool
 crm_is_daemon_name(const char *name)
 {
     name = pcmk_message_name(name);
     return (!strcmp(name, CRM_SYSTEM_CRMD)
             || !strcmp(name, CRM_SYSTEM_STONITHD)
             || !strcmp(name, "stonith-ng")
             || !strcmp(name, "attrd")
             || !strcmp(name, CRM_SYSTEM_CIB)
             || !strcmp(name, CRM_SYSTEM_MCP)
             || !strcmp(name, CRM_SYSTEM_DC)
             || !strcmp(name, CRM_SYSTEM_TENGINE)
             || !strcmp(name, CRM_SYSTEM_LRMD));
 }
 
 #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
 
 /*!
  * \brief Get the local hostname
  *
  * \return Newly allocated string with name, or NULL (and set errno) on error
  */
 char *
 pcmk_hostname()
 {
     struct utsname hostinfo;
 
     return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
 }
diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c
index 353e137b83..f3eeb9b0e0 100644
--- a/tools/attrd_updater.c
+++ b/tools/attrd_updater.c
@@ -1,370 +1,372 @@
 /*
- * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2019 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <libgen.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/ipc.h>
 
 #include <crm/attrd.h>
 
 /* *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\n"},
 
     {"name",    1, 0, 'n', "The attribute's name"},
 
     {"-spacer-",1, 0, '-', "\nCommands:"},
     {"update",  1, 0, 'U', "Update the attribute's value in pacemaker-attrd. If this causes the value to change, it will also be updated in the cluster configuration"},
     {"update-both", 1, 0, 'B', "Update the attribute's value and time to wait (dampening) in pacemaker-attrd. If this causes the value or dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
     {"update-delay", 0, 0, 'Y', "Update the attribute's dampening in pacemaker-attrd (requires -d/--delay). If this causes the dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
     {"query",   0, 0, 'Q', "\tQuery the attribute's value from pacemaker-attrd"},
     {"delete",  0, 0, 'D', "\tDelete the attribute in pacemaker-attrd.  If a value was previously set, it will also be removed from the cluster configuration"},
     {"refresh", 0, 0, 'R', "\t(Advanced) Force the pacemaker-attrd daemon to resend all current values to the CIB\n"},
 
     {"-spacer-",1, 0, '-', "\nAdditional options:"},
     {"delay",   1, 0, 'd', "The time to wait (dampening) in seconds for further changes before writing"},
     {"set",     1, 0, 's', "(Advanced) The attribute set in which to place the value"},
     {"node",    1, 0, 'N', "Set the attribute for the named node (instead of the local one)"},
     {"all",     0, 0, 'A', "Show values of the attribute for all nodes (query only)"},
     /* lifetime could be implemented if there is sufficient user demand */
     {"lifetime",1, 0, 'l', "(Deprecated) Lifetime of the node attribute (silently ignored by cluster)"},
     {"private", 0, 0, 'p', "\tIf this creates a new attribute, never write the attribute to the CIB"},
 
     /* Legacy options */
     {"quiet",   0, 0, 'q', NULL, pcmk_option_hidden},
     {"update",  1, 0, 'v', NULL, pcmk_option_hidden},
     {"section", 1, 0, 'S', NULL, pcmk_option_hidden},
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 static int do_query(const char *attr_name, const char *attr_node, gboolean query_all);
 static int do_update(char command, const char *attr_node, const char *attr_name,
                      const char *attr_value, const char *attr_section,
                      const char *attr_set, const char *attr_dampen, int attr_options);
 
 // Free memory at exit to make analyzers happy
 #define cleanup_memory() \
     free(attr_dampen); \
     free(attr_name); \
     free(attr_node); \
     free(attr_section); \
     free(attr_set);
 
 int
 main(int argc, char **argv)
 {
     int index = 0;
     int argerr = 0;
     int attr_options = attrd_opt_none;
     int flag;
     crm_exit_t exit_code = CRM_EX_OK;
     char *attr_node = NULL;
     char *attr_name = NULL;
     char *attr_set = NULL;
     char *attr_section = NULL;
     char *attr_dampen = NULL;
     const char *attr_value = NULL;
     char command = 'Q';
 
     gboolean query_all = FALSE;
 
     crm_log_cli_init("attrd_updater");
     crm_set_options(NULL, "command -n attribute [options]", long_options,
                     "Tool for updating cluster node attributes");
 
     if (argc < 2) {
-        return crm_help('?', CRM_EX_USAGE);
+        crm_help('?', CRM_EX_USAGE);
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '?':
             case '$':
                 cleanup_memory();
-                return crm_help(flag, CRM_EX_OK);
+                crm_help(flag, CRM_EX_OK);
                 break;
             case 'n':
                 attr_name = strdup(optarg);
                 break;
             case 's':
                 attr_set = strdup(optarg);
                 break;
             case 'd':
                 attr_dampen = strdup(optarg);
                 break;
             case 'l':
             case 'S':
                 attr_section = strdup(optarg);
                 break;
             case 'N':
                 attr_node = strdup(optarg);
                 break;
             case 'A':
                 query_all = TRUE;
                 break;
             case 'p':
                 set_bit(attr_options, attrd_opt_private);
                 break;
             case 'q':
                 break;
             case 'Y':
                 command = flag;
                 crm_log_args(argc, argv); /* Too much? */
                 break;
             case 'Q':
             case 'B':
             case 'R':
             case 'D':
             case 'U':
             case 'v':
                 command = flag;
                 attr_value = optarg;
                 crm_log_args(argc, argv); /* Too much? */
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (command != 'R' && attr_name == NULL) {
         ++argerr;
     }
 
     if (argerr) {
         cleanup_memory();
-        return crm_help('?', CRM_EX_USAGE);
+        crm_help('?', CRM_EX_USAGE);
     }
 
     if (command == 'Q') {
         exit_code = crm_errno2exit(do_query(attr_name, attr_node, query_all));
     } else {
         /* @TODO We don't know whether the specified node is a Pacemaker Remote
          * node or not, so we can't set attrd_opt_remote when appropriate.
          * However, it's not a big problem, because pacemaker-attrd will learn
          * and remember a node's "remoteness".
          */
         exit_code = crm_errno2exit(do_update(command,
                                    attrd_get_target(attr_node), attr_name,
                                    attr_value, attr_section, attr_set,
                                    attr_dampen, attr_options));
     }
 
     cleanup_memory();
     return crm_exit(exit_code);
 }
 
 /*!
  * \internal
  * \brief Submit a query request to pacemaker-attrd and wait for reply
  *
  * \param[in] name    Name of attribute to query
  * \param[in] host    Query applies to this host only (or all hosts if NULL)
  * \param[out] reply  On success, will be set to new XML tree with reply
  *
  * \return pcmk_ok on success, -errno on error
  * \note On success, caller is responsible for freeing result via free_xml(*reply)
  */
 static int
 send_attrd_query(const char *name, const char *host, xmlNode **reply)
 {
     int rc;
     crm_ipc_t *ipc;
     xmlNode *query;
 
     /* Build the query XML */
     query = create_xml_node(NULL, __FUNCTION__);
     if (query == NULL) {
         return -ENOMEM;
     }
     crm_xml_add(query, F_TYPE, T_ATTRD);
     crm_xml_add(query, F_ORIG, crm_system_name);
     crm_xml_add(query, F_ATTRD_HOST, host);
     crm_xml_add(query, F_ATTRD_TASK, ATTRD_OP_QUERY);
     crm_xml_add(query, F_ATTRD_ATTRIBUTE, name);
 
     /* Connect to pacemaker-attrd, send query XML and get reply */
     crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
     ipc = crm_ipc_new(T_ATTRD, 0);
     if (crm_ipc_connect(ipc) == FALSE) {
         crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
         rc = -ENOTCONN;
     } else {
         rc = crm_ipc_send(ipc, query, crm_ipc_flags_none|crm_ipc_client_response, 0, reply);
         if (rc > 0) {
             rc = pcmk_ok;
         }
         crm_ipc_close(ipc);
     }
 
     free_xml(query);
     return(rc);
 }
 
 /*!
  * \brief Validate pacemaker-attrd's XML reply to an query
  *
  * param[in] reply      Root of reply XML tree to validate
  * param[in] attr_name  Name of attribute that was queried
  *
  * \return pcmk_ok on success,
  *         -errno on error (-ENXIO = requested attribute does not exist)
  */
 static int
 validate_attrd_reply(xmlNode *reply, const char *attr_name)
 {
     const char *reply_attr;
 
     if (reply == NULL) {
         fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n",
                 attr_name);
         return -pcmk_err_schema_validation;
     }
     crm_log_xml_trace(reply, "Reply");
 
     reply_attr = crm_element_value(reply, F_ATTRD_ATTRIBUTE);
     if (reply_attr == NULL) {
         fprintf(stderr, "Could not query value of %s: attribute does not exist\n",
                 attr_name);
         return -ENXIO;
     }
 
     if (safe_str_neq(crm_element_value(reply, F_TYPE), T_ATTRD)
         || (crm_element_value(reply, F_ATTRD_VERSION) == NULL)
         || strcmp(reply_attr, attr_name)) {
             fprintf(stderr,
                     "Could not query value of %s: reply did not contain expected identification\n",
                     attr_name);
             return -pcmk_err_schema_validation;
     }
     return pcmk_ok;
 }
 
 /*!
  * \brief Print the attribute values in a pacemaker-attrd XML query reply
  *
  * \param[in] reply     Root of XML tree with query reply
  * \param[in] attr_name Name of attribute that was queried
  *
  * \return TRUE if any values were printed
  */
 static gboolean
 print_attrd_values(xmlNode *reply, const char *attr_name)
 {
     xmlNode *child;
     const char *reply_host, *reply_value;
     gboolean have_values = FALSE;
 
     /* Iterate through reply's XML tags (a node tag for each host-value pair) */
     for (child = __xml_first_child(reply); child != NULL; child = __xml_next(child)) {
         if (safe_str_neq((const char*)child->name, XML_CIB_TAG_NODE)) {
             crm_warn("Ignoring unexpected %s tag in query reply", child->name);
         } else {
             reply_host = crm_element_value(child, F_ATTRD_HOST);
             reply_value = crm_element_value(child, F_ATTRD_VALUE);
 
             if (reply_host == NULL) {
                 crm_warn("Ignoring %s tag without %s attribute in query reply",
                          XML_CIB_TAG_NODE, F_ATTRD_HOST);
             } else {
                 printf("name=\"%s\" host=\"%s\" value=\"%s\"\n",
                        attr_name, reply_host, (reply_value? reply_value : ""));
                 have_values = TRUE;
             }
         }
     }
     return have_values;
 }
 
 /*!
  * \brief Submit a query to pacemaker-attrd and print reply
  *
  * \param[in] attr_name  Name of attribute to be affected by request
  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
  *
  * \return pcmk_ok on success, -errno on error
  */
 static int
 do_query(const char *attr_name, const char *attr_node, gboolean query_all)
 {
     xmlNode *reply = NULL;
     int rc;
 
     /* Decide which node(s) to query */
     if (query_all == TRUE) {
         attr_node = NULL;
     } else {
         attr_node = attrd_get_target(attr_node);
     }
 
     /* Build and send pacemaker-attrd request, and get XML reply */
     rc = send_attrd_query(attr_name, attr_node, &reply);
     if (rc != pcmk_ok) {
         fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc);
         return rc;
     }
 
     /* Validate the XML reply */
     rc = validate_attrd_reply(reply, attr_name);
     if (rc != pcmk_ok) {
         if (reply != NULL) {
             free_xml(reply);
         }
         return rc;
     }
 
     /* Print the values from the reply */
     if (print_attrd_values(reply, attr_name) == FALSE) {
         fprintf(stderr,
                 "Could not query value of %s: reply had attribute name but no host values\n",
                 attr_name);
         free_xml(reply);
         return -pcmk_err_schema_validation;
     }
 
     return pcmk_ok;
 }
 
 static int
 do_update(char command, const char *attr_node, const char *attr_name,
           const char *attr_value, const char *attr_section,
           const char *attr_set, const char *attr_dampen, int attr_options)
 {
     int rc = attrd_update_delegate(NULL, command, attr_node, attr_name,
                                    attr_value, attr_section, attr_set,
                                    attr_dampen, NULL, attr_options);
     if (rc != pcmk_ok) {
         fprintf(stderr, "Could not update %s=%s: %s (%d)\n", attr_name, attr_value, pcmk_strerror(rc), rc);
     }
     return rc;
 }
diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 76c3c78732..bb07d5b0e1 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1,4451 +1,4453 @@
 /*
- * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2019 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 
 #include <crm/crm.h>
 
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
 #include <sys/utsname.h>
 
 #include <crm/msg_xml.h>
 #include <crm/services.h>
 #include <crm/lrmd.h>
 #include <crm/common/internal.h>  /* crm_ends_with_ext */
 #include <crm/common/ipc.h>
 #include <crm/common/mainloop.h>
 #include <crm/common/util.h>
 #include <crm/common/xml.h>
 
 #include <crm/cib/internal.h>
 #include <crm/pengine/status.h>
 #include <crm/pengine/internal.h>
 #include <../lib/pengine/unpack.h>
 #include <pacemaker-internal.h>
 #include <crm/stonith-ng.h>
 
 static void clean_up_connections(void);
 static crm_exit_t clean_up(crm_exit_t exit_code);
 static void crm_diff_update(const char *event, xmlNode * msg);
 static gboolean mon_refresh_display(gpointer user_data);
 static int cib_connect(gboolean full);
 static void mon_st_callback_event(stonith_t * st, stonith_event_t * e);
 static void mon_st_callback_display(stonith_t * st, stonith_event_t * e);
 static void kick_refresh(gboolean data_updated);
 static char *get_node_display_name(node_t *node);
 
 /*
  * Definitions indicating which items to print
  */
 
 #define mon_show_times         (0x0001U)
 #define mon_show_stack         (0x0002U)
 #define mon_show_dc            (0x0004U)
 #define mon_show_count         (0x0008U)
 #define mon_show_nodes         (0x0010U)
 #define mon_show_resources     (0x0020U)
 #define mon_show_attributes    (0x0040U)
 #define mon_show_failcounts    (0x0080U)
 #define mon_show_operations    (0x0100U)
 #define mon_show_tickets       (0x0200U)
 #define mon_show_bans          (0x0400U)
 #define mon_show_fence_history (0x0800U)
 
 #define mon_show_headers       (mon_show_times | mon_show_stack | mon_show_dc \
                                | mon_show_count)
 #define mon_show_default       (mon_show_headers | mon_show_nodes \
                                | mon_show_resources)
 #define mon_show_all           (mon_show_default | mon_show_attributes \
                                | mon_show_failcounts | mon_show_operations \
                                | mon_show_tickets | mon_show_bans \
                                | mon_show_fence_history)
 
 static unsigned int show = mon_show_default;
 
 /*
  * Definitions indicating how to output
  */
 
 enum mon_output_format_e {
     mon_output_none,
     mon_output_monitor,
     mon_output_plain,
     mon_output_console,
     mon_output_xml,
     mon_output_html,
     mon_output_cgi
 } output_format = mon_output_console;
 
 static char *output_filename = NULL;   /* if sending output to a file, its name */
 
 /* other globals */
 static char *pid_file = NULL;
 
 static gboolean group_by_node = FALSE;
 static gboolean inactive_resources = FALSE;
 static int reconnect_msec = 5000;
 static gboolean daemonize = FALSE;
 static GMainLoop *mainloop = NULL;
 static guint timer_id = 0;
 static mainloop_timer_t *refresh_timer = NULL;
 static pe_working_set_t *mon_data_set = NULL;
 static GList *attr_list = NULL;
 
 static const char *external_agent = NULL;
 static const char *external_recipient = NULL;
 
 static cib_t *cib = NULL;
 static stonith_t *st = NULL;
 static xmlNode *current_cib = NULL;
 
 static gboolean one_shot = FALSE;
 static gboolean has_warnings = FALSE;
 static gboolean print_timing = FALSE;
 static gboolean watch_fencing = FALSE;
 static gboolean fence_history = FALSE;
 static gboolean fence_full_history = FALSE;
 static gboolean fence_connect = FALSE;
 static int fence_history_level = 1;
 static gboolean print_brief = FALSE;
 static gboolean print_pending = TRUE;
 static gboolean print_clone_detail = FALSE;
 #if CURSES_ENABLED
 static gboolean curses_console_initialized = FALSE;
 #endif
 
 /* FIXME allow, detect, and correctly interpret glob pattern or regex? */
 const char *print_neg_location_prefix = "";
 
 /* Never display node attributes whose name starts with one of these prefixes */
 #define FILTER_STR { CRM_FAIL_COUNT_PREFIX, CRM_LAST_FAILURE_PREFIX,       \
                      "shutdown", "terminate", "standby", "probe_complete", \
                      "#", NULL }
 
 long last_refresh = 0;
 crm_trigger_t *refresh_trigger = NULL;
 
 /* Define exit codes for monitoring-compatible output
  * For nagios plugins, the possibilities are
  * OK=0, WARN=1, CRIT=2, and UNKNOWN=3
  */
 #define MON_STATUS_WARN    CRM_EX_ERROR
 #define MON_STATUS_CRIT    CRM_EX_INVALID_PARAM
 #define MON_STATUS_UNKNOWN CRM_EX_UNIMPLEMENT_FEATURE
 
 /* Convenience macro for prettifying output (e.g. "node" vs "nodes") */
 #define s_if_plural(i) (((i) == 1)? "" : "s")
 
 #if CURSES_ENABLED
 #  define print_dot() if (output_format == mon_output_console) { \
 	printw(".");				\
 	clrtoeol();				\
 	refresh();				\
     } else {					\
 	fprintf(stdout, ".");			\
     }
 #else
 #  define print_dot() fprintf(stdout, ".");
 #endif
 
 #if CURSES_ENABLED
 #  define print_as(fmt, args...) if (output_format == mon_output_console) { \
 	printw(fmt, ##args);				\
 	clrtoeol();					\
 	refresh();					\
     } else {						\
 	fprintf(stdout, fmt, ##args);			\
     }
 #else
 #  define print_as(fmt, args...) fprintf(stdout, fmt, ##args);
 #endif
 
 static void
 blank_screen(void)
 {
 #if CURSES_ENABLED
     int lpc = 0;
 
     for (lpc = 0; lpc < LINES; lpc++) {
         move(lpc, 0);
         clrtoeol();
     }
     move(0, 0);
     refresh();
 #endif
 }
 
 static gboolean
 mon_timer_popped(gpointer data)
 {
     int rc = pcmk_ok;
 
 #if CURSES_ENABLED
     if (output_format == mon_output_console) {
         clear();
         refresh();
     }
 #endif
 
     if (timer_id > 0) {
         g_source_remove(timer_id);
         timer_id = 0;
     }
 
     print_as("Reconnecting...\n");
     rc = cib_connect(TRUE);
 
     if (rc != pcmk_ok) {
         timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
     }
     return FALSE;
 }
 
 static void
 mon_cib_connection_destroy(gpointer user_data)
 {
     print_as("Connection to the cluster-daemons terminated\n");
     if (refresh_timer != NULL) {
         /* we'll trigger a refresh after reconnect */
         mainloop_timer_stop(refresh_timer);
     }
     if (timer_id) {
         /* we'll trigger a new reconnect-timeout at the end */
         g_source_remove(timer_id);
         timer_id = 0;
     }
     if (st) {
         /* the client API won't properly reconnect notifications
          * if they are still in the table - so remove them
          */
         st->cmds->remove_notification(st, T_STONITH_NOTIFY_DISCONNECT);
         st->cmds->remove_notification(st, T_STONITH_NOTIFY_FENCE);
         st->cmds->remove_notification(st, T_STONITH_NOTIFY_HISTORY);
         if (st->state != stonith_disconnected) {
             st->cmds->disconnect(st);
         }
     }
     if (cib) {
         cib->cmds->signoff(cib);
         timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
     }
     return;
 }
 
 /*
  * Mainloop signal handler.
  */
 static void
 mon_shutdown(int nsig)
 {
     clean_up(CRM_EX_OK);
 }
 
 #if ON_DARWIN
 #  define sighandler_t sig_t
 #endif
 
 #if CURSES_ENABLED
 #  ifndef HAVE_SIGHANDLER_T
 typedef void (*sighandler_t) (int);
 #  endif
 static sighandler_t ncurses_winch_handler;
 static void
 mon_winresize(int nsig)
 {
     static int not_done;
     int lines = 0, cols = 0;
 
     if (!not_done++) {
         if (ncurses_winch_handler)
             /* the original ncurses WINCH signal handler does the
              * magic of retrieving the new window size;
              * otherwise, we'd have to use ioctl or tgetent */
             (*ncurses_winch_handler) (SIGWINCH);
         getmaxyx(stdscr, lines, cols);
         resizeterm(lines, cols);
         mainloop_set_trigger(refresh_trigger);
     }
     not_done--;
 }
 #endif
 
 static int
 cib_connect(gboolean full)
 {
     int rc = pcmk_ok;
     static gboolean need_pass = TRUE;
 
     CRM_CHECK(cib != NULL, return -EINVAL);
 
     if (getenv("CIB_passwd") != NULL) {
         need_pass = FALSE;
     }
 
     if ((fence_connect) && (st == NULL)) {
         st = stonith_api_new();
     }
 
     if ((fence_connect) && (st->state == stonith_disconnected)) {
         crm_trace("Connecting to stonith");
         rc = st->cmds->connect(st, crm_system_name, NULL);
         if (rc == pcmk_ok) {
             crm_trace("Setting up stonith callbacks");
             if (watch_fencing) {
                 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
                                                 mon_st_callback_event);
                 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback_event);
             } else {
                 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
                                                 mon_st_callback_display);
                 st->cmds->register_notification(st, T_STONITH_NOTIFY_HISTORY, mon_st_callback_display);
             }
         }
     }
 
     if (cib->state != cib_connected_query && cib->state != cib_connected_command) {
         crm_trace("Connecting to the CIB");
         if ((output_format == mon_output_console) && need_pass && (cib->variant == cib_remote)) {
             need_pass = FALSE;
             print_as("Password:");
         }
 
         rc = cib->cmds->signon(cib, crm_system_name, cib_query);
 
         if (rc != pcmk_ok) {
             return rc;
         }
 
         rc = cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
         if (rc == pcmk_ok) {
             mon_refresh_display(NULL);
         }
 
         if (rc == pcmk_ok && full) {
             if (rc == pcmk_ok) {
                 rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy);
                 if (rc == -EPROTONOSUPPORT) {
                     print_as
                         ("Notification setup not supported, won't be able to reconnect after failure");
                     if (output_format == mon_output_console) {
                         sleep(2);
                     }
                     rc = pcmk_ok;
                 }
 
             }
 
             if (rc == pcmk_ok) {
                 cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
                 rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
             }
 
             if (rc != pcmk_ok) {
                 print_as("Notification setup failed, could not monitor CIB actions");
                 if (output_format == mon_output_console) {
                     sleep(2);
                 }
                 clean_up_connections();
             }
         }
     }
     return rc;
 }
 
 /* *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', "\tDisplay only essential output" },
 
     {"-spacer-",	1, 0, '-', "\nModes (mutually exclusive):"},
     {"as-html",        1, 0, 'h', "\tWrite cluster status to the named html file"},
     {"as-xml",         0, 0, 'X', "\t\tWrite cluster status as xml to stdout. This will enable one-shot mode."},
     {"web-cgi",        0, 0, 'w', "\t\tWeb mode with output suitable for CGI (preselected when run as *.cgi)"},
     {"simple-status",  0, 0, 's', "\tDisplay the cluster status once as a simple one line output (suitable for nagios)"},
     {"-spacer-",	1, 0, '-', "\nDisplay Options:"},
     {"group-by-node",  0, 0, 'n', "\tGroup resources by node"     },
     {"inactive",       0, 0, 'r', "\t\tDisplay inactive resources"  },
     {"failcounts",     0, 0, 'f', "\tDisplay resource fail counts"},
     {"operations",     0, 0, 'o', "\tDisplay resource operation history" },
     {"timing-details", 0, 0, 't', "\tDisplay resource operation history with timing details" },
     {"tickets",        0, 0, 'c', "\t\tDisplay cluster tickets"},
     {"watch-fencing",  0, 0, 'W', "\tListen for fencing events. For use with --external-agent"},
     {"fence-history",  2, 0, 'm', "Show fence history\n"
                                   "\t\t\t\t\t0=off, 1=failures and pending (default without option),\n"
                                   "\t\t\t\t\t2=add successes (default without value for option),\n"
                                   "\t\t\t\t\t3=show full history without reduction to most recent of each flavor"},
     {"neg-locations",  2, 0, 'L', "Display negative location constraints [optionally filtered by id prefix]"},
     {"show-node-attributes", 0, 0, 'A', "Display node attributes" },
     {"hide-headers",   0, 0, 'D', "\tHide all headers" },
     {"show-detail",    0, 0, 'R', "\tShow more details (node IDs, individual clone instances)" },
     {"brief",          0, 0, 'b', "\t\tBrief output" },
     {"pending",        0, 0, 'j', "\t\tDisplay pending state if 'record-pending' is enabled", pcmk_option_hidden},
 
     {"-spacer-",	1, 0, '-', "\nAdditional Options:"},
     {"interval",       1, 0, 'i', "\tUpdate frequency in seconds" },
     {"one-shot",       0, 0, '1', "\t\tDisplay the cluster status once on the console and exit"},
     {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses", !CURSES_ENABLED},
     {"daemonize",      0, 0, 'd', "\tRun in the background as a daemon"},
     {"pid-file",       1, 0, 'p', "\t(Advanced) Daemon pid file location"},
     {"external-agent",    1, 0, 'E', "A program to run when resource operations take place."},
     {"external-recipient",1, 0, 'e', "A recipient for your program (assuming you want the program to send something to someone)."},
 
 
     {"xml-file",       1, 0, 'x', NULL, pcmk_option_hidden},
 
     {"-spacer-",	1, 0, '-', "\nExamples:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "Display the cluster status on the console with updates as they occur:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_mon", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display the cluster status on the console just once then exit:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_mon -1", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display your cluster status, group resources by node, and include inactive resources in the list:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_mon --group-by-node --inactive", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Start crm_mon as a background daemon and have it write the cluster status to an HTML file:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_mon --daemonize --as-html /path/to/docroot/filename.html", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Start crm_mon and export the current cluster status as xml to stdout, then exit.:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_mon --as-xml", pcmk_option_example},
 
     {NULL, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 #if CURSES_ENABLED
 static const char *
 get_option_desc(char c)
 {
     int lpc;
 
     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
 
         if (long_options[lpc].name[0] == '-')
             continue;
 
         if (long_options[lpc].val == c) {
             static char *buf = NULL;
             const char *rv;
             char *nl;
 
             /* chop off tabs and cut at newline */
             free(buf); /* free string from last usage */
             buf = strdup(long_options[lpc].desc);
             rv = buf; /* make a copy to keep buf pointer unaltered
                          for freeing when we come by next time.
                          Like this the result stays valid until
                          the next call.
                        */
             while(isspace(rv[0])) {
                 rv++;
             }
             nl = strchr(rv, '\n');
             if (nl) {
                 *nl = '\0';
             }
             return rv;
         }
     }
 
     return NULL;
 }
 
 #define print_option_help(option, condition) \
     print_as("%c %c: \t%s\n", ((condition)? '*': ' '), option, get_option_desc(option));
 
 static gboolean
 detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer unused)
 {
     int c;
     gboolean config_mode = FALSE;
 
     while (1) {
 
         /* Get user input */
         c = getchar();
 
         switch (c) {
             case 'm':
                 if (!fence_history_level) {
                     fence_history = TRUE;
                     fence_connect = TRUE;
                     if (st == NULL) {
                         mon_cib_connection_destroy(NULL);
                     }
                 }
                 show ^= mon_show_fence_history;
                 break;
             case 'c':
                 show ^= mon_show_tickets;
                 break;
             case 'f':
                 show ^= mon_show_failcounts;
                 break;
             case 'n':
                 group_by_node = ! group_by_node;
                 break;
             case 'o':
                 show ^= mon_show_operations;
                 if ((show & mon_show_operations) == 0) {
                     print_timing = 0;
                 }
                 break;
             case 'r':
                 inactive_resources = ! inactive_resources;
                 break;
             case 'R':
                 print_clone_detail = ! print_clone_detail;
                 break;
             case 't':
                 print_timing = ! print_timing;
                 if (print_timing) {
                     show |= mon_show_operations;
                 }
                 break;
             case 'A':
                 show ^= mon_show_attributes;
                 break;
             case 'L':
                 show ^= mon_show_bans;
                 break;
             case 'D':
                 /* If any header is shown, clear them all, otherwise set them all */
                 if (show & mon_show_headers) {
                     show &= ~mon_show_headers;
                 } else {
                     show |= mon_show_headers;
                 }
                 break;
             case 'b':
                 print_brief = ! print_brief;
                 break;
             case 'j':
                 print_pending = ! print_pending;
                 break;
             case '?':
                 config_mode = TRUE;
                 break;
             default:
                 goto refresh;
         }
 
         if (!config_mode)
             goto refresh;
 
         blank_screen();
 
         print_as("Display option change mode\n");
         print_as("\n");
         print_option_help('c', show & mon_show_tickets);
         print_option_help('f', show & mon_show_failcounts);
         print_option_help('n', group_by_node);
         print_option_help('o', show & mon_show_operations);
         print_option_help('r', inactive_resources);
         print_option_help('t', print_timing);
         print_option_help('A', show & mon_show_attributes);
         print_option_help('L', show & mon_show_bans);
         print_option_help('D', (show & mon_show_headers) == 0);
         print_option_help('R', print_clone_detail);
         print_option_help('b', print_brief);
         print_option_help('j', print_pending);
         print_option_help('m', (show & mon_show_fence_history));
         print_as("\n");
         print_as("Toggle fields via field letter, type any other key to return");
     }
 
 refresh:
     mon_refresh_display(NULL);
     return TRUE;
 }
 #endif
 
 int
 main(int argc, char **argv)
 {
     int flag;
     int argerr = 0;
     int option_index = 0;
     int rc = pcmk_ok;
 
     pid_file = strdup("/tmp/ClusterMon.pid");
     crm_log_cli_init("crm_mon");
     crm_set_options(NULL, "mode [options]", long_options,
                     "Provides a summary of cluster's current state."
                     "\n\nOutputs varying levels of detail in a number of different formats.\n");
 
 #if !defined (ON_DARWIN) && !defined (ON_BSD)
     /* prevent zombies */
     signal(SIGCLD, SIG_IGN);
 #endif
 
     if (crm_ends_with_ext(argv[0], ".cgi") == TRUE) {
         output_format = mon_output_cgi;
         one_shot = TRUE;
     }
 
     /* to enable stonith-connection when called via some application like pcs
      * set environment-variable FENCE_HISTORY to desired level
      * so you don't have to modify this application
      */
     /* fence_history_level = crm_atoi(getenv("FENCE_HISTORY"), "0"); */
 
     while (1) {
         flag = crm_get_option(argc, argv, &option_index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case 'Q':
                 show &= ~mon_show_times;
                 break;
             case 'i':
                 reconnect_msec = crm_get_msec(optarg);
                 break;
             case 'n':
                 group_by_node = TRUE;
                 break;
             case 'r':
                 inactive_resources = TRUE;
                 break;
             case 'W':
                 watch_fencing = TRUE;
                 fence_connect = TRUE;
                 break;
             case 'm':
                 fence_history_level = crm_atoi(optarg, "2");
                 break;
             case 'd':
                 daemonize = TRUE;
                 break;
             case 't':
                 print_timing = TRUE;
                 show |= mon_show_operations;
                 break;
             case 'o':
                 show |= mon_show_operations;
                 break;
             case 'f':
                 show |= mon_show_failcounts;
                 break;
             case 'A':
                 show |= mon_show_attributes;
                 break;
             case 'L':
                 show |= mon_show_bans;
                 print_neg_location_prefix = optarg? optarg : "";
                 break;
             case 'D':
                 show &= ~mon_show_headers;
                 break;
             case 'b':
                 print_brief = TRUE;
                 break;
             case 'j':
                 print_pending = TRUE;
                 break;
             case 'R':
                 print_clone_detail = TRUE;
                 break;
             case 'c':
                 show |= mon_show_tickets;
                 break;
             case 'p':
                 free(pid_file);
                 if(optarg == NULL) {
-                    return crm_help(flag, CRM_EX_USAGE);
+                    crm_help(flag, CRM_EX_USAGE);
                 }
                 pid_file = strdup(optarg);
                 break;
             case 'x':
                 if(optarg == NULL) {
-                    return crm_help(flag, CRM_EX_USAGE);
+                    crm_help(flag, CRM_EX_USAGE);
                 }
                 setenv("CIB_file", optarg, 1);
                 one_shot = TRUE;
                 break;
             case 'h':
                 if(optarg == NULL) {
-                    return crm_help(flag, CRM_EX_USAGE);
+                    crm_help(flag, CRM_EX_USAGE);
                 }
                 argerr += (output_format != mon_output_console);
                 output_format = mon_output_html;
                 output_filename = strdup(optarg);
                 umask(S_IWGRP | S_IWOTH);
                 break;
             case 'X':
                 argerr += (output_format != mon_output_console);
                 output_format = mon_output_xml;
                 one_shot = TRUE;
                 break;
             case 'w':
                 /* do not allow argv[0] and argv[1...] redundancy */
                 argerr += (output_format != mon_output_console);
                 output_format = mon_output_cgi;
                 one_shot = TRUE;
                 break;
             case 's':
                 argerr += (output_format != mon_output_console);
                 output_format = mon_output_monitor;
                 one_shot = TRUE;
                 break;
             case 'E':
                 external_agent = optarg;
                 break;
             case 'e':
                 external_recipient = optarg;
                 break;
             case '1':
                 one_shot = TRUE;
                 break;
             case 'N':
                 if (output_format == mon_output_console) {
                     output_format = mon_output_plain;
                 }
                 break;
             case '$':
             case '?':
-                return crm_help(flag, CRM_EX_OK);
+                crm_help(flag, CRM_EX_OK);
                 break;
             default:
                 printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
                 ++argerr;
                 break;
         }
     }
 
     if (watch_fencing) {
         /* don't moan as fence_history_level == 1 is default */
         fence_history_level = 0;
     }
 
     /* create the cib-object early to be able to do further
      * decisions based on the cib-source
      */
     cib = cib_new();
 
     if (cib == NULL) {
         rc = -EINVAL;
     } else {
         switch (cib->variant) {
 
             case cib_native:
                 /* cib & fencing - everything available */
                 break;
 
             case cib_file:
                 /* Don't try to connect to fencing as we
                  * either don't have a running cluster or
                  * the fencing-information would possibly
                  * not match the cib data from a file.
                  * As we don't expect cib-updates coming
                  * in enforce one-shot. */
                 fence_history_level = 0;
                 one_shot = TRUE;
                 break;
 
             case cib_remote:
                 /* updates coming in but no fencing */
                 fence_history_level = 0;
                 break;
 
             case cib_undefined:
             case cib_database:
             default:
                 /* something is odd */
                 rc = -EINVAL;
                 crm_err("Invalid cib-source");
                 break;
         }
     }
 
     switch (fence_history_level) {
         case 3:
             fence_full_history = TRUE;
             /* fall through to next lower level */
         case 2:
             show |= mon_show_fence_history;
             /* fall through to next lower level */
         case 1:
             fence_history = TRUE;
             fence_connect = TRUE;
             break;
         default:
             break;
     }
 
     /* Extra sanity checks when in CGI mode */
     if (output_format == mon_output_cgi) {
         argerr += (optind < argc);
         argerr += (output_filename != NULL);
         argerr += ((cib) && (cib->variant == cib_file));
         argerr += (external_agent != NULL);
         argerr += (daemonize == TRUE);  /* paranoia */
 
     } else if (optind < argc) {
         printf("non-option ARGV-elements: ");
         while (optind < argc)
             printf("%s ", argv[optind++]);
         printf("\n");
     }
 
     if (argerr) {
         return clean_up(CRM_EX_USAGE);
     }
 
     /* XML output always prints everything */
     if (output_format == mon_output_xml) {
         show = mon_show_all;
         print_timing = TRUE;
     }
 
     if (one_shot) {
         if (output_format == mon_output_console) {
             output_format = mon_output_plain;
         }
 
     } else if (daemonize) {
         if ((output_format == mon_output_console) || (output_format == mon_output_plain)) {
             output_format = mon_output_none;
         }
         crm_enable_stderr(FALSE);
 
         if ((output_format != mon_output_html)
             && !external_agent) {
             printf ("Looks like you forgot to specify one or more of: "
                     "--as-html, --external-agent\n");
             return clean_up(CRM_EX_USAGE);
         }
 
         if (cib) {
             /* to be on the safe side don't have cib-object around
              * when we are forking
              */
             cib_delete(cib);
             cib = NULL;
             crm_make_daemon(crm_system_name, TRUE, pid_file);
             cib = cib_new();
             if (cib == NULL) {
                 rc = -EINVAL;
             }
             /* otherwise assume we've got the same cib-object we've just destroyed
              * in our parent
              */
         }
 
 
     } else if (output_format == mon_output_console) {
 #if CURSES_ENABLED
         initscr();
         cbreak();
         noecho();
         crm_enable_stderr(FALSE);
         curses_console_initialized = TRUE;
 #else
         one_shot = TRUE;
         output_format = mon_output_plain;
         printf("Defaulting to one-shot mode\n");
         printf("You need to have curses available at compile time to enable console mode\n");
 #endif
     }
 
     crm_info("Starting %s", crm_system_name);
 
     if (cib) {
 
         do {
             if (!one_shot) {
                 print_as("Waiting until cluster is available on this node ...\n");
             }
             rc = cib_connect(!one_shot);
 
             if (one_shot) {
                 break;
 
             } else if (rc != pcmk_ok) {
                 sleep(reconnect_msec / 1000);
 #if CURSES_ENABLED
                 if (output_format == mon_output_console) {
                     clear();
                     refresh();
                 }
 #endif
             } else {
                 if (output_format == mon_output_html) {
                     print_as("Writing html to %s ...\n", output_filename);
                 }
             }
 
         } while (rc == -ENOTCONN);
     }
 
     if (rc != pcmk_ok) {
         if (output_format == mon_output_monitor) {
             printf("CLUSTER CRIT: Connection to cluster failed: %s\n",
                     pcmk_strerror(rc));
             return clean_up(MON_STATUS_CRIT);
         } else {
             if (rc == -ENOTCONN) {
                 print_as("\nError: cluster is not available on this node\n");
             } else {
                 print_as("\nConnection to cluster failed: %s\n",
                             pcmk_strerror(rc));
             }
         }
         if (output_format == mon_output_console) {
             sleep(2);
         }
         return clean_up(crm_errno2exit(rc));
     }
 
     if (one_shot) {
         return clean_up(CRM_EX_OK);
     }
 
     mainloop = g_main_loop_new(NULL, FALSE);
 
     mainloop_add_signal(SIGTERM, mon_shutdown);
     mainloop_add_signal(SIGINT, mon_shutdown);
 #if CURSES_ENABLED
     if (output_format == mon_output_console) {
         ncurses_winch_handler = signal(SIGWINCH, mon_winresize);
         if (ncurses_winch_handler == SIG_DFL ||
             ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR)
             ncurses_winch_handler = NULL;
         g_io_add_watch(g_io_channel_unix_new(STDIN_FILENO), G_IO_IN, detect_user_input, NULL);
     }
 #endif
     refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL);
 
     g_main_loop_run(mainloop);
     g_main_loop_unref(mainloop);
 
     crm_info("Exiting %s", crm_system_name);
 
     return clean_up(CRM_EX_OK);
 }
 
 #define mon_warn(fmt...) do {			\
 	if (!has_warnings) {			\
 	    print_as("CLUSTER WARN:");		\
 	} else {				\
 	    print_as(",");			\
 	}					\
 	print_as(fmt);				\
 	has_warnings = TRUE;			\
     } while(0)
 
 static int
 count_resources(pe_working_set_t * data_set, resource_t * rsc)
 {
     int count = 0;
     GListPtr gIter = NULL;
 
     if (rsc == NULL) {
         gIter = data_set->resources;
     } else if (rsc->children) {
         gIter = rsc->children;
     } else {
         return is_not_set(rsc->flags, pe_rsc_orphan);
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         count += count_resources(data_set, gIter->data);
     }
     return count;
 }
 
 /*!
  * \internal
  * \brief Print one-line status suitable for use with monitoring software
  *
  * \param[in] data_set  Working set of CIB state
  * \param[in] history   List of stonith actions
  *
  * \note This function's output (and the return code when the program exits)
  *       should conform to https://www.monitoring-plugins.org/doc/guidelines.html
  */
 static void
 print_simple_status(pe_working_set_t * data_set,
                     stonith_history_t *history)
 {
     GListPtr gIter = NULL;
     int nodes_online = 0;
     int nodes_standby = 0;
     int nodes_maintenance = 0;
 
     if (data_set->dc_node == NULL) {
         mon_warn(" No DC");
     }
 
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         if (node->details->standby && node->details->online) {
             nodes_standby++;
         } else if (node->details->maintenance && node->details->online) {
             nodes_maintenance++;
         } else if (node->details->online) {
             nodes_online++;
         } else {
             mon_warn(" offline node: %s", node->details->uname);
         }
     }
 
     if (!has_warnings) {
         int nresources = count_resources(data_set, NULL);
 
         print_as("CLUSTER OK: %d node%s online", nodes_online, s_if_plural(nodes_online));
         if (nodes_standby > 0) {
             print_as(", %d standby node%s", nodes_standby, s_if_plural(nodes_standby));
         }
         if (nodes_maintenance > 0) {
             print_as(", %d maintenance node%s", nodes_maintenance, s_if_plural(nodes_maintenance));
         }
         print_as(", %d resource%s configured", nresources, s_if_plural(nresources));
     }
 
     print_as("\n");
 }
 
 /*!
  * \internal
  * \brief Print a [name]=[value][units] pair, optionally using time string
  *
  * \param[in] stream      File stream to display output to
  * \param[in] name        Name to display
  * \param[in] value       Value to display (or NULL to convert time instead)
  * \param[in] units       Units to display (or NULL for no units)
  * \param[in] epoch_time  Epoch time to convert if value is NULL
  */
 static void
 print_nvpair(FILE *stream, const char *name, const char *value,
              const char *units, time_t epoch_time)
 {
     /* print name= */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as(" %s=", name);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
         case mon_output_xml:
             fprintf(stream, " %s=", name);
             break;
 
         default:
             break;
     }
 
     /* If we have a value (and optionally units), print it */
     if (value) {
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as("%s%s", value, (units? units : ""));
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, "%s%s", value, (units? units : ""));
                 break;
 
             case mon_output_xml:
                 fprintf(stream, "\"%s%s\"", value, (units? units : ""));
                 break;
 
             default:
                 break;
         }
 
     /* Otherwise print user-friendly time string */
     } else {
         static char empty_str[] = "";
         char *c, *date_str = asctime(localtime(&epoch_time));
 
         for (c = (date_str != NULL) ? date_str : empty_str; *c != '\0'; ++c) {
             if (*c == '\n') {
                 *c = '\0';
                 break;
             }
         }
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as("'%s'", date_str);
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
             case mon_output_xml:
                 fprintf(stream, "\"%s\"", date_str);
                 break;
 
             default:
                 break;
         }
     }
 }
 
 /*!
  * \internal
  * \brief Print whatever is needed to start a node section
  *
  * \param[in] stream     File stream to display output to
  * \param[in] node       Node to print
  */
 static void
 print_node_start(FILE *stream, node_t *node)
 {
     char *node_name;
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             node_name = get_node_display_name(node);
             print_as("* Node %s:\n", node_name);
             free(node_name);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             node_name = get_node_display_name(node);
             fprintf(stream, "  <h3>Node: %s</h3>\n  <ul>\n", node_name);
             free(node_name);
             break;
 
         case mon_output_xml:
             fprintf(stream, "        <node name=\"%s\">\n", node->details->uname);
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print whatever is needed to end a node section
  *
  * \param[in] stream     File stream to display output to
  */
 static void
 print_node_end(FILE *stream)
 {
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "  </ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "        </node>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print resources section heading appropriate to options
  *
  * \param[in] stream      File stream to display output to
  */
 static void
 print_resources_heading(FILE *stream)
 {
     const char *heading;
 
     if (group_by_node) {
 
         /* Active resources have already been printed by node */
         heading = (inactive_resources? "Inactive resources" : NULL);
 
     } else if (inactive_resources) {
         heading = "Full list of resources";
 
     } else {
         heading = "Active resources";
     }
 
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\n%s:\n\n", heading);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>%s</h2>\n", heading);
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <resources>\n");
             break;
 
         default:
             break;
     }
 
 }
 
 /*!
  * \internal
  * \brief Print whatever resource section closing is appropriate
  *
  * \param[in] stream     File stream to display output to
  */
 static void
 print_resources_closing(FILE *stream, gboolean printed_heading)
 {
     const char *heading;
 
     /* What type of resources we did or did not display */
     if (group_by_node) {
         heading = "inactive ";
     } else if (inactive_resources) {
         heading = "";
     } else {
         heading = "active ";
     }
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             if (!printed_heading) {
                 print_as("\nNo %sresources\n\n", heading);
             }
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             if (!printed_heading) {
                 fprintf(stream, " <hr />\n <h2>No %sresources</h2>\n", heading);
             }
             break;
 
         case mon_output_xml:
             fprintf(stream, "    %s\n",
                     (printed_heading? "</resources>" : "<resources/>"));
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print whatever resource section(s) are appropriate
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Cluster state to display
  * \param[in] print_opts  Bitmask of pe_print_options
  */
 static void
 print_resources(FILE *stream, pe_working_set_t *data_set, int print_opts)
 {
     GListPtr rsc_iter;
     const char *prefix = NULL;
     gboolean printed_heading = FALSE;
     gboolean brief_output = print_brief;
 
     /* If we already showed active resources by node, and
      * we're not showing inactive resources, we have nothing to do
      */
     if (group_by_node && !inactive_resources) {
         return;
     }
 
     /* XML uses an indent, and ignores brief option for resources */
     if (output_format == mon_output_xml) {
         prefix = "        ";
         brief_output = FALSE;
     }
 
     /* If we haven't already printed resources grouped by node,
      * and brief output was requested, print resource summary */
     if (brief_output && !group_by_node) {
         print_resources_heading(stream);
         printed_heading = TRUE;
         print_rscs_brief(data_set->resources, NULL, print_opts, stream,
                          inactive_resources);
     }
 
     /* For each resource, display it if appropriate */
     for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
         resource_t *rsc = (resource_t *) rsc_iter->data;
 
         /* Complex resources may have some sub-resources active and some inactive */
         gboolean is_active = rsc->fns->active(rsc, TRUE);
         gboolean partially_active = rsc->fns->active(rsc, FALSE);
 
         /* Skip inactive orphans (deleted but still in CIB) */
         if (is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
             continue;
 
         /* Skip active resources if we already displayed them by node */
         } else if (group_by_node) {
             if (is_active) {
                 continue;
             }
 
         /* Skip primitives already counted in a brief summary */
         } else if (brief_output && (rsc->variant == pe_native)) {
             continue;
 
         /* Skip resources that aren't at least partially active,
          * unless we're displaying inactive resources
          */
         } else if (!partially_active && !inactive_resources) {
             continue;
         }
 
         /* Print this resource */
         if (printed_heading == FALSE) {
             print_resources_heading(stream);
             printed_heading = TRUE;
         }
         rsc->fns->print(rsc, prefix, print_opts, stream);
     }
 
     print_resources_closing(stream, printed_heading);
 }
 
 /*!
  * \internal
  * \brief Print heading for resource history
  *
  * \param[in] stream      File stream to display output to
  * \param[in] data_set    Current state of CIB
  * \param[in] node        Node that ran this resource
  * \param[in] rsc         Resource to print
  * \param[in] rsc_id      ID of resource to print
  * \param[in] all         Whether to print every resource or just failed ones
  */
 static void
 print_rsc_history_start(FILE *stream, pe_working_set_t *data_set, node_t *node,
                         resource_t *rsc, const char *rsc_id, gboolean all)
 {
     time_t last_failure = 0;
     int failcount = rsc?
                     pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
                                      NULL, data_set)
                     : 0;
 
     if (!all && !failcount && (last_failure <= 0)) {
         return;
     }
 
     /* Print resource ID */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("   %s:", rsc_id);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "   <li>%s:", rsc_id);
             break;
 
         case mon_output_xml:
             fprintf(stream, "            <resource_history id=\"%s\"", rsc_id);
             break;
 
         default:
             break;
     }
 
     /* If resource is an orphan, that's all we can say about it */
     if (rsc == NULL) {
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as(" orphan");
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, " orphan");
                 break;
 
             case mon_output_xml:
                 fprintf(stream, " orphan=\"true\"");
                 break;
 
             default:
                 break;
         }
 
     /* If resource is not an orphan, print some details */
     } else if (all || failcount || (last_failure > 0)) {
 
         /* Print migration threshold */
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as(" migration-threshold=%d", rsc->migration_threshold);
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, " migration-threshold=%d", rsc->migration_threshold);
                 break;
 
             case mon_output_xml:
                 fprintf(stream, " orphan=\"false\" migration-threshold=\"%d\"",
                         rsc->migration_threshold);
                 break;
 
             default:
                 break;
         }
 
         /* Print fail count if any */
         if (failcount > 0) {
             switch (output_format) {
                 case mon_output_plain:
                 case mon_output_console:
                     print_as(" " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
                     break;
 
                 case mon_output_html:
                 case mon_output_cgi:
                     fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
                     break;
 
                 case mon_output_xml:
                     fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=\"%d\"",
                             failcount);
                     break;
 
                 default:
                     break;
             }
         }
 
         /* Print last failure time if any */
         if (last_failure > 0) {
             print_nvpair(stream, CRM_LAST_FAILURE_PREFIX, NULL, NULL,
                          last_failure);
         }
     }
 
     /* End the heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "\n    <ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, ">\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print closing for resource history
  *
  * \param[in] stream      File stream to display output to
  */
 static void
 print_rsc_history_end(FILE *stream)
 {
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "    </ul>\n   </li>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "            </resource_history>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print operation history
  *
  * \param[in] stream        File stream to display output to
  * \param[in] data_set      Current state of CIB
  * \param[in] node          Node this operation is for
  * \param[in] xml_op        Root of XML tree describing this operation
  * \param[in] task          Task parsed from this operation's XML
  * \param[in] interval_ms_s Interval parsed from this operation's XML
  * \param[in] rc            Return code parsed from this operation's XML
  */
 static void
 print_op_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
                  xmlNode *xml_op, const char *task, const char *interval_ms_s,
                  int rc)
 {
     const char *value = NULL;
     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
 
     /* Begin the operation description */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("    + (%s) %s:", call, task);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "     <li>(%s) %s:", call, task);
             break;
 
         case mon_output_xml:
             fprintf(stream, "                <operation_history call=\"%s\" task=\"%s\"",
                     call, task);
             break;
 
         default:
             break;
     }
 
     /* Add name=value pairs as appropriate */
     if (interval_ms_s && safe_str_neq(interval_ms_s, "0")) {
         print_nvpair(stream, "interval", interval_ms_s, "ms", 0);
     }
     if (print_timing) {
         int int_value;
         const char *attr;
 
         attr = XML_RSC_OP_LAST_CHANGE;
         value = crm_element_value(xml_op, attr);
         if (value) {
             int_value = crm_parse_int(value, NULL);
             if (int_value > 0) {
                 print_nvpair(stream, attr, NULL, NULL, int_value);
             }
         }
 
         attr = XML_RSC_OP_LAST_RUN;
         value = crm_element_value(xml_op, attr);
         if (value) {
             int_value = crm_parse_int(value, NULL);
             if (int_value > 0) {
                 print_nvpair(stream, attr, NULL, NULL, int_value);
             }
         }
 
         attr = XML_RSC_OP_T_EXEC;
         value = crm_element_value(xml_op, attr);
         if (value) {
             print_nvpair(stream, attr, value, "ms", 0);
         }
 
         attr = XML_RSC_OP_T_QUEUE;
         value = crm_element_value(xml_op, attr);
         if (value) {
             print_nvpair(stream, attr, value, "ms", 0);
         }
     }
 
     /* End the operation description */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as(" rc=%d (%s)\n", rc, services_ocf_exitcode_str(rc));
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " rc=%d (%s)</li>\n", rc, services_ocf_exitcode_str(rc));
             break;
 
         case mon_output_xml:
             fprintf(stream, " rc=\"%d\" rc_text=\"%s\" />\n", rc, services_ocf_exitcode_str(rc));
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print resource operation/failure history
  *
  * \param[in] stream      File stream to display output to
  * \param[in] data_set    Current state of CIB
  * \param[in] node        Node that ran this resource
  * \param[in] rsc_entry   Root of XML tree describing resource status
  * \param[in] operations  Whether to print operations or just failcounts
  */
 static void
 print_rsc_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
                   xmlNode *rsc_entry, gboolean operations)
 {
     GListPtr gIter = NULL;
     GListPtr op_list = NULL;
     gboolean printed = FALSE;
     const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
     resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
     xmlNode *rsc_op = NULL;
 
     /* If we're not showing operations, just print the resource failure summary */
     if (operations == FALSE) {
         print_rsc_history_start(stream, data_set, node, rsc, rsc_id, FALSE);
         print_rsc_history_end(stream);
         return;
     }
 
     /* Create a list of this resource's operations */
     for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
         if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
             op_list = g_list_append(op_list, rsc_op);
         }
     }
     op_list = g_list_sort(op_list, sort_op_by_callid);
 
     /* Print each operation */
     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
         xmlNode *xml_op = (xmlNode *) gIter->data;
         const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
         const char *interval_ms_s = crm_element_value(xml_op,
                                                       XML_LRM_ATTR_INTERVAL_MS);
         const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
         int rc = crm_parse_int(op_rc, "0");
 
         /* Display 0-interval monitors as "probe" */
         if (safe_str_eq(task, CRMD_ACTION_STATUS)
             && ((interval_ms_s == NULL) || safe_str_eq(interval_ms_s, "0"))) {
             task = "probe";
         }
 
         /* Ignore notifies and some probes */
         if (safe_str_eq(task, CRMD_ACTION_NOTIFY) || (safe_str_eq(task, "probe") && (rc == 7))) {
             continue;
         }
 
         /* If this is the first printed operation, print heading for resource */
         if (printed == FALSE) {
             printed = TRUE;
             print_rsc_history_start(stream, data_set, node, rsc, rsc_id, TRUE);
         }
 
         /* Print the operation */
         print_op_history(stream, data_set, node, xml_op, task, interval_ms_s,
                          rc);
     }
 
     /* Free the list we created (no need to free the individual items) */
     g_list_free(op_list);
 
     /* If we printed anything, close the resource */
     if (printed) {
         print_rsc_history_end(stream);
     }
 }
 
 /*!
  * \internal
  * \brief Print node operation/failure history
  *
  * \param[in] stream      File stream to display output to
  * \param[in] data_set    Current state of CIB
  * \param[in] node_state  Root of XML tree describing node status
  * \param[in] operations  Whether to print operations or just failcounts
  */
 static void
 print_node_history(FILE *stream, pe_working_set_t *data_set,
                    xmlNode *node_state, gboolean operations)
 {
     node_t *node = pe_find_node_id(data_set->nodes, ID(node_state));
     xmlNode *lrm_rsc = NULL;
     xmlNode *rsc_entry = NULL;
 
     if (node && node->details && node->details->online) {
         print_node_start(stream, node);
 
         lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
         lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
 
         /* Print history of each of the node's resources */
         for (rsc_entry = __xml_first_child(lrm_rsc); rsc_entry != NULL;
              rsc_entry = __xml_next(rsc_entry)) {
 
             if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
                 print_rsc_history(stream, data_set, node, rsc_entry, operations);
             }
         }
 
         print_node_end(stream);
     }
 }
 
 /*!
  * \internal
  * \brief Print extended information about an attribute if appropriate
  *
  * \param[in] data_set  Working set of CIB state
  *
  * \return TRUE if extended information was printed, FALSE otherwise
  * \note Currently, extended information is only supported for ping/pingd
  *       resources, for which a message will be printed if connectivity is lost
  *       or degraded.
  */
 static gboolean
 print_attr_msg(FILE *stream, node_t * node, GListPtr rsc_list, const char *attrname, const char *attrvalue)
 {
     GListPtr gIter = NULL;
 
     for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
         const char *type = g_hash_table_lookup(rsc->meta, "type");
 
         if (rsc->children != NULL) {
             if (print_attr_msg(stream, node, rsc->children, attrname, attrvalue)) {
                 return TRUE;
             }
         }
 
         if (safe_str_eq(type, "ping") || safe_str_eq(type, "pingd")) {
             const char *name = g_hash_table_lookup(rsc->parameters, "name");
 
             if (name == NULL) {
                 name = "pingd";
             }
 
             /* To identify the resource with the attribute name. */
             if (safe_str_eq(name, attrname)) {
                 int host_list_num = 0;
                 int expected_score = 0;
                 int value = crm_parse_int(attrvalue, "0");
                 const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list");
                 const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier");
 
                 if(hosts) {
                     char **host_list = g_strsplit(hosts, " ", 0);
                     host_list_num = g_strv_length(host_list);
                     g_strfreev(host_list);
                 }
 
                 /* pingd multiplier is the same as the default value. */
                 expected_score = host_list_num * crm_parse_int(multiplier, "1");
 
                 switch (output_format) {
                     case mon_output_plain:
                     case mon_output_console:
                         if (value <= 0) {
                             print_as("\t: Connectivity is lost");
                         } else if (value < expected_score) {
                             print_as("\t: Connectivity is degraded (Expected=%d)", expected_score);
                         }
                         break;
 
                     case mon_output_html:
                     case mon_output_cgi:
                         if (value <= 0) {
                             fprintf(stream, " <b>(connectivity is lost)</b>");
                         } else if (value < expected_score) {
                             fprintf(stream, " <b>(connectivity is degraded -- expected %d)</b>",
                                     expected_score);
                         }
                         break;
 
                     case mon_output_xml:
                         fprintf(stream, " expected=\"%d\"", expected_score);
                         break;
 
                     default:
                         break;
                 }
                 return TRUE;
             }
         }
     }
     return FALSE;
 }
 
 static int
 compare_attribute(gconstpointer a, gconstpointer b)
 {
     int rc;
 
     rc = strcmp((const char *)a, (const char *)b);
 
     return rc;
 }
 
 static void
 create_attr_list(gpointer name, gpointer value, gpointer data)
 {
     int i;
     const char *filt_str[] = FILTER_STR;
 
     CRM_CHECK(name != NULL, return);
 
     /* filtering automatic attributes */
     for (i = 0; filt_str[i] != NULL; i++) {
         if (g_str_has_prefix(name, filt_str[i])) {
             return;
         }
     }
 
     attr_list = g_list_insert_sorted(attr_list, name, compare_attribute);
 }
 
 /* structure for passing multiple user data to g_list_foreach() */
 struct mon_attr_data {
     FILE *stream;
     node_t *node;
 };
 
 static void
 print_node_attribute(gpointer name, gpointer user_data)
 {
     const char *value = NULL;
     struct mon_attr_data *data = (struct mon_attr_data *) user_data;
 
     value = pe_node_attribute_raw(data->node, name);
 
     /* Print attribute name and value */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("    + %-32s\t: %-10s", (char *)name, value);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(data->stream, "   <li>%s: %s",
                     (char *)name, value);
             break;
 
         case mon_output_xml:
             fprintf(data->stream,
                     "            <attribute name=\"%s\" value=\"%s\"",
                     (char *)name, value);
             break;
 
         default:
             break;
     }
 
     /* Print extended information if appropriate */
     print_attr_msg(data->stream, data->node, data->node->details->running_rsc,
                    name, value);
 
     /* Close out the attribute */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(data->stream, "</li>\n");
             break;
 
         case mon_output_xml:
             fprintf(data->stream, " />\n");
             break;
 
         default:
             break;
     }
 }
 
 static void
 print_node_summary(FILE *stream, pe_working_set_t * data_set, gboolean operations)
 {
     xmlNode *node_state = NULL;
     xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
 
     /* Print heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             if (operations) {
                 print_as("\nOperations:\n");
             } else {
                 print_as("\nMigration Summary:\n");
             }
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             if (operations) {
                 fprintf(stream, " <hr />\n <h2>Operations</h2>\n");
             } else {
                 fprintf(stream, " <hr />\n <h2>Migration Summary</h2>\n");
             }
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <node_history>\n");
             break;
 
         default:
             break;
     }
 
     /* Print each node in the CIB status */
     for (node_state = __xml_first_child(cib_status); node_state != NULL;
          node_state = __xml_next(node_state)) {
         if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
             print_node_history(stream, data_set, node_state, operations);
         }
     }
 
     /* Close section */
     switch (output_format) {
         case mon_output_xml:
             fprintf(stream, "    </node_history>\n");
             break;
 
         default:
             break;
     }
 }
 
 static void
 print_ticket(gpointer name, gpointer value, gpointer data)
 {
     ticket_t *ticket = (ticket_t *) value;
     FILE *stream = (FILE *) data;
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("* %s:\t%s%s", ticket->id,
                      (ticket->granted? "granted" : "revoked"),
                      (ticket->standby? " [standby]" : ""));
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "  <li>%s: %s%s", ticket->id,
                     (ticket->granted? "granted" : "revoked"),
                     (ticket->standby? " [standby]" : ""));
             break;
 
         case mon_output_xml:
             fprintf(stream, "        <ticket id=\"%s\" status=\"%s\" standby=\"%s\"",
                     ticket->id, (ticket->granted? "granted" : "revoked"),
                     (ticket->standby? "true" : "false"));
             break;
 
         default:
             break;
     }
     if (ticket->last_granted > -1) {
         print_nvpair(stdout, "last-granted", NULL, NULL, ticket->last_granted);
     }
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "</li>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, " />\n");
             break;
 
         default:
             break;
     }
 }
 
 static void
 print_cluster_tickets(FILE *stream, pe_working_set_t * data_set)
 {
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\nTickets:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>Tickets</h2>\n <ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <tickets>\n");
             break;
 
         default:
             break;
     }
 
     /* Print each ticket */
     g_hash_table_foreach(data_set->tickets, print_ticket, stream);
 
     /* Close section */
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " </ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    </tickets>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Return human-friendly string representing node name
  *
  * The returned string will be in the format
  *    uname[@hostUname] [(nodeID)]
  * "@hostUname" will be printed if the node is a guest node.
  * "(nodeID)" will be printed if the node ID is different from the node uname,
  *  and detailed output has been requested.
  *
  * \param[in] node  Node to represent
  * \return Newly allocated string with representation of node name
  * \note It is the caller's responsibility to free the result with free().
  */
 static char *
 get_node_display_name(node_t *node)
 {
     char *node_name;
     const char *node_host = NULL;
     const char *node_id = NULL;
     int name_len;
 
     CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
 
     /* Host is displayed only if this is a guest node */
     if (is_container_remote_node(node)) {
         node_t *host_node = pe__current_node(node->details->remote_rsc);
 
         if (host_node && host_node->details) {
             node_host = host_node->details->uname;
         }
         if (node_host == NULL) {
             node_host = ""; /* so we at least get "uname@" to indicate guest */
         }
     }
 
     /* Node ID is displayed if different from uname and detail is requested */
     if (print_clone_detail && safe_str_neq(node->details->uname, node->details->id)) {
         node_id = node->details->id;
     }
 
     /* Determine name length */
     name_len = strlen(node->details->uname) + 1;
     if (node_host) {
         name_len += strlen(node_host) + 1; /* "@node_host" */
     }
     if (node_id) {
         name_len += strlen(node_id) + 3; /* + " (node_id)" */
     }
 
     /* Allocate and populate display name */
     node_name = malloc(name_len);
     CRM_ASSERT(node_name != NULL);
     strcpy(node_name, node->details->uname);
     if (node_host) {
         strcat(node_name, "@");
         strcat(node_name, node_host);
     }
     if (node_id) {
         strcat(node_name, " (");
         strcat(node_name, node_id);
         strcat(node_name, ")");
     }
     return node_name;
 }
 
 /*!
  * \internal
  * \brief Print a negative location constraint
  *
  * \param[in] stream     File stream to display output to
  * \param[in] node       Node affected by constraint
  * \param[in] location   Constraint to print
  */
 static void
 print_ban(FILE *stream, pe_node_t *node, pe__location_t *location)
 {
     char *node_name = NULL;
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             node_name = get_node_display_name(node);
             print_as(" %s\tprevents %s from running %son %s\n",
                      location->id, location->rsc_lh->id,
                      ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
                      node_name);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             node_name = get_node_display_name(node);
             fprintf(stream, "  <li>%s prevents %s from running %son %s</li>\n",
                      location->id, location->rsc_lh->id,
                      ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
                      node_name);
             break;
 
         case mon_output_xml:
             fprintf(stream,
                     "        <ban id=\"%s\" resource=\"%s\" node=\"%s\" weight=\"%d\" master_only=\"%s\" />\n",
                     location->id, location->rsc_lh->id, node->details->uname, node->weight,
                     ((location->role_filter == RSC_ROLE_MASTER)? "true" : "false"));
             break;
 
         default:
             break;
     }
     free(node_name);
 }
 
 /*!
  * \internal
  * \brief Print section for negative location constraints
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set corresponding to CIB status to display
  */
 static void
 print_neg_locations(FILE *stream, pe_working_set_t *data_set)
 {
     GListPtr gIter, gIter2;
 
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\nNegative Location Constraints:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>Negative Location Constraints</h2>\n <ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <bans>\n");
             break;
 
         default:
             break;
     }
 
     /* Print each ban */
     for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
         pe__location_t *location = gIter->data;
         if (!g_str_has_prefix(location->id, print_neg_location_prefix))
             continue;
         for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
             node_t *node = (node_t *) gIter2->data;
 
             if (node->weight < 0) {
                 print_ban(stream, node, location);
             }
         }
     }
 
     /* Close section */
     switch (output_format) {
         case mon_output_cgi:
         case mon_output_html:
             fprintf(stream, " </ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    </bans>\n");
             break;
 
         default:
             break;
     }
 }
 
 static void
 crm_mon_get_parameters(resource_t *rsc, pe_working_set_t * data_set)
 {
     get_rsc_attributes(rsc->parameters, rsc, NULL, data_set);
     crm_trace("Beekhof: unpacked params for %s (%d)", rsc->id, g_hash_table_size(rsc->parameters));
     if(rsc->children) {
         GListPtr gIter = NULL;
 
         for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
             crm_mon_get_parameters(gIter->data, data_set);
         }
     }
 }
 
 /*!
  * \internal
  * \brief Print node attributes section
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_node_attributes(FILE *stream, pe_working_set_t *data_set)
 {
     GListPtr gIter = NULL;
 
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\nNode Attributes:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>Node Attributes</h2>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <node_attributes>\n");
             break;
 
         default:
             break;
     }
 
     /* Unpack all resource parameters (it would be more efficient to do this
      * only when needed for the first time in print_attr_msg())
      */
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         crm_mon_get_parameters(gIter->data, data_set);
     }
 
     /* Display each node's attributes */
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         struct mon_attr_data data;
 
         data.stream = stream;
         data.node = (node_t *) gIter->data;
 
         if (data.node && data.node->details && data.node->details->online) {
             print_node_start(stream, data.node);
             g_hash_table_foreach(data.node->details->attrs, create_attr_list, NULL);
             g_list_foreach(attr_list, print_node_attribute, &data);
             g_list_free(attr_list);
             attr_list = NULL;
             print_node_end(stream);
         }
     }
 
     /* Print section footer */
     switch (output_format) {
         case mon_output_xml:
             fprintf(stream, "    </node_attributes>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Return resource display options corresponding to command-line choices
  *
  * \return Bitmask of pe_print_options suitable for resource print functions
  */
 static int
 get_resource_display_options(void)
 {
     int print_opts;
 
     /* Determine basic output format */
     switch (output_format) {
         case mon_output_console:
             print_opts = pe_print_ncurses;
             break;
         case mon_output_html:
         case mon_output_cgi:
             print_opts = pe_print_html;
             break;
         case mon_output_xml:
             print_opts = pe_print_xml;
             break;
         default:
             print_opts = pe_print_printf;
             break;
     }
 
     /* Add optional display elements */
     if (print_pending) {
         print_opts |= pe_print_pending;
     }
     if (print_clone_detail) {
         print_opts |= pe_print_clone_details|pe_print_implicit;
     }
     if (!inactive_resources) {
         print_opts |= pe_print_clone_active;
     }
     if (print_brief) {
         print_opts |= pe_print_brief;
     }
     return print_opts;
 }
 
 /*!
  * \internal
  * \brief Return human-friendly string representing current time
  *
  * \return Current time as string (as by ctime() but without newline) on success
  *         or "Could not determine current time" on error
  * \note The return value points to a statically allocated string which might be
  *       overwritten by subsequent calls to any of the C library date and time functions.
  */
 static const char *
 crm_now_string(void)
 {
     time_t a_time = time(NULL);
     char *since_epoch = ctime(&a_time);
 
     if ((a_time == (time_t) -1) || (since_epoch == NULL)) {
         return "Could not determine current time";
     }
     since_epoch[strlen(since_epoch) - 1] = EOS; /* trim newline */
     return (since_epoch);
 }
 
 /*!
  * \internal
  * \brief Print header for cluster summary if needed
  *
  * \param[in] stream     File stream to display output to
  */
 static void
 print_cluster_summary_header(FILE *stream)
 {
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <h2>Cluster Summary</h2>\n <p>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <summary>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print footer for cluster summary if needed
  *
  * \param[in] stream     File stream to display output to
  */
 static void
 print_cluster_summary_footer(FILE *stream)
 {
     switch (output_format) {
         case mon_output_cgi:
         case mon_output_html:
             fprintf(stream, " </p>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    </summary>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print times the display was last updated and CIB last changed
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_cluster_times(FILE *stream, pe_working_set_t *data_set)
 {
     const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
     const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
     const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
     const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("Last updated: %s", crm_now_string());
             print_as((user || client || origin)? "\n" : "\t\t");
             print_as("Last change: %s", last_written ? last_written : "");
             if (user) {
                 print_as(" by %s", user);
             }
             if (client) {
                 print_as(" via %s", client);
             }
             if (origin) {
                 print_as(" on %s", origin);
             }
             print_as("\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <b>Last updated:</b> %s<br/>\n", crm_now_string());
             fprintf(stream, " <b>Last change:</b> %s", last_written ? last_written : "");
             if (user) {
                 fprintf(stream, " by %s", user);
             }
             if (client) {
                 fprintf(stream, " via %s", client);
             }
             if (origin) {
                 fprintf(stream, " on %s", origin);
             }
             fprintf(stream, "<br/>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "        <last_update time=\"%s\" />\n", crm_now_string());
             fprintf(stream, "        <last_change time=\"%s\" user=\"%s\" client=\"%s\" origin=\"%s\" />\n",
                     last_written ? last_written : "", user ? user : "",
                     client ? client : "", origin ? origin : "");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print cluster stack
  *
  * \param[in] stream     File stream to display output to
  * \param[in] stack_s    Stack name
  */
 static void
 print_cluster_stack(FILE *stream, const char *stack_s)
 {
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("Stack: %s\n", stack_s);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <b>Stack:</b> %s<br/>\n", stack_s);
             break;
 
         case mon_output_xml:
             fprintf(stream, "        <stack type=\"%s\" />\n", stack_s);
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print current DC and its version
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_cluster_dc(FILE *stream, pe_working_set_t *data_set)
 {
     node_t *dc = data_set->dc_node;
     xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
                                            data_set->input, LOG_DEBUG);
     const char *dc_version_s = dc_version?
                                crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
                                : NULL;
     const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
     char *dc_name = dc? get_node_display_name(dc) : NULL;
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("Current DC: ");
             if (dc) {
                 print_as("%s (version %s) - partition %s quorum\n",
                          dc_name, (dc_version_s? dc_version_s : "unknown"),
                          (crm_is_true(quorum) ? "with" : "WITHOUT"));
             } else {
                 print_as("NONE\n");
             }
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <b>Current DC:</b> ");
             if (dc) {
                 fprintf(stream, "%s (version %s) - partition %s quorum",
                         dc_name, (dc_version_s? dc_version_s : "unknown"),
                         (crm_is_true(quorum)? "with" : "<font color=\"red\"><b>WITHOUT</b></font>"));
             } else {
                 fprintf(stream, "<font color=\"red\"><b>NONE</b></font>");
             }
             fprintf(stream, "<br/>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream,  "        <current_dc ");
             if (dc) {
                 fprintf(stream,
                         "present=\"true\" version=\"%s\" name=\"%s\" id=\"%s\" with_quorum=\"%s\"",
                         (dc_version_s? dc_version_s : ""), dc->details->uname, dc->details->id,
                         (crm_is_true(quorum) ? "true" : "false"));
             } else {
                 fprintf(stream, "present=\"false\"");
             }
             fprintf(stream, " />\n");
             break;
 
         default:
             break;
     }
     free(dc_name);
 }
 
 /*!
  * \internal
  * \brief Print counts of configured nodes and resources
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  * \param[in] stack_s    Stack name
  */
 static void
 print_cluster_counts(FILE *stream, pe_working_set_t *data_set, const char *stack_s)
 {
     int nnodes = g_list_length(data_set->nodes);
     int nresources = count_resources(data_set, NULL);
 
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
 
             print_as("\n%d node%s configured\n", nnodes, s_if_plural(nnodes));
 
             print_as("%d resource%s configured",
                      nresources, s_if_plural(nresources));
             if(data_set->disabled_resources || data_set->blocked_resources) {
                 print_as(" (");
                 if (data_set->disabled_resources) {
                     print_as("%d DISABLED", data_set->disabled_resources);
                 }
                 if (data_set->disabled_resources && data_set->blocked_resources) {
                     print_as(", ");
                 }
                 if (data_set->blocked_resources) {
                     print_as("%d BLOCKED from starting due to failure",
                              data_set->blocked_resources);
                 }
                 print_as(")");
             }
             print_as("\n");
 
             break;
 
         case mon_output_html:
         case mon_output_cgi:
 
             fprintf(stream, " %d node%s configured<br/>\n",
                     nnodes, s_if_plural(nnodes));
 
             fprintf(stream, " %d resource%s configured",
                     nresources, s_if_plural(nresources));
             if (data_set->disabled_resources || data_set->blocked_resources) {
                 fprintf(stream, " (");
                 if (data_set->disabled_resources) {
                     fprintf(stream, "%d <strong>DISABLED</strong>",
                             data_set->disabled_resources);
                 }
                 if (data_set->disabled_resources && data_set->blocked_resources) {
                     fprintf(stream, ", ");
                 }
                 if (data_set->blocked_resources) {
                     fprintf(stream,
                             "%d <strong>BLOCKED</strong> from starting due to failure",
                             data_set->blocked_resources);
                 }
                 fprintf(stream, ")");
             }
             fprintf(stream, "<br/>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream,
                     "        <nodes_configured number=\"%d\" />\n",
                     g_list_length(data_set->nodes));
             fprintf(stream,
                     "        <resources_configured number=\"%d\" disabled=\"%d\" blocked=\"%d\" />\n",
                     count_resources(data_set, NULL),
                     data_set->disabled_resources, data_set->blocked_resources);
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print cluster-wide options
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  *
  * \note Currently this is only implemented for HTML and XML output, and
  *       prints only a few options. If there is demand, more could be added.
  */
 static void
 print_cluster_options(FILE *stream, pe_working_set_t *data_set)
 {
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
                 print_as("\n              *** Resource management is DISABLED ***");
                 print_as("\n  The cluster will not attempt to start, stop or recover services");
                 print_as("\n");
             }
             break;
 
         case mon_output_html:
             fprintf(stream, " </p>\n <h3>Config Options</h3>\n");
             fprintf(stream, " <table>\n");
             fprintf(stream, "  <tr><th>STONITH of failed nodes</th><td>%s</td></tr>\n",
                     is_set(data_set->flags, pe_flag_stonith_enabled)? "enabled" : "disabled");
 
             fprintf(stream, "  <tr><th>Cluster is</th><td>%ssymmetric</td></tr>\n",
                     is_set(data_set->flags, pe_flag_symmetric_cluster)? "" : "a");
 
             fprintf(stream, "  <tr><th>No Quorum Policy</th><td>");
             switch (data_set->no_quorum_policy) {
                 case no_quorum_freeze:
                     fprintf(stream, "Freeze resources");
                     break;
                 case no_quorum_stop:
                     fprintf(stream, "Stop ALL resources");
                     break;
                 case no_quorum_ignore:
                     fprintf(stream, "Ignore");
                     break;
                 case no_quorum_suicide:
                     fprintf(stream, "Suicide");
                     break;
             }
             fprintf(stream, "</td></tr>\n");
 
             fprintf(stream, "  <tr><th>Resource management</th><td>");
             if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
                 fprintf(stream, "<strong>DISABLED</strong> (the cluster will "
                                 "not attempt to start, stop or recover services)");
             } else {
                 fprintf(stream, "enabled");
             }
             fprintf(stream, "</td></tr>\n");
 
             fprintf(stream, "</table>\n <p>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "        <cluster_options");
             fprintf(stream, " stonith-enabled=\"%s\"",
                     is_set(data_set->flags, pe_flag_stonith_enabled)?
                     "true" : "false");
             fprintf(stream, " symmetric-cluster=\"%s\"",
                     is_set(data_set->flags, pe_flag_symmetric_cluster)?
                     "true" : "false");
             fprintf(stream, " no-quorum-policy=\"");
             switch (data_set->no_quorum_policy) {
                 case no_quorum_freeze:
                     fprintf(stream, "freeze");
                     break;
                 case no_quorum_stop:
                     fprintf(stream, "stop");
                     break;
                 case no_quorum_ignore:
                     fprintf(stream, "ignore");
                     break;
                 case no_quorum_suicide:
                     fprintf(stream, "suicide");
                     break;
             }
             fprintf(stream, "\"");
             fprintf(stream, " maintenance-mode=\"%s\"",
                     is_set(data_set->flags, pe_flag_maintenance_mode)?
                     "true" : "false");
             fprintf(stream, " />\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Get the name of the stack in use (or "unknown" if not available)
  *
  * \param[in] data_set   Working set of CIB state
  *
  * \return String representing stack name
  */
 static const char *
 get_cluster_stack(pe_working_set_t *data_set)
 {
     xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
                                       data_set->input, LOG_DEBUG);
     return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
 }
 
 /*!
  * \internal
  * \brief Print a summary of cluster-wide information
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_cluster_summary(FILE *stream, pe_working_set_t *data_set)
 {
     const char *stack_s = get_cluster_stack(data_set);
     gboolean header_printed = FALSE;
 
     if (show & mon_show_stack) {
         if (header_printed == FALSE) {
             print_cluster_summary_header(stream);
             header_printed = TRUE;
         }
         print_cluster_stack(stream, stack_s);
     }
 
     /* Always print DC if none, even if not requested */
     if ((data_set->dc_node == NULL) || (show & mon_show_dc)) {
         if (header_printed == FALSE) {
             print_cluster_summary_header(stream);
             header_printed = TRUE;
         }
         print_cluster_dc(stream, data_set);
     }
 
     if (show & mon_show_times) {
         if (header_printed == FALSE) {
             print_cluster_summary_header(stream);
             header_printed = TRUE;
         }
         print_cluster_times(stream, data_set);
     }
 
     if (is_set(data_set->flags, pe_flag_maintenance_mode)
         || data_set->disabled_resources
         || data_set->blocked_resources
         || is_set(show, mon_show_count)) {
         if (header_printed == FALSE) {
             print_cluster_summary_header(stream);
             header_printed = TRUE;
         }
         print_cluster_counts(stream, data_set, stack_s);
     }
 
     /* There is not a separate option for showing cluster options, so show with
      * stack for now; a separate option could be added if there is demand
      */
     if (show & mon_show_stack) {
         print_cluster_options(stream, data_set);
     }
 
     if (header_printed) {
         print_cluster_summary_footer(stream);
     }
 }
 
 /*!
  * \internal
  * \brief Print a failed action
  *
  * \param[in] stream     File stream to display output to
  * \param[in] xml_op     Root of XML tree describing failed action
  */
 static void
 print_failed_action(FILE *stream, xmlNode *xml_op)
 {
     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
     const char *op_key_attr = "op_key";
     const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE);
     const char *node = crm_element_value(xml_op, XML_ATTR_UNAME);
     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
     int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0");
     int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0");
     char *exit_reason_cleaned;
 
     /* If no op_key was given, use id instead */
     if (op_key == NULL) {
         op_key = ID(xml_op);
         op_key_attr = "id";
     }
 
     /* If no exit reason was given, use "none" */
     if (exit_reason == NULL) {
         exit_reason = "none";
     }
 
     /* Print common action information */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("* %s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
                      op_key, node, services_ocf_exitcode_str(rc), rc,
                      call, services_lrm_status_str(status), exit_reason);
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "  <li>%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
                      op_key, node, services_ocf_exitcode_str(rc), rc,
                      call, services_lrm_status_str(status), exit_reason);
             break;
 
         case mon_output_xml:
             exit_reason_cleaned = crm_xml_escape(exit_reason);
             fprintf(stream, "        <failure %s=\"%s\" node=\"%s\"",
                     op_key_attr, op_key, node);
             fprintf(stream, " exitstatus=\"%s\" exitreason=\"%s\" exitcode=\"%d\"",
                     services_ocf_exitcode_str(rc), exit_reason_cleaned, rc);
             fprintf(stream, " call=\"%s\" status=\"%s\"",
                     call, services_lrm_status_str(status));
             free(exit_reason_cleaned);
             break;
 
         default:
             break;
     }
 
     /* If last change was given, print timing information as well */
     if (last) {
         time_t run_at = crm_parse_int(last, "0");
         char *run_at_s = ctime(&run_at);
 
         if (run_at_s) {
             run_at_s[24] = 0; /* Overwrite the newline */
         }
 
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as(",\n    last-rc-change='%s', queued=%sms, exec=%sms",
                          run_at_s? run_at_s : "",
                          crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
                          crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, " last-rc-change='%s', queued=%sms, exec=%sms",
                         run_at_s? run_at_s : "",
                         crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
                         crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
                 break;
 
             case mon_output_xml:
                 fprintf(stream,
                         " last-rc-change=\"%s\" queued=\"%s\" exec=\"%s\" interval=\"%u\" task=\"%s\"",
                         run_at_s? run_at_s : "",
                         crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
                         crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
                         crm_parse_ms(crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS)),
                         crm_element_value(xml_op, XML_LRM_ATTR_TASK));
                 break;
 
             default:
                 break;
         }
     }
 
     /* End the action listing */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, "</li>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, " />\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print a section for failed actions
  *
  * \param[in] stream     File stream to display output to
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_failed_actions(FILE *stream, pe_working_set_t *data_set)
 {
     xmlNode *xml_op = NULL;
 
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\nFailed Resource Actions:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream,
                     " <hr />\n <h2>Failed Resource Actions</h2>\n <ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <failures>\n");
             break;
 
         default:
             break;
     }
 
     /* Print each failed action */
     for (xml_op = __xml_first_child(data_set->failed); xml_op != NULL;
          xml_op = __xml_next(xml_op)) {
         print_failed_action(stream, xml_op);
     }
 
     /* End section */
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " </ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    </failures>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Reduce the stonith-history
  *        for successful actions we keep the last of every action-type & target
  *        for failed actions we record as well who had failed
  *        for actions in progress we keep full track
  *
  * \param[in] history    List of stonith actions
  *
  */
 static stonith_history_t *
 reduce_stonith_history(stonith_history_t *history)
 {
     stonith_history_t *new = NULL, *hp, *np, *tmp;
 
     for (hp = history; hp; ) {
         for (np = new; np; np = np->next) {
             if ((hp->state == st_done) || (hp->state == st_failed)) {
                 /* action not in progress */
                 if (safe_str_eq(hp->target, np->target) &&
                     safe_str_eq(hp->action, np->action) &&
                     (hp->state == np->state)) {
                     if ((hp->state == st_done) ||
                         safe_str_eq(hp->delegate, np->delegate)) {
                         /* replace or purge */
                         if (hp->completed < np->completed) {
                             /* purge older hp */
                             tmp = hp->next;
                             hp->next = NULL;
                             stonith_history_free(hp);
                             hp = tmp;
                             break;
                         }
                         /* damn single linked list */
                         free(hp->target);
                         free(hp->action);
                         free(np->origin);
                         np->origin = hp->origin;
                         free(np->delegate);
                         np->delegate = hp->delegate;
                         free(np->client);
                         np->client = hp->client;
                         np->completed = hp->completed;
                         tmp = hp;
                         hp = hp->next;
                         free(tmp);
                         break;
                     }
                 }
                 if (np->next) {
                     continue;
                 }
             }
             np = 0; /* let outer loop progress hp */
             break;
         }
         /* simply move hp from history to new */
         if (np == NULL) {
             tmp = hp->next;
             hp->next = new;
             new = hp;
             hp = tmp;
         }
     }
     return new;
 }
 
 /*!
  * \internal
  * \brief Sort the stonith-history
  *        sort by competed most current on the top
  *        pending actions lacking a completed-stamp are gathered at the top
  *
  * \param[in] history    List of stonith actions
  *
  */
 static stonith_history_t *
 sort_stonith_history(stonith_history_t *history)
 {
     stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
 
     for (hp = history; hp; ) {
         tmp = hp->next;
         if ((hp->state == st_done) || (hp->state == st_failed)) {
             /* sort into new */
             if ((!new) || (hp->completed > new->completed)) {
                 hp->next = new;
                 new = hp;
             } else {
                 np = new;
                 do {
                     if ((!np->next) || (hp->completed > np->next->completed)) {
                         hp->next = np->next;
                         np->next = hp;
                         break;
                     }
                     np = np->next;
                 } while (1);
             }
         } else {
             /* put into pending */
             hp->next = pending;
             pending = hp;
         }
         hp = tmp;
     }
 
     /* pending actions don't have a completed-stamp so make them go front */
     if (pending) {
         stonith_history_t *last_pending = pending;
 
         while (last_pending->next) {
             last_pending = last_pending->next;
         }
 
         last_pending->next = new;
         new = pending;
     }
     return new;
 }
 
 /*!
  * \internal
  * \brief Turn stonith action into a better readable string
  *
  * \param[in] action     Stonith action
  */
 static char *
 fence_action_str(const char *action)
 {
     char *str = NULL;
 
     if (action == NULL) {
         str = strdup("fencing");
     } else if (!strcmp(action, "on")) {
         str = strdup("unfencing");
     } else if (!strcmp(action, "off")) {
         str = strdup("turning off");
     } else {
         str = strdup(action);
     }
     return str;
 }
 
 /*!
  * \internal
  * \brief Print a stonith action
  *
  * \param[in] stream     File stream to display output to
  * \param[in] event      stonith event
  */
 static void
 print_stonith_action(FILE *stream, stonith_history_t *event)
 {
     char *action_s = fence_action_str(event->action);
     char *run_at_s = ctime(&event->completed);
 
     if ((run_at_s) && (run_at_s[0] != 0)) {
         run_at_s[strlen(run_at_s)-1] = 0; /* Overwrite the newline */
     }
 
     switch(output_format) {
         case mon_output_xml:
             fprintf(stream, "        <fence_event target=\"%s\" action=\"%s\"",
                     event->target, event->action);
             switch(event->state) {
                 case st_done:
                     fprintf(stream, " state=\"success\"");
                     break;
                 case st_failed:
                     fprintf(stream, " state=\"failed\"");
                     break;
                 default:
                     fprintf(stream, " state=\"pending\"");
             }
             fprintf(stream, " origin=\"%s\" client=\"%s\"",
                     event->origin, event->client);
             if (event->delegate) {
                 fprintf(stream, " delegate=\"%s\"", event->delegate);
             }
             switch(event->state) {
                 case st_done:
                 case st_failed:
                     fprintf(stream, " completed=\"%s\"", run_at_s?run_at_s:"");
                     break;
                 default:
                     break;
             }
             fprintf(stream, " />\n");
             break;
 
         case mon_output_plain:
         case mon_output_console:
             switch(event->state) {
                 case st_done:
                     print_as("* %s of %s successful: delegate=%s, client=%s, origin=%s,\n"
                              "    %s='%s'\n",
                              action_s, event->target,
                              event->delegate ? event->delegate : "",
                              event->client, event->origin,
                              fence_full_history?"completed":"last-successful",
                              run_at_s?run_at_s:"");
                     break;
                 case st_failed:
                     print_as("* %s of %s failed: delegate=%s, client=%s, origin=%s,\n"
                              "    %s='%s'\n",
                              action_s, event->target,
                              event->delegate ? event->delegate : "",
                              event->client, event->origin,
                              fence_full_history?"completed":"last-failed",
                              run_at_s?run_at_s:"");
                     break;
                 default:
                     print_as("* %s of %s pending: client=%s, origin=%s\n",
                              action_s, event->target,
                              event->client, event->origin);
             }
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             switch(event->state) {
                 case st_done:
                     fprintf(stream, "  <li>%s of %s successful: delegate=%s, "
                                     "client=%s, origin=%s, %s='%s'</li>\n",
                                     action_s, event->target,
                                     event->delegate ? event->delegate : "",
                                     event->client, event->origin,
                                     fence_full_history?"completed":"last-successful",
                                     run_at_s?run_at_s:"");
                     break;
                 case st_failed:
                     fprintf(stream, "  <li>%s of %s failed: delegate=%s, "
                                     "client=%s, origin=%s, %s='%s'</li>\n",
                                     action_s, event->target,
                                     event->delegate ? event->delegate : "",
                                     event->client, event->origin,
                                     fence_full_history?"completed":"last-failed",
                                     run_at_s?run_at_s:"");
                     break;
                 default:
                     fprintf(stream, "  <li>%s of %s pending: client=%s, "
                                     "origin=%s</li>\n",
                                     action_s, event->target,
                                     event->client, event->origin);
             }
             break;
 
         default:
             /* no support for fence history for other formats so far */
             break;
     }
 
     free(action_s);
 }
 
 /*!
  * \internal
  * \brief Print a section for failed stonith actions
  *
  * \param[in] stream     File stream to display output to
  * \param[in] history    List of stonith actions
  *
  */
 static void
 print_failed_stonith_actions(FILE *stream, stonith_history_t *history)
 {
     stonith_history_t *hp;
 
     for (hp = history; hp; hp = hp->next) {
         if (hp->state == st_failed) {
             break;
         }
     }
     if (!hp) {
         return;
     }
 
     /* Print section heading */
     switch (output_format) {
         /* no need to take care of xml in here as xml gets full
          * history anyway
          */
         case mon_output_plain:
         case mon_output_console:
             print_as("\nFailed Fencing Actions:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>Failed Fencing Actions</h2>\n <ul>\n");
             break;
 
         default:
             break;
     }
 
     /* Print each failed stonith action */
     for (hp = history; hp; hp = hp->next) {
         if (hp->state == st_failed) {
             print_stonith_action(stream, hp);
         }
     }
 
     /* End section */
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " </ul>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print pending stonith actions
  *
  * \param[in] stream     File stream to display output to
  * \param[in] history    List of stonith actions
  *
  */
 static void
 print_stonith_pending(FILE *stream, stonith_history_t *history)
 {
     /* xml-output always shows the full history
      * so we'll never have to show pending-actions
      * separately
      */
     if (history && (history->state != st_failed) &&
         (history->state != st_done)) {
         stonith_history_t *hp;
 
         /* Print section heading */
         switch (output_format) {
             case mon_output_plain:
             case mon_output_console:
                 print_as("\nPending Fencing Actions:\n");
                 break;
 
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, " <hr />\n <h2>Pending Fencing Actions</h2>\n <ul>\n");
                 break;
 
             default:
                 break;
         }
 
         for (hp = history; hp; hp = hp->next) {
             if ((hp->state == st_failed) || (hp->state == st_done)) {
                 break;
             }
             print_stonith_action(stream, hp);
         }
 
         /* End section */
         switch (output_format) {
             case mon_output_html:
             case mon_output_cgi:
                 fprintf(stream, " </ul>\n");
                 break;
 
         default:
             break;
         }
     }
 }
 
 /*!
  * \internal
  * \brief Print a section for stonith-history
  *
  * \param[in] stream     File stream to display output to
  * \param[in] history    List of stonith actions
  *
  */
 static void
 print_stonith_history(FILE *stream, stonith_history_t *history)
 {
     stonith_history_t *hp;
 
     /* Print section heading */
     switch (output_format) {
         case mon_output_plain:
         case mon_output_console:
             print_as("\nFencing History:\n");
             break;
 
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " <hr />\n <h2>Fencing History</h2>\n <ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    <fence_history>\n");
             break;
 
         default:
             break;
     }
 
     for (hp = history; hp; hp = hp->next) {
         if ((hp->state != st_failed) || (output_format == mon_output_xml)) {
             print_stonith_action(stream, hp);
         }
     }
 
     /* End section */
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             fprintf(stream, " </ul>\n");
             break;
 
         case mon_output_xml:
             fprintf(stream, "    </fence_history>\n");
             break;
 
         default:
             break;
     }
 }
 
 /*!
  * \internal
  * \brief Print cluster status to screen
  *
  * This uses the global display preferences set by command-line options
  * to display cluster status in a human-friendly way.
  *
  * \param[in] data_set          Working set of CIB state
  * \param[in] stonith_history   List of stonith actions
  */
 static void
 print_status(pe_working_set_t * data_set,
              stonith_history_t *stonith_history)
 {
     GListPtr gIter = NULL;
     int print_opts = get_resource_display_options();
 
     /* space-separated lists of node names */
     char *online_nodes = NULL;
     char *online_remote_nodes = NULL;
     char *online_guest_nodes = NULL;
     char *offline_nodes = NULL;
     char *offline_remote_nodes = NULL;
 
     if (output_format == mon_output_console) {
         blank_screen();
     }
     print_cluster_summary(stdout, data_set);
     print_as("\n");
 
     /* Gather node information (and print if in bad state or grouping by node) */
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         const char *node_mode = NULL;
         char *node_name = get_node_display_name(node);
 
         /* Get node mode */
         if (node->details->unclean) {
             if (node->details->online) {
                 node_mode = "UNCLEAN (online)";
 
             } else if (node->details->pending) {
                 node_mode = "UNCLEAN (pending)";
 
             } else {
                 node_mode = "UNCLEAN (offline)";
             }
 
         } else if (node->details->pending) {
             node_mode = "pending";
 
         } else if (node->details->standby_onfail && node->details->online) {
             node_mode = "standby (on-fail)";
 
         } else if (node->details->standby) {
             if (node->details->online) {
                 if (node->details->running_rsc) {
                     node_mode = "standby (with active resources)";
                 } else {
                     node_mode = "standby";
                 }
             } else {
                 node_mode = "OFFLINE (standby)";
             }
 
         } else if (node->details->maintenance) {
             if (node->details->online) {
                 node_mode = "maintenance";
             } else {
                 node_mode = "OFFLINE (maintenance)";
             }
 
         } else if (node->details->online) {
             node_mode = "online";
             if (group_by_node == FALSE) {
                 if (is_container_remote_node(node)) {
                     online_guest_nodes = add_list_element(online_guest_nodes, node_name);
                 } else if (is_baremetal_remote_node(node)) {
                     online_remote_nodes = add_list_element(online_remote_nodes, node_name);
                 } else {
                     online_nodes = add_list_element(online_nodes, node_name);
                 }
                 free(node_name);
                 continue;
             }
         } else {
             node_mode = "OFFLINE";
             if (group_by_node == FALSE) {
                 if (is_baremetal_remote_node(node)) {
                     offline_remote_nodes = add_list_element(offline_remote_nodes, node_name);
                 } else if (is_container_remote_node(node)) {
                     /* ignore offline guest nodes */
                 } else {
                     offline_nodes = add_list_element(offline_nodes, node_name);
                 }
                 free(node_name);
                 continue;
             }
         }
 
         /* If we get here, node is in bad state, or we're grouping by node */
 
         /* Print the node name and status */
         if (is_container_remote_node(node)) {
             print_as("Guest");
         } else if (is_baremetal_remote_node(node)) {
             print_as("Remote");
         }
         print_as("Node %s: %s\n", node_name, node_mode);
 
         /* If we're grouping by node, print its resources */
         if (group_by_node) {
             if (print_brief) {
                 print_rscs_brief(node->details->running_rsc, "\t", print_opts | pe_print_rsconly,
                                  stdout, FALSE);
             } else {
                 GListPtr gIter2 = NULL;
 
                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
                     resource_t *rsc = (resource_t *) gIter2->data;
 
                     rsc->fns->print(rsc, "\t", print_opts | pe_print_rsconly, stdout);
                 }
             }
         }
         free(node_name);
     }
 
     /* If we're not grouping by node, summarize nodes by status */
     if (online_nodes) {
         print_as("Online: [%s ]\n", online_nodes);
         free(online_nodes);
     }
     if (offline_nodes) {
         print_as("OFFLINE: [%s ]\n", offline_nodes);
         free(offline_nodes);
     }
     if (online_remote_nodes) {
         print_as("RemoteOnline: [%s ]\n", online_remote_nodes);
         free(online_remote_nodes);
     }
     if (offline_remote_nodes) {
         print_as("RemoteOFFLINE: [%s ]\n", offline_remote_nodes);
         free(offline_remote_nodes);
     }
     if (online_guest_nodes) {
         print_as("GuestOnline: [%s ]\n", online_guest_nodes);
         free(online_guest_nodes);
     }
 
     /* Print resources section, if needed */
     print_resources(stdout, data_set, print_opts);
 
     /* print Node Attributes section if requested */
     if (show & mon_show_attributes) {
         print_node_attributes(stdout, data_set);
     }
 
     /* If requested, print resource operations (which includes failcounts)
      * or just failcounts
      */
     if (show & (mon_show_operations | mon_show_failcounts)) {
         print_node_summary(stdout, data_set,
                            ((show & mon_show_operations)? TRUE : FALSE));
     }
 
     /* If there were any failed actions, print them */
     if (xml_has_children(data_set->failed)) {
         print_failed_actions(stdout, data_set);
     }
 
     /* Print failed stonith actions */
     if (fence_history) {
         print_failed_stonith_actions(stdout, stonith_history);
     }
 
     /* Print tickets if requested */
     if (show & mon_show_tickets) {
         print_cluster_tickets(stdout, data_set);
     }
 
     /* Print negative location constraints if requested */
     if (show & mon_show_bans) {
         print_neg_locations(stdout, data_set);
     }
 
     /* Print stonith history */
     if (fence_history) {
         if (show & mon_show_fence_history) {
             print_stonith_history(stdout, stonith_history);
         } else {
             print_stonith_pending(stdout, stonith_history);
         }
     }
 
 #if CURSES_ENABLED
     if (output_format == mon_output_console) {
         refresh();
     }
 #endif
 }
 
 /*!
  * \internal
  * \brief Print cluster status in XML format
  *
  * \param[in] data_set   Working set of CIB state
  */
 static void
 print_xml_status(pe_working_set_t * data_set,
                  stonith_history_t *stonith_history)
 {
     FILE *stream = stdout;
     GListPtr gIter = NULL;
     int print_opts = get_resource_display_options();
 
     fprintf(stream, "<?xml version=\"1.0\"?>\n");
     fprintf(stream, "<crm_mon version=\"%s\">\n", VERSION);
 
     print_cluster_summary(stream, data_set);
 
     /*** NODES ***/
     fprintf(stream, "    <nodes>\n");
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         const char *node_type = "unknown";
 
         switch (node->details->type) {
             case node_member:
                 node_type = "member";
                 break;
             case node_remote:
                 node_type = "remote";
                 break;
             case node_ping:
                 node_type = "ping";
                 break;
         }
 
         fprintf(stream, "        <node name=\"%s\" ", node->details->uname);
         fprintf(stream, "id=\"%s\" ", node->details->id);
         fprintf(stream, "online=\"%s\" ", node->details->online ? "true" : "false");
         fprintf(stream, "standby=\"%s\" ", node->details->standby ? "true" : "false");
         fprintf(stream, "standby_onfail=\"%s\" ", node->details->standby_onfail ? "true" : "false");
         fprintf(stream, "maintenance=\"%s\" ", node->details->maintenance ? "true" : "false");
         fprintf(stream, "pending=\"%s\" ", node->details->pending ? "true" : "false");
         fprintf(stream, "unclean=\"%s\" ", node->details->unclean ? "true" : "false");
         fprintf(stream, "shutdown=\"%s\" ", node->details->shutdown ? "true" : "false");
         fprintf(stream, "expected_up=\"%s\" ", node->details->expected_up ? "true" : "false");
         fprintf(stream, "is_dc=\"%s\" ", node->details->is_dc ? "true" : "false");
         fprintf(stream, "resources_running=\"%d\" ", g_list_length(node->details->running_rsc));
         fprintf(stream, "type=\"%s\" ", node_type);
         if (is_container_remote_node(node)) {
             fprintf(stream, "id_as_resource=\"%s\" ", node->details->remote_rsc->container->id);
         }
 
         if (group_by_node) {
             GListPtr lpc2 = NULL;
 
             fprintf(stream, ">\n");
             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
                 resource_t *rsc = (resource_t *) lpc2->data;
 
                 rsc->fns->print(rsc, "            ", print_opts | pe_print_rsconly, stream);
             }
             fprintf(stream, "        </node>\n");
         } else {
             fprintf(stream, "/>\n");
         }
     }
     fprintf(stream, "    </nodes>\n");
 
     /* Print resources section, if needed */
     print_resources(stream, data_set, print_opts);
 
     /* print Node Attributes section if requested */
     if (show & mon_show_attributes) {
         print_node_attributes(stream, data_set);
     }
 
     /* If requested, print resource operations (which includes failcounts)
      * or just failcounts
      */
     if (show & (mon_show_operations | mon_show_failcounts)) {
         print_node_summary(stream, data_set,
                            ((show & mon_show_operations)? TRUE : FALSE));
     }
 
     /* If there were any failed actions, print them */
     if (xml_has_children(data_set->failed)) {
         print_failed_actions(stream, data_set);
     }
 
     /* Print stonith history */
     if (fence_history) {
         print_stonith_history(stdout, stonith_history);
     }
 
     /* Print tickets if requested */
     if (show & mon_show_tickets) {
         print_cluster_tickets(stream, data_set);
     }
 
     /* Print negative location constraints if requested */
     if (show & mon_show_bans) {
         print_neg_locations(stream, data_set);
     }
 
     fprintf(stream, "</crm_mon>\n");
     fflush(stream);
     fclose(stream);
 }
 
 /*!
  * \internal
  * \brief Print cluster status in HTML format (with HTTP headers if CGI)
  *
  * \param[in] data_set   Working set of CIB state
  * \param[in] filename   Name of file to write HTML to (ignored if CGI)
  *
  * \return 0 on success, -1 on error
  */
 static int
 print_html_status(pe_working_set_t * data_set,
                   const char *filename,
                   stonith_history_t *stonith_history)
 {
     FILE *stream;
     GListPtr gIter = NULL;
     char *filename_tmp = NULL;
     int print_opts = get_resource_display_options();
 
     if (output_format == mon_output_cgi) {
         stream = stdout;
         fprintf(stream, "Content-Type: text/html\n\n");
 
     } else {
         filename_tmp = crm_concat(filename, "tmp", '.');
         stream = fopen(filename_tmp, "w");
         if (stream == NULL) {
             crm_perror(LOG_ERR, "Cannot open %s for writing", filename_tmp);
             free(filename_tmp);
             return -1;
         }
     }
 
     fprintf(stream, "<html>\n");
     fprintf(stream, " <head>\n");
     fprintf(stream, "  <title>Cluster status</title>\n");
     fprintf(stream, "  <meta http-equiv=\"refresh\" content=\"%d\">\n", reconnect_msec / 1000);
     fprintf(stream, " </head>\n");
     fprintf(stream, "<body>\n");
 
     print_cluster_summary(stream, data_set);
 
     /*** NODE LIST ***/
 
     fprintf(stream, " <hr />\n <h2>Node List</h2>\n");
     fprintf(stream, "<ul>\n");
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         char *node_name = get_node_display_name(node);
 
         fprintf(stream, "<li>Node: %s: ", node_name);
         if (node->details->standby_onfail && node->details->online) {
             fprintf(stream, "<font color=\"orange\">standby (on-fail)</font>\n");
         } else if (node->details->standby && node->details->online) {
 
             fprintf(stream, "<font color=\"orange\">standby%s</font>\n",
                 node->details->running_rsc?" (with active resources)":"");
         } else if (node->details->standby) {
             fprintf(stream, "<font color=\"red\">OFFLINE (standby)</font>\n");
         } else if (node->details->maintenance && node->details->online) {
             fprintf(stream, "<font color=\"blue\">maintenance</font>\n");
         } else if (node->details->maintenance) {
             fprintf(stream, "<font color=\"red\">OFFLINE (maintenance)</font>\n");
         } else if (node->details->online) {
             fprintf(stream, "<font color=\"green\">online</font>\n");
         } else {
             fprintf(stream, "<font color=\"red\">OFFLINE</font>\n");
         }
         if (print_brief && group_by_node) {
             fprintf(stream, "<ul>\n");
             print_rscs_brief(node->details->running_rsc, NULL, print_opts | pe_print_rsconly,
                              stream, FALSE);
             fprintf(stream, "</ul>\n");
 
         } else if (group_by_node) {
             GListPtr lpc2 = NULL;
 
             fprintf(stream, "<ul>\n");
             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
                 resource_t *rsc = (resource_t *) lpc2->data;
 
                 fprintf(stream, "<li>");
                 rsc->fns->print(rsc, NULL, print_opts | pe_print_rsconly, stream);
                 fprintf(stream, "</li>\n");
             }
             fprintf(stream, "</ul>\n");
         }
         fprintf(stream, "</li>\n");
         free(node_name);
     }
     fprintf(stream, "</ul>\n");
 
     /* Print resources section, if needed */
     print_resources(stream, data_set, print_opts);
 
     /* print Node Attributes section if requested */
     if (show & mon_show_attributes) {
         print_node_attributes(stream, data_set);
     }
 
     /* If requested, print resource operations (which includes failcounts)
      * or just failcounts
      */
     if (show & (mon_show_operations | mon_show_failcounts)) {
         print_node_summary(stream, data_set,
                            ((show & mon_show_operations)? TRUE : FALSE));
     }
 
     /* If there were any failed actions, print them */
     if (xml_has_children(data_set->failed)) {
         print_failed_actions(stream, data_set);
     }
 
     /* Print failed stonith actions */
     if (fence_history) {
         print_failed_stonith_actions(stream, stonith_history);
     }
 
     /* Print stonith history */
     if (fence_history) {
         if (show & mon_show_fence_history) {
             print_stonith_history(stream, stonith_history);
         } else {
             print_stonith_pending(stdout, stonith_history);
         }
     }
 
     /* Print tickets if requested */
     if (show & mon_show_tickets) {
         print_cluster_tickets(stream, data_set);
     }
 
     /* Print negative location constraints if requested */
     if (show & mon_show_bans) {
         print_neg_locations(stream, data_set);
     }
 
     fprintf(stream, "</body>\n");
     fprintf(stream, "</html>\n");
     fflush(stream);
     fclose(stream);
 
     if (output_format != mon_output_cgi) {
         if (rename(filename_tmp, filename) != 0) {
             crm_perror(LOG_ERR, "Unable to rename %s->%s", filename_tmp, filename);
         }
         free(filename_tmp);
     }
     return 0;
 }
 
 static int
 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
                  int status, const char *desc)
 {
     pid_t pid;
 
     /*setenv needs chars, these are ints */
     char *rc_s = crm_itoa(rc);
     char *status_s = crm_itoa(status);
     char *target_rc_s = crm_itoa(target_rc);
 
     crm_debug("Sending external notification to '%s' via '%s'", external_recipient, external_agent);
 
     if(rsc) {
         setenv("CRM_notify_rsc", rsc, 1);
     }
     if (external_recipient) {
         setenv("CRM_notify_recipient", external_recipient, 1);
     }
     setenv("CRM_notify_node", node, 1);
     setenv("CRM_notify_task", task, 1);
     setenv("CRM_notify_desc", desc, 1);
     setenv("CRM_notify_rc", rc_s, 1);
     setenv("CRM_notify_target_rc", target_rc_s, 1);
     setenv("CRM_notify_status", status_s, 1);
 
     pid = fork();
     if (pid == -1) {
         crm_perror(LOG_ERR, "notification fork() failed.");
     }
     if (pid == 0) {
         /* crm_debug("notification: I am the child. Executing the nofitication program."); */
         execl(external_agent, external_agent, NULL);
         exit(CRM_EX_ERROR);
     }
 
     crm_trace("Finished running custom notification program '%s'.", external_agent);
     free(target_rc_s);
     free(status_s);
     free(rc_s);
     return 0;
 }
 
 static void
 handle_rsc_op(xmlNode * xml, const char *node_id)
 {
     int rc = -1;
     int status = -1;
     int action = -1;
     int target_rc = -1;
     int transition_num = -1;
     gboolean notify = TRUE;
 
     char *rsc = NULL;
     char *task = NULL;
     const char *desc = NULL;
     const char *magic = NULL;
     const char *id = NULL;
     char *update_te_uuid = NULL;
     const char *node = NULL;
 
     xmlNode *n = xml;
     xmlNode * rsc_op = xml;
 
     if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
         xmlNode *cIter;
 
         for(cIter = xml->children; cIter; cIter = cIter->next) {
             handle_rsc_op(cIter, node_id);
         }
 
         return;
     }
 
     id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
     if (id == NULL) {
         /* Compatibility with <= 1.1.5 */
         id = ID(rsc_op);
     }
 
     magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
     if (magic == NULL) {
         /* non-change */
         return;
     }
 
     if (FALSE == decode_transition_magic(magic, &update_te_uuid, &transition_num, &action,
                                          &status, &rc, &target_rc)) {
         crm_err("Invalid event %s detected for %s", magic, id);
         return;
     }
 
     if (parse_op_key(id, &rsc, &task, NULL) == FALSE) {
         crm_err("Invalid event detected for %s", id);
         goto bail;
     }
 
     node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
 
     while (n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) {
         n = n->parent;
     }
 
     if(node == NULL && n) {
         node = crm_element_value(n, XML_ATTR_UNAME);
     }
 
     if (node == NULL && n) {
         node = ID(n);
     }
 
     if (node == NULL) {
         node = node_id;
     }
 
     if (node == NULL) {
         crm_err("No node detected for event %s (%s)", magic, id);
         goto bail;
     }
 
     /* look up where we expected it to be? */
     desc = pcmk_strerror(pcmk_ok);
     if (status == PCMK_LRM_OP_DONE && target_rc == rc) {
         crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc);
         if (rc == PCMK_OCF_NOT_RUNNING) {
             notify = FALSE;
         }
 
     } else if (status == PCMK_LRM_OP_DONE) {
         desc = services_ocf_exitcode_str(rc);
         crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
 
     } else {
         desc = services_lrm_status_str(status);
         crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
     }
 
     if (notify && external_agent) {
         send_custom_trap(node, rsc, task, target_rc, rc, status, desc);
     }
   bail:
     free(update_te_uuid);
     free(rsc);
     free(task);
 }
 
 static gboolean
 mon_trigger_refresh(gpointer user_data)
 {
     mainloop_set_trigger(refresh_trigger);
     return FALSE;
 }
 
 #define NODE_PATT "/lrm[@id="
 static char *
 get_node_from_xpath(const char *xpath)
 {
     char *nodeid = NULL;
     char *tmp = strstr(xpath, NODE_PATT);
 
     if(tmp) {
         tmp += strlen(NODE_PATT);
         tmp += 1;
 
         nodeid = strdup(tmp);
         tmp = strstr(nodeid, "\'");
         CRM_ASSERT(tmp);
         tmp[0] = 0;
     }
     return nodeid;
 }
 
 static void
 crm_diff_update_v2(const char *event, xmlNode * msg)
 {
     xmlNode *change = NULL;
     xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 
     for (change = __xml_first_child(diff); change != NULL; change = __xml_next(change)) {
         const char *name = NULL;
         const char *op = crm_element_value(change, XML_DIFF_OP);
         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
         xmlNode *match = NULL;
         const char *node = NULL;
 
         if(op == NULL) {
             continue;
 
         } else if(strcmp(op, "create") == 0) {
             match = change->children;
 
         } else if(strcmp(op, "move") == 0) {
             continue;
 
         } else if(strcmp(op, "delete") == 0) {
             continue;
 
         } else if(strcmp(op, "modify") == 0) {
             match = first_named_child(change, XML_DIFF_RESULT);
             if(match) {
                 match = match->children;
             }
         }
 
         if(match) {
             name = (const char *)match->name;
         }
 
         crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
         if(xpath == NULL) {
             /* Version field, ignore */
 
         } else if(name == NULL) {
             crm_debug("No result for %s operation to %s", op, xpath);
             CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
 
         } else if(strcmp(name, XML_TAG_CIB) == 0) {
             xmlNode *state = NULL;
             xmlNode *status = first_named_child(match, XML_CIB_TAG_STATUS);
 
             for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
                 node = crm_element_value(state, XML_ATTR_UNAME);
                 if (node == NULL) {
                     node = ID(state);
                 }
                 handle_rsc_op(state, node);
             }
 
         } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
             xmlNode *state = NULL;
 
             for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
                 node = crm_element_value(state, XML_ATTR_UNAME);
                 if (node == NULL) {
                     node = ID(state);
                 }
                 handle_rsc_op(state, node);
             }
 
         } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
             node = crm_element_value(match, XML_ATTR_UNAME);
             if (node == NULL) {
                 node = ID(match);
             }
             handle_rsc_op(match, node);
 
         } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
             node = ID(match);
             handle_rsc_op(match, node);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
             char *local_node = get_node_from_xpath(xpath);
 
             handle_rsc_op(match, local_node);
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
             char *local_node = get_node_from_xpath(xpath);
 
             handle_rsc_op(match, local_node);
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
             char *local_node = get_node_from_xpath(xpath);
 
             handle_rsc_op(match, local_node);
             free(local_node);
 
         } else {
             crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
         }
     }
 }
 
 static void
 crm_diff_update_v1(const char *event, xmlNode * msg)
 {
     /* Process operation updates */
     xmlXPathObject *xpathObj = xpath_search(msg,
                                             "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
                                             "//" XML_LRM_TAG_RSC_OP);
     int lpc = 0, max = numXpathResults(xpathObj);
 
     for (lpc = 0; lpc < max; lpc++) {
         xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
 
         handle_rsc_op(rsc_op, NULL);
     }
     freeXpathObject(xpathObj);
 }
 
 static void
 crm_diff_update(const char *event, xmlNode * msg)
 {
     int rc = -1;
     static bool stale = FALSE;
     gboolean cib_updated = FALSE;
     xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 
     print_dot();
 
     if (current_cib != NULL) {
         rc = xml_apply_patchset(current_cib, diff, TRUE);
 
         switch (rc) {
             case -pcmk_err_diff_resync:
             case -pcmk_err_diff_failed:
                 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
                 free_xml(current_cib); current_cib = NULL;
                 break;
             case pcmk_ok:
                 cib_updated = TRUE;
                 break;
             default:
                 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
                 free_xml(current_cib); current_cib = NULL;
         }
     }
 
     if (current_cib == NULL) {
         crm_trace("Re-requesting the full cib");
         cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
     }
 
     if (external_agent) {
         int format = 0;
         crm_element_value_int(diff, "format", &format);
         switch(format) {
             case 1:
                 crm_diff_update_v1(event, msg);
                 break;
             case 2:
                 crm_diff_update_v2(event, msg);
                 break;
             default:
                 crm_err("Unknown patch format: %d", format);
         }
     }
 
     if (current_cib == NULL) {
         if(!stale) {
             print_as("--- Stale data ---");
         }
         stale = TRUE;
         return;
     }
 
     stale = FALSE;
     kick_refresh(cib_updated);
 }
 
 static gboolean
 mon_refresh_display(gpointer user_data)
 {
     xmlNode *cib_copy = copy_xml(current_cib);
     stonith_history_t *stonith_history = NULL;
 
     last_refresh = time(NULL);
 
     if (cli_config_update(&cib_copy, NULL, FALSE) == FALSE) {
         if (cib) {
             cib->cmds->signoff(cib);
         }
         print_as("Upgrade failed: %s", pcmk_strerror(-pcmk_err_schema_validation));
         if (output_format == mon_output_console) {
             sleep(2);
         }
         clean_up(CRM_EX_CONFIG);
         return FALSE;
     }
 
     /* get the stonith-history if there is evidence we need it
      */
     while (fence_history) {
         if (st != NULL) {
             if (st->cmds->history(st, st_opt_sync_call, NULL, &stonith_history, 120)) {
                 fprintf(stderr, "Critical: Unable to get stonith-history\n");
                 mon_cib_connection_destroy(NULL);
             } else {
                 if ((!fence_full_history) && (output_format != mon_output_xml)) {
                     stonith_history = reduce_stonith_history(stonith_history);
                 }
                 stonith_history = sort_stonith_history(stonith_history);
                 break; /* all other cases are errors */
             }
         } else {
             fprintf(stderr, "Critical: No stonith-API\n");
         }
         free_xml(cib_copy);
         print_as("Reading stonith-history failed");
         if (output_format == mon_output_console) {
             sleep(2);
         }
         return FALSE;
     }
 
     if (mon_data_set == NULL) {
         mon_data_set = pe_new_working_set();
         CRM_ASSERT(mon_data_set != NULL);
     }
 
     mon_data_set->input = cib_copy;
     cluster_status(mon_data_set);
 
     /* Unpack constraints if any section will need them
      * (tickets may be referenced in constraints but not granted yet,
      * and bans need negative location constraints) */
     if (show & (mon_show_bans | mon_show_tickets)) {
         xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
                                                    mon_data_set->input);
         unpack_constraints(cib_constraints, mon_data_set);
     }
 
     switch (output_format) {
         case mon_output_html:
         case mon_output_cgi:
             if (print_html_status(mon_data_set, output_filename, stonith_history) != 0) {
                 fprintf(stderr, "Critical: Unable to output html file\n");
                 clean_up(CRM_EX_CANTCREAT);
                 return FALSE;
             }
             break;
 
         case mon_output_xml:
             print_xml_status(mon_data_set, stonith_history);
             break;
 
         case mon_output_monitor:
             print_simple_status(mon_data_set, stonith_history);
             if (has_warnings) {
                 clean_up(MON_STATUS_WARN);
                 return FALSE;
             }
             break;
 
         case mon_output_plain:
         case mon_output_console:
             print_status(mon_data_set, stonith_history);
             break;
 
         case mon_output_none:
             break;
     }
 
     stonith_history_free(stonith_history);
     stonith_history = NULL;
     pe_reset_working_set(mon_data_set);
     return TRUE;
 }
 
 static void
 mon_st_callback_event(stonith_t * st, stonith_event_t * e)
 {
     if (st->state == stonith_disconnected) {
         /* disconnect cib as well and have everything reconnect */
         mon_cib_connection_destroy(NULL);
     } else if (external_agent) {
         char *desc = crm_strdup_printf("Operation %s requested by %s for peer %s: %s (ref=%s)",
                                     e->operation, e->origin, e->target, pcmk_strerror(e->result),
                                     e->id);
         send_custom_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
         free(desc);
     }
 }
 
 static void
 kick_refresh(gboolean data_updated)
 {
     static int updates = 0;
     long now = time(NULL);
 
     if (data_updated) {
         updates++;
     }
 
     if(refresh_timer == NULL) {
         refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL);
     }
 
     /* Refresh
      * - immediately if the last update was more than 5s ago
      * - every 10 cib-updates
      * - at most 2s after the last update
      */
     if ((now - last_refresh) > (reconnect_msec / 1000)) {
         mainloop_set_trigger(refresh_trigger);
         mainloop_timer_stop(refresh_timer);
         updates = 0;
 
     } else if(updates >= 10) {
         mainloop_set_trigger(refresh_trigger);
         mainloop_timer_stop(refresh_timer);
         updates = 0;
 
     } else {
         mainloop_timer_start(refresh_timer);
     }
 }
 
 static void
 mon_st_callback_display(stonith_t * st, stonith_event_t * e)
 {
     if (st->state == stonith_disconnected) {
         /* disconnect cib as well and have everything reconnect */
         mon_cib_connection_destroy(NULL);
     } else {
         print_dot();
         kick_refresh(TRUE);
     }
 }
 
 static void
 clean_up_connections(void)
 {
     if (cib != NULL) {
         cib->cmds->signoff(cib);
         cib_delete(cib);
         cib = NULL;
     }
 
     if (st != NULL) {
         if (st->state != stonith_disconnected) {
             st->cmds->remove_notification(st, T_STONITH_NOTIFY_DISCONNECT);
             st->cmds->remove_notification(st, T_STONITH_NOTIFY_FENCE);
             st->cmds->remove_notification(st, T_STONITH_NOTIFY_HISTORY);
             st->cmds->disconnect(st);
         }
         stonith_api_delete(st);
         st = NULL;
     }
 }
 
 /*
  * De-init ncurses, disconnect from the CIB manager, disconnect fencing,
  * deallocate memory and show usage-message if requested.
  *
  * We don't actually return, but nominally returning crm_exit_t allows a usage
  * like "return clean_up(exit_code);" which helps static analysis understand the
  * code flow.
  */
 static crm_exit_t
 clean_up(crm_exit_t exit_code)
 {
 #if CURSES_ENABLED
     if (curses_console_initialized) {
         output_format = mon_output_plain;
         echo();
         nocbreak();
         endwin();
         curses_console_initialized = FALSE;
     }
 #endif
 
     clean_up_connections();
     free(output_filename);
     free(pid_file);
 
     pe_free_working_set(mon_data_set);
     mon_data_set = NULL;
 
     if (exit_code == CRM_EX_USAGE) {
         if (output_format == mon_output_cgi) {
             fprintf(stdout, "Content-Type: text/plain\n"
                             "Status: 500\n\n");
         } else {
-            return crm_help('?', CRM_EX_USAGE);
+            crm_help('?', CRM_EX_USAGE);
         }
     }
     return crm_exit(exit_code);
 }
diff --git a/tools/crm_node.c b/tools/crm_node.c
index 23ce1da332..ffbde6407a 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -1,588 +1,590 @@
 /*
- * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2019 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/types.h>
 
 #include <crm/crm.h>
 #include <crm/common/mainloop.h>
 #include <crm/msg_xml.h>
 #include <crm/cib.h>
 #include <crm/attrd.h>
 
 static int command = 0;
 static char *pid_s = NULL;
 static GMainLoop *mainloop = NULL;
 static crm_exit_t exit_code = CRM_EX_OK;
 
 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, '-', "\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"},
     {"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"},
     {"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, '-', "(the node must already have been removed from the underlying cluster stack configuration)"},
 
     {"-spacer-", 1, 0, '-', "\nAdditional Options:"},
     {"force",	 0, 0, 'f'},
 #if SUPPORT_COROSYNC
     { "corosync",   0, 0, 'C', NULL, pcmk_option_hidden },
 #endif
 
     // @TODO add timeout option for when IPC replies are needed
 
     {0, 0, 0, 0}
 };
 
 /*!
  * \internal
  * \brief Exit crm_node
  * Clean up memory, and either quit mainloop (if running) or exit
  *
  * \param[in] value  Exit status
  */
 static void
 crm_node_exit(crm_exit_t value)
 {
     if (pid_s) {
         free(pid_s);
         pid_s = NULL;
     }
 
     exit_code = value;
 
     if (mainloop && g_main_loop_is_running(mainloop)) {
         g_main_loop_quit(mainloop);
     } else {
         crm_exit(exit_code);
     }
 }
 
 static void
 exit_disconnect(gpointer user_data)
 {
     fprintf(stderr, "error: Lost connection to cluster\n");
     crm_node_exit(CRM_EX_DISCONNECT);
 }
 
 typedef int (*ipc_dispatch_fn) (const char *buffer, ssize_t length,
                                 gpointer userdata);
 
 static crm_ipc_t *
 new_mainloop_for_ipc(const char *system, ipc_dispatch_fn dispatch)
 {
     mainloop_io_t *source = NULL;
     crm_ipc_t *ipc = NULL;
 
     struct ipc_client_callbacks ipc_callbacks = {
         .dispatch = dispatch,
         .destroy = exit_disconnect
     };
 
     mainloop = g_main_loop_new(NULL, FALSE);
     source = mainloop_add_ipc_client(system, G_PRIORITY_DEFAULT, 0,
                                      NULL, &ipc_callbacks);
     ipc = mainloop_get_ipc_client(source);
     if (ipc == NULL) {
         fprintf(stderr,
                 "error: Could not connect to cluster (is it running?)\n");
         crm_node_exit(CRM_EX_DISCONNECT);
     }
     return ipc;
 }
 
 static void
 run_mainloop_and_exit()
 {
     g_main_loop_run(mainloop);
     g_main_loop_unref(mainloop);
     mainloop = NULL;
     crm_node_exit(exit_code);
 }
 
 static int
 send_controller_hello(crm_ipc_t *controller)
 {
     xmlNode *hello = NULL;
     int rc;
 
     pid_s = crm_getpid_s();
     hello = create_hello_message(pid_s, crm_system_name, "1", "0");
     rc = crm_ipc_send(controller, hello, 0, 0, NULL);
     free_xml(hello);
     return (rc < 0)? rc : 0;
 }
 
 static int
 send_node_info_request(crm_ipc_t *controller, uint32_t nodeid)
 {
     xmlNode *ping = NULL;
     int rc;
 
     ping = create_request(CRM_OP_NODE_INFO, NULL, NULL, CRM_SYSTEM_CRMD,
                           crm_system_name, pid_s);
     if (nodeid > 0) {
         crm_xml_add_int(ping, XML_ATTR_ID, nodeid);
     }
     rc = crm_ipc_send(controller, ping, 0, 0, NULL);
     free_xml(ping);
     return (rc < 0)? rc : 0;
 }
 
 static int
 dispatch_controller(const char *buffer, ssize_t length, gpointer userdata)
 {
     xmlNode *message = string2xml(buffer);
     xmlNode *data = NULL;
     const char *value = NULL;
 
     if (message == NULL) {
         fprintf(stderr, "error: Could not understand reply from controller\n");
         crm_node_exit(CRM_EX_PROTOCOL);
         return 0;
     }
     crm_log_xml_trace(message, "controller reply");
 
     exit_code = CRM_EX_PROTOCOL;
 
     // Validate reply
     value = crm_element_value(message, F_CRM_MSG_TYPE);
     if (safe_str_neq(value, XML_ATTR_RESPONSE)) {
         fprintf(stderr, "error: Message from controller was not a reply\n");
         goto done;
     }
     value = crm_element_value(message, XML_ATTR_REFERENCE);
     if (value == NULL) {
         fprintf(stderr, "error: Controller reply did not specify original message\n");
         goto done;
     }
     data = get_message_xml(message, F_CRM_DATA);
     if (data == NULL) {
         fprintf(stderr, "error: Controller reply did not contain any data\n");
         goto done;
     }
 
     switch (command) {
         case 'i':
             value = crm_element_value(data, XML_ATTR_ID);
             if (value == NULL) {
                 fprintf(stderr, "error: Controller reply did not contain node ID\n");
             } else {
                 printf("%s\n", value);
                 exit_code = CRM_EX_OK;
             }
             break;
 
         case 'n':
         case 'N':
             value = crm_element_value(data, XML_ATTR_UNAME);
             if (value == NULL) {
                 fprintf(stderr, "Node is not known to cluster\n");
                 exit_code = CRM_EX_NOHOST;
             } else {
                 printf("%s\n", value);
                 exit_code = CRM_EX_OK;
             }
             break;
 
         case 'q':
             value = crm_element_value(data, XML_ATTR_HAVE_QUORUM);
             if (value == NULL) {
                 fprintf(stderr, "error: Controller reply did not contain quorum status\n");
             } else {
                 bool quorum = crm_is_true(value);
 
                 printf("%d\n", quorum);
                 exit_code = quorum? CRM_EX_OK : CRM_EX_QUORUM;
             }
             break;
 
         default:
             fprintf(stderr, "internal error: Controller reply not expected\n");
             exit_code = CRM_EX_SOFTWARE;
             break;
     }
 
 done:
     free_xml(message);
     crm_node_exit(exit_code);
     return 0;
 }
 
 static void
 run_controller_mainloop(uint32_t nodeid)
 {
     crm_ipc_t *controller = NULL;
     int rc;
 
     controller = new_mainloop_for_ipc(CRM_SYSTEM_CRMD, dispatch_controller);
 
     rc = send_controller_hello(controller);
     if (rc < 0) {
         fprintf(stderr, "error: Could not register with controller: %s\n",
                 pcmk_strerror(rc));
         crm_node_exit(crm_errno2exit(rc));
     }
 
     rc = send_node_info_request(controller, nodeid);
     if (rc < 0) {
         fprintf(stderr, "error: Could not ping controller: %s\n",
                 pcmk_strerror(rc));
         crm_node_exit(crm_errno2exit(rc));
     }
 
     // Run main loop to get controller reply via dispatch_controller()
     run_mainloop_and_exit();
 }
 
 static void
 print_node_name()
 {
     // Check environment first (i.e. when called by resource agent)
     const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
 
     if (name != NULL) {
         printf("%s\n", name);
         crm_node_exit(CRM_EX_OK);
 
     } else {
         // Otherwise ask the controller
         run_controller_mainloop(0);
     }
 }
 
 static int
 cib_remove_node(long 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 > 0) {
         crm_xml_set_id(node, "%ld", id);
         crm_xml_add(node_state, XML_ATTR_ID, ID(node));
     }
 
     cib = cib_new();
     cib->cmds->signon(cib, crm_system_name, cib_command);
 
     rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s",
                 name, id, pcmk_strerror(rc));
     }
     rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s",
                 name, id, pcmk_strerror(rc));
     }
 
     cib->cmds->signoff(cib);
     cib_delete(cib);
     return rc;
 }
 
 static int
 tools_remove_node_cache(const char *node_name, long nodeid, const char *target)
 {
     int rc = -1;
     crm_ipc_t *conn = crm_ipc_new(target, 0);
     xmlNode *cmd = 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)) {
         // The controller requires a hello message before sending a request
         rc = send_controller_hello(conn);
         if (rc < 0) {
             fprintf(stderr, "error: Could not register with controller: %s\n",
                     pcmk_strerror(rc));
             return rc;
         }
     }
 
     crm_trace("Removing %s[%ld] from the %s membership cache",
               node_name, nodeid, target);
 
     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, node_name);
 
         if (nodeid > 0) {
             crm_xml_add_int(cmd, F_ATTRD_HOST_ID, (int) nodeid);
         }
 
     } else {
         cmd = create_request(CRM_OP_RM_NODE_CACHE,
                              NULL, NULL, target, crm_system_name, pid_s);
         if (nodeid > 0) {
             crm_xml_set_id(cmd, "%ld", nodeid);
         }
         crm_xml_add(cmd, XML_ATTR_UNAME, node_name);
     }
 
     rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
     crm_debug("%s peer cache cleanup for %s (%ld): %d",
               target, node_name, nodeid, rc);
 
     if (rc > 0) {
         rc = cib_remove_node(nodeid, node_name);
     }
 
     if (conn) {
         crm_ipc_close(conn);
         crm_ipc_destroy(conn);
     }
     free_xml(cmd);
     return rc > 0 ? 0 : rc;
 }
 
 static void
 remove_node(const char *target_uname)
 {
     int d = 0;
     long nodeid = 0;
     const char *node_name = NULL;
     char *endptr = NULL;
     const char *daemons[] = {
         CRM_SYSTEM_CRMD,
         "stonith-ng",
         T_ATTRD,
         CRM_SYSTEM_MCP,
     };
 
     // Check whether node was specified by name or numeric ID
     errno = 0;
     nodeid = strtol(target_uname, &endptr, 10);
     if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0')
         || (nodeid <= 0)) {
         // It's not a positive integer, so assume it's a node name
         nodeid = 0;
         node_name = target_uname;
     }
 
     for (d = 0; d < DIMOF(daemons); d++) {
         if (tools_remove_node_cache(node_name, nodeid, daemons[d])) {
             crm_err("Failed to connect to %s to remove node '%s'",
                     daemons[d], target_uname);
             crm_node_exit(CRM_EX_ERROR);
             return;
         }
     }
     crm_node_exit(CRM_EX_OK);
 }
 
 static gint
 compare_node_xml(gconstpointer a, gconstpointer b)
 {
     const char *a_name = crm_element_value((xmlNode*) a, "uname");
     const char *b_name = crm_element_value((xmlNode*) b, "uname");
 
     return strcmp((a_name? a_name : ""), (b_name? b_name : ""));
 }
 
 static int
 node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
 {
     GList *nodes = NULL;
     xmlNode *node = NULL;
     xmlNode *msg = string2xml(buffer);
     const char *uname;
     const char *state;
 
     if (msg == NULL) {
         fprintf(stderr, "error: Could not understand pacemakerd response\n");
         crm_node_exit(CRM_EX_PROTOCOL);
         return 0;
     }
 
     crm_log_xml_trace(msg, "message");
 
     for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
         nodes = g_list_insert_sorted(nodes, node, compare_node_xml);
     }
 
     for (GList *iter = nodes; iter; iter = iter->next) {
         node = (xmlNode*) iter->data;
         uname = crm_element_value(node, "uname");
         state = crm_element_value(node, "state");
 
         if (command == 'l') {
             int id = 0;
 
             crm_element_value_int(node, "id", &id);
             printf("%d %s %s\n", id, (uname? uname : ""), (state? state : ""));
 
         // This is CRM_NODE_MEMBER but we don't want to include cluster header
         } else if ((command == 'p') && safe_str_eq(state, "member")) {
             printf("%s ", (uname? uname : ""));
         }
     }
     if (command == 'p') {
         fprintf(stdout, "\n");
     }
 
     free_xml(msg);
     crm_node_exit(CRM_EX_OK);
     return 0;
 }
 
 static void
 run_pacemakerd_mainloop()
 {
     crm_ipc_t *ipc = NULL;
     xmlNode *poke = NULL;
 
     ipc = new_mainloop_for_ipc(CRM_SYSTEM_MCP, node_mcp_dispatch);
 
     // Sending anything will get us a list of nodes
     poke = create_xml_node(NULL, "poke");
     crm_ipc_send(ipc, poke, 0, 0, NULL);
     free_xml(poke);
 
     // Handle reply via node_mcp_dispatch()
     run_mainloop_and_exit();
 }
 
 int
 main(int argc, char **argv)
 {
     int flag = 0;
     int argerr = 0;
     uint32_t nodeid = 0;
     gboolean force_flag = FALSE;
     gboolean dangerous_cmd = FALSE;
     int option_index = 0;
     const char *target_uname = NULL;
 
     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 '?':
-                return crm_help(flag, CRM_EX_OK);
+                crm_help(flag, CRM_EX_OK);
                 break;
             case 'Q':
                 // currently unused
                 break;
             case 'C':
                 // unused and deprecated
                 break;
             case 'f':
                 force_flag = TRUE;
                 break;
             case 'R':
                 command = flag;
                 dangerous_cmd = TRUE;
                 target_uname = optarg;
                 if (optarg == NULL) {
                     ++argerr;
                 }
                 break;
             case 'N':
                 command = flag;
                 nodeid = crm_parse_int(optarg, NULL);
                 break;
             case 'p':
             case 'q':
             case 'i':
             case 'l':
             case 'n':
                 command = flag;
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
-        return crm_help('?', CRM_EX_USAGE);
+        crm_help('?', CRM_EX_USAGE);
     }
 
     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");
         crm_node_exit(CRM_EX_USAGE);
     }
 
     switch (command) {
         case 'n':
             print_node_name();
             break;
         case 'R':
             remove_node(target_uname);
             break;
         case 'i':
         case 'q':
         case 'N':
             run_controller_mainloop(nodeid);
             break;
         case 'l':
         case 'p':
             run_pacemakerd_mainloop();
             break;
         default:
             break;
     }
 
     fprintf(stderr, "error: Must specify a command option\n");
     crm_node_exit(CRM_EX_USAGE);
     return CRM_EX_USAGE;
 }
diff --git a/tools/iso8601.c b/tools/iso8601.c
index 36f28d5adc..e885cb65b9 100644
--- a/tools/iso8601.c
+++ b/tools/iso8601.c
@@ -1,225 +1,225 @@
 /*
  * Copyright 2005-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/util.h>  /* CRM_ASSERT */
 #include <unistd.h>
 
 char command = 0;
 
 /* *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"},
 
     {"-spacer-",    0, 0, '-', "\nCommands:"},
     {"now",      0, 0, 'n', "\tDisplay the current date/time"},
     {"date",     1, 0, 'd', "Parse an ISO8601 date/time.  Eg. '2005-01-20 00:30:00 +01:00' or '2005-040'"},
     { "period",   1, 0, 'p',
       "Parse an ISO8601 period (interval) with start time (for example, '2005-040/2005-043')"
     },
     { "duration", 1, 0, 'D',
       "Parse an ISO8601 duration with start time (for example, '2005-040/P1M')"
     },
     { "expected", 1, 0, 'E',
       "Parse an ISO8601 duration with start time (for example, '2005-040/P1M')"
     },
     {"-spacer-",0, 0, '-', "\nOutput Modifiers:"},
     {"seconds", 0, 0, 's', "\tShow result as a seconds since 0000-001 00:00:00Z"},
     {"epoch", 0, 0, 'S', "\tShow result as a seconds since EPOCH (1970-001 00:00:00Z)"},
     {"local",   0, 0, 'L', "\tShow result as a 'local' date/time"},
     {"ordinal", 0, 0, 'O', "\tShow result as an 'ordinal' date/time"},
     {"week",    0, 0, 'W', "\tShow result as an 'calendar week' date/time"},
     {"-spacer-",0, 0, '-', "\nFor more information on the ISO8601 standard, see https://en.wikipedia.org/wiki/ISO_8601"},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 static void
 log_time_period(int log_level, crm_time_period_t * dtp, int flags)
 {
     char *start = crm_time_as_string(dtp->start, flags);
     char *end = crm_time_as_string(dtp->end, flags);
     CRM_ASSERT(start != NULL && end != NULL);
 
     if (log_level < LOG_CRIT) {
         printf("Period: %s to %s\n", start, end);
     } else {
         do_crm_log(log_level, "Period: %s to %s", start, end);
     }
     free(start);
     free(end);
 }
 
 int
 main(int argc, char **argv)
 {
     crm_exit_t exit_code = CRM_EX_OK;
     int argerr = 0;
     int flag;
     int index = 0;
     int print_options = 0;
     crm_time_t *duration = NULL;
     crm_time_t *date_time = NULL;
     crm_time_period_t *period = NULL;
 
     const char *period_s = NULL;
     const char *duration_s = NULL;
     const char *date_time_s = NULL;
     const char *expected_s = NULL;
 
     crm_log_cli_init("iso8601");
     crm_set_options(NULL, "command [output modifier] ", long_options,
                     "Display and parse ISO8601 dates and times");
 
     if (argc < 2) {
         argerr++;
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '?':
             case '$':
-                return crm_help(flag, CRM_EX_OK);
+                crm_help(flag, CRM_EX_OK);
                 break;
             case 'n':
                 date_time_s = "now";
                 break;
             case 'd':
                 date_time_s = optarg;
                 break;
             case 'p':
                 period_s = optarg;
                 break;
             case 'D':
                 duration_s = optarg;
                 break;
             case 'E':
                 expected_s = optarg;
                 break;
             case 'S':
                 print_options |= crm_time_epoch;
                 break;
             case 's':
                 print_options |= crm_time_seconds;
                 break;
             case 'W':
                 print_options |= crm_time_weeks;
                 break;
             case 'O':
                 print_options |= crm_time_ordinal;
                 break;
             case 'L':
                 print_options |= crm_time_log_with_timezone;
                 break;
                 break;
         }
     }
 
     if (safe_str_eq("now", date_time_s)) {
         date_time = crm_time_new(NULL);
 
         if (date_time == NULL) {
             fprintf(stderr, "Internal error: couldn't determine 'now'!\n");
             crm_exit(CRM_EX_SOFTWARE);
         }
         crm_time_log(LOG_TRACE, "Current date/time", date_time,
                      crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
         crm_time_log(-1, "Current date/time", date_time,
                      print_options | crm_time_log_date | crm_time_log_timeofday);
 
     } else if (date_time_s) {
         date_time = crm_time_new(date_time_s);
 
         if (date_time == NULL) {
             fprintf(stderr, "Invalid date/time specified: %s\n", optarg);
-            return crm_help('?', CRM_EX_USAGE);
+            crm_help('?', CRM_EX_USAGE);
         }
         crm_time_log(LOG_TRACE, "Date", date_time,
                      crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
         crm_time_log(-1, "Date", date_time,
                      print_options | crm_time_log_date | crm_time_log_timeofday);
     }
 
     if (duration_s) {
         duration = crm_time_parse_duration(duration_s);
 
         if (duration == NULL) {
             fprintf(stderr, "Invalid duration specified: %s\n", duration_s);
-            return crm_help('?', CRM_EX_USAGE);
+            crm_help('?', CRM_EX_USAGE);
         }
         crm_time_log(LOG_TRACE, "Duration", duration, crm_time_log_duration);
         crm_time_log(-1, "Duration", duration, print_options | crm_time_log_duration);
     }
 
     if (period_s) {
         period = crm_time_parse_period(period_s);
 
         if (period == NULL) {
             fprintf(stderr, "Invalid interval specified: %s\n", optarg);
-            return crm_help('?', CRM_EX_USAGE);
+            crm_help('?', CRM_EX_USAGE);
         }
         log_time_period(LOG_TRACE, period,
                         print_options | crm_time_log_date | crm_time_log_timeofday);
         log_time_period(-1, period,
                         print_options | crm_time_log_date | crm_time_log_timeofday);
     }
 
     if (date_time && duration) {
         crm_time_t *later = crm_time_add(date_time, duration);
 
         crm_time_log(LOG_TRACE, "Duration ends at", later,
                      crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
         crm_time_log(-1, "Duration ends at", later,
                      print_options | crm_time_log_date | crm_time_log_timeofday |
                      crm_time_log_with_timezone);
         if (expected_s) {
             char *dt_s = crm_time_as_string(later,
                                             print_options | crm_time_log_date |
                                             crm_time_log_timeofday);
             if (safe_str_neq(expected_s, dt_s)) {
                 exit_code = CRM_EX_ERROR;
             }
             free(dt_s);
         }
         crm_time_free(later);
 
     } else if (date_time && expected_s) {
         char *dt_s = crm_time_as_string(date_time,
                                         print_options | crm_time_log_date | crm_time_log_timeofday);
 
         if (safe_str_neq(expected_s, dt_s)) {
             exit_code = CRM_EX_ERROR;
         }
         free(dt_s);
     }
 
     crm_time_free(date_time);
     crm_time_free(duration);
     if (period) {
         crm_time_free(period->start);
         crm_time_free(period->end);
         crm_time_free(period->diff);
         free(period);
     }
 
     return crm_exit(exit_code);
 }