945d2e
From e01a1178fd8f5b99895683b3af9998e9485d12a4 Mon Sep 17 00:00:00 2001
945d2e
From: Ken Gaillot <kgaillot@redhat.com>
945d2e
Date: Fri, 24 Apr 2020 16:42:02 -0500
945d2e
Subject: [PATCH 1/3] Feature: controller: add new IPC API command for getting
945d2e
 node list
945d2e
945d2e
This is based on and will replace the corresponding functionality in
945d2e
pacemakerd.
945d2e
---
945d2e
 daemons/controld/controld_messages.c | 44 ++++++++++++++++++++++++++++--
945d2e
 include/crm/common/ipc_controld.h    | 13 +++++++++
945d2e
 include/crm_internal.h               |  1 +
945d2e
 lib/common/ipc_controld.c            | 53 ++++++++++++++++++++++++++++++++++++
945d2e
 4 files changed, 109 insertions(+), 2 deletions(-)
945d2e
945d2e
diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c
945d2e
index 0be04d0..423f006 100644
945d2e
--- a/daemons/controld/controld_messages.c
945d2e
+++ b/daemons/controld/controld_messages.c
945d2e
@@ -375,10 +375,11 @@ relay_message(xmlNode * msg, gboolean originated_locally)
945d2e
             is_local = 0;
945d2e
 
945d2e
         } else if (is_for_crm) {
945d2e
-            if (safe_str_eq(task, CRM_OP_NODE_INFO)) {
945d2e
+            if (safe_str_eq(task, CRM_OP_NODE_INFO)
945d2e
+                || safe_str_eq(task, PCMK__CONTROLD_CMD_NODES)) {
945d2e
                 /* Node info requests do not specify a host, which is normally
945d2e
                  * treated as "all hosts", because the whole point is that the
945d2e
-                 * client doesn't know the local node name. Always handle these
945d2e
+                 * client may not know the local node name. Always handle these
945d2e
                  * requests locally.
945d2e
                  */
945d2e
                 is_local = 1;
945d2e
@@ -784,6 +785,42 @@ handle_ping(xmlNode *msg)
945d2e
 }
945d2e
 
945d2e
 /*!
945d2e
+ * \brief Handle a PCMK__CONTROLD_CMD_NODES message
945d2e
+ *
945d2e
+ * \return Next FSA input
945d2e
+ */
945d2e
+static enum crmd_fsa_input
945d2e
+handle_node_list(xmlNode *request)
945d2e
+{
945d2e
+    GHashTableIter iter;
945d2e
+    crm_node_t *node = NULL;
945d2e
+    xmlNode *reply = NULL;
945d2e
+    xmlNode *reply_data = NULL;
945d2e
+
945d2e
+    // Create message data for reply
945d2e
+    reply_data = create_xml_node(NULL, XML_CIB_TAG_NODES);
945d2e
+    g_hash_table_iter_init(&iter, crm_peer_cache);
945d2e
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
945d2e
+        xmlNode *xml = create_xml_node(reply_data, XML_CIB_TAG_NODE);
945d2e
+
945d2e
+        crm_xml_add_ll(xml, XML_ATTR_ID, (long long) node->id); // uint32_t
945d2e
+        crm_xml_add(xml, XML_ATTR_UNAME, node->uname);
945d2e
+        crm_xml_add(xml, XML_NODE_IN_CLUSTER, node->state);
945d2e
+    }
945d2e
+
945d2e
+    // Create and send reply
945d2e
+    reply = create_reply(request, reply_data);
945d2e
+    free_xml(reply_data);
945d2e
+    if (reply) {
945d2e
+        (void) relay_message(reply, TRUE);
945d2e
+        free_xml(reply);
945d2e
+    }
945d2e
+
945d2e
+    // Nothing further to do
945d2e
+    return I_NULL;
945d2e
+}
945d2e
+
945d2e
+/*!
945d2e
  * \brief Handle a CRM_OP_NODE_INFO request
945d2e
  *
945d2e
  * \param[in] msg  Message XML
945d2e
@@ -1080,6 +1117,9 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
945d2e
 
945d2e
         remote_ra_process_maintenance_nodes(xml);
945d2e
 
945d2e
+    } else if (strcmp(op, PCMK__CONTROLD_CMD_NODES) == 0) {
945d2e
+        return handle_node_list(stored_msg);
945d2e
+
945d2e
         /*========== (NOT_DC)-Only Actions ==========*/
945d2e
     } else if (!AM_I_DC) {
945d2e
 
945d2e
diff --git a/include/crm/common/ipc_controld.h b/include/crm/common/ipc_controld.h
945d2e
index 0ebabfc..b817357 100644
945d2e
--- a/include/crm/common/ipc_controld.h
945d2e
+++ b/include/crm/common/ipc_controld.h
945d2e
@@ -22,6 +22,7 @@ extern "C" {
945d2e
  */
945d2e
 
945d2e
 #include <stdbool.h>                    // bool
945d2e
+#include <glib.h>                       // GList
945d2e
 #include <libxml/tree.h>                // xmlNode
945d2e
 #include <crm/common/ipc.h>             // pcmk_ipc_api_t
945d2e
 
945d2e
@@ -32,8 +33,16 @@ enum pcmk_controld_api_reply {
945d2e
     pcmk_controld_reply_info,
945d2e
     pcmk_controld_reply_resource,
945d2e
     pcmk_controld_reply_ping,
945d2e
+    pcmk_controld_reply_nodes,
945d2e
 };
945d2e
 
945d2e
+// Node information passed with pcmk_controld_reply_nodes
945d2e
+typedef struct {
945d2e
+    uint32_t id;
945d2e
+    const char *uname;
945d2e
+    const char *state;
945d2e
+} pcmk_controld_api_node_t;
945d2e
+
945d2e
 /*!
945d2e
  * Controller reply passed to event callback
945d2e
  *
945d2e
@@ -72,6 +81,9 @@ typedef struct {
945d2e
             const char *fsa_state;
945d2e
             const char *result;
945d2e
         } ping;
945d2e
+
945d2e
+        // pcmk_controld_reply_nodes
945d2e
+        GList *nodes; // list of pcmk_controld_api_node_t *
945d2e
     } data;
945d2e
 } pcmk_controld_api_reply_t;
945d2e
 
945d2e
@@ -88,6 +100,7 @@ int pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node,
945d2e
                               const char *provider, const char *type,
945d2e
                               bool cib_only);
945d2e
 int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name);
945d2e
+int pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api);
945d2e
 int pcmk_controld_api_shutdown(pcmk_ipc_api_t *api, const char *node_name);
945d2e
 int pcmk_controld_api_start_election(pcmk_ipc_api_t *api);
945d2e
 unsigned int pcmk_controld_api_replies_expected(pcmk_ipc_api_t *api);
945d2e
diff --git a/include/crm_internal.h b/include/crm_internal.h
945d2e
index fd56fc6..cf8999f 100644
945d2e
--- a/include/crm_internal.h
945d2e
+++ b/include/crm_internal.h
945d2e
@@ -122,6 +122,7 @@ pid_t pcmk_locate_sbd(void);
945d2e
 #define PCMK__ATTRD_CMD_SYNC_RESPONSE   "sync-response"
945d2e
 #define PCMK__ATTRD_CMD_CLEAR_FAILURE   "clear-failure"
945d2e
 
945d2e
+#define PCMK__CONTROLD_CMD_NODES        "list-nodes"
945d2e
 
945d2e
 /*
945d2e
  * Environment variables used by Pacemaker
945d2e
diff --git a/lib/common/ipc_controld.c b/lib/common/ipc_controld.c
945d2e
index 22bb733..a733dd3 100644
945d2e
--- a/lib/common/ipc_controld.c
945d2e
+++ b/lib/common/ipc_controld.c
945d2e
@@ -120,6 +120,28 @@ set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
945d2e
     data->data.ping.result = crm_element_value(msg_data, XML_PING_ATTR_STATUS);
945d2e
 }
945d2e
 
945d2e
+static void
945d2e
+set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
945d2e
+{
945d2e
+    pcmk_controld_api_node_t *node_info;
945d2e
+
945d2e
+    data->reply_type = pcmk_controld_reply_nodes;
945d2e
+    for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
945d2e
+         node != NULL; node = crm_next_same_xml(node)) {
945d2e
+
945d2e
+        long long id_ll = 0;
945d2e
+
945d2e
+        node_info = calloc(1, sizeof(pcmk_controld_api_node_t));
945d2e
+        crm_element_value_ll(node, XML_ATTR_ID, &id_ll);
945d2e
+        if (id_ll > 0) {
945d2e
+            node_info->id = id_ll;
945d2e
+        }
945d2e
+        node_info->uname = crm_element_value(node, XML_ATTR_UNAME);
945d2e
+        node_info->state = crm_element_value(node, XML_NODE_IN_CLUSTER);
945d2e
+        data->data.nodes = g_list_prepend(data->data.nodes, node_info);
945d2e
+    }
945d2e
+}
945d2e
+
945d2e
 static bool
945d2e
 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
945d2e
 {
945d2e
@@ -201,6 +223,9 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
945d2e
         } else if (!strcmp(value, CRM_OP_PING)) {
945d2e
             set_ping_data(&reply_data, msg_data);
945d2e
 
945d2e
+        } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) {
945d2e
+            set_nodes_data(&reply_data, msg_data);
945d2e
+
945d2e
         } else {
945d2e
             crm_debug("Unrecognizable controller message: unknown command '%s'",
945d2e
                       value);
945d2e
@@ -210,6 +235,11 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
945d2e
     }
945d2e
 
945d2e
     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
945d2e
+
945d2e
+    // Free any reply data that was allocated
945d2e
+    if (safe_str_eq(value, PCMK__CONTROLD_CMD_NODES)) {
945d2e
+        g_list_free_full(reply_data.data.nodes, free);
945d2e
+    }
945d2e
 }
945d2e
 
945d2e
 pcmk__ipc_methods_t *
945d2e
@@ -376,6 +406,29 @@ pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
945d2e
 }
945d2e
 
945d2e
 /*!
945d2e
+ * \brief Ask the controller for cluster information
945d2e
+ *
945d2e
+ * \param[in] api          Controller connection
945d2e
+ *
945d2e
+ * \return Standard Pacemaker return code
945d2e
+ * \note Event callback will get a reply of type pcmk_controld_reply_nodes.
945d2e
+ */
945d2e
+int
945d2e
+pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api)
945d2e
+{
945d2e
+    xmlNode *request;
945d2e
+    int rc = EINVAL;
945d2e
+
945d2e
+    request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL,
945d2e
+                                        NULL);
945d2e
+    if (request != NULL) {
945d2e
+        rc = send_controller_request(api, request, true);
945d2e
+        free_xml(request);
945d2e
+    }
945d2e
+    return rc;
945d2e
+}
945d2e
+
945d2e
+/*!
945d2e
  * \internal
945d2e
  * \brief Ask the controller to shut down
945d2e
  *
945d2e
-- 
945d2e
1.8.3.1
945d2e
945d2e
945d2e
From 74e2d8d18bf534c1ec6f0e0f44a90772d393a553 Mon Sep 17 00:00:00 2001
945d2e
From: Ken Gaillot <kgaillot@redhat.com>
945d2e
Date: Thu, 2 Jul 2020 11:51:56 -0500
945d2e
Subject: [PATCH 2/3] Refactor: functionize numeric comparisons of strings
945d2e
945d2e
This moves the guts of sort_node_uname() from libpe_status to a new function,
945d2e
pcmk_numeric_strcasecmp(), in libcrmcommon, so it can be used with strings and
945d2e
not just pe_node_t objects.
945d2e
---
945d2e
 include/crm/common/util.h |  1 +
945d2e
 lib/common/strings.c      | 65 +++++++++++++++++++++++++++++++++++++++++++++++
945d2e
 lib/pengine/utils.c       | 50 ++----------------------------------
945d2e
 3 files changed, 68 insertions(+), 48 deletions(-)
945d2e
945d2e
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
945d2e
index 67d74d2..bb97b0a 100644
945d2e
--- a/include/crm/common/util.h
945d2e
+++ b/include/crm/common/util.h
945d2e
@@ -59,6 +59,7 @@ gboolean crm_strcase_equal(gconstpointer a, gconstpointer b);
945d2e
 char *crm_strdup_printf(char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
945d2e
 int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end);
945d2e
 gboolean pcmk__str_in_list(GList *lst, const gchar *s);
945d2e
+int pcmk_numeric_strcasecmp(const char *s1, const char *s2);
945d2e
 
945d2e
 #  define safe_str_eq(a, b) crm_str_eq(a, b, FALSE)
945d2e
 #  define crm_str_hash g_str_hash_traditional
945d2e
diff --git a/lib/common/strings.c b/lib/common/strings.c
945d2e
index 4562738..bd68ccf 100644
945d2e
--- a/lib/common/strings.c
945d2e
+++ b/lib/common/strings.c
945d2e
@@ -16,6 +16,7 @@
945d2e
 #include <stdio.h>
945d2e
 #include <string.h>
945d2e
 #include <stdlib.h>
945d2e
+#include <ctype.h>
945d2e
 #include <limits.h>
945d2e
 #include <bzlib.h>
945d2e
 #include <sys/types.h>
945d2e
@@ -715,3 +716,67 @@ pcmk__str_none_of(const char *s, ...)
945d2e
 
945d2e
     return g_list_find_custom(lst, s, (GCompareFunc) strcmp) != NULL;
945d2e
 }
945d2e
+
945d2e
+/*
945d2e
+ * \brief Sort strings, with numeric portions sorted numerically
945d2e
+ *
945d2e
+ * Sort two strings case-insensitively like strcasecmp(), but with any numeric
945d2e
+ * portions of the string sorted numerically. This is particularly useful for
945d2e
+ * node names (for example, "node10" will sort higher than "node9" but lower
945d2e
+ * than "remotenode9").
945d2e
+ *
945d2e
+ * \param[in] s1  First string to compare (must not be NULL)
945d2e
+ * \param[in] s2  Second string to compare (must not be NULL)
945d2e
+ *
945d2e
+ * \retval -1 \p s1 comes before \p s2
945d2e
+ * \retval  0 \p s1 and \p s2 are equal
945d2e
+ * \retval  1 \p s1 comes after \p s2
945d2e
+ */
945d2e
+int
945d2e
+pcmk_numeric_strcasecmp(const char *s1, const char *s2)
945d2e
+{
945d2e
+    while (*s1 && *s2) {
945d2e
+        if (isdigit(*s1) && isdigit(*s2)) {
945d2e
+            // If node names contain a number, sort numerically
945d2e
+
945d2e
+            char *end1 = NULL;
945d2e
+            char *end2 = NULL;
945d2e
+            long num1 = strtol(s1, &end1, 10);
945d2e
+            long num2 = strtol(s2, &end2, 10);
945d2e
+
945d2e
+            // allow ordering e.g. 007 > 7
945d2e
+            size_t len1 = end1 - s1;
945d2e
+            size_t len2 = end2 - s2;
945d2e
+
945d2e
+            if (num1 < num2) {
945d2e
+                return -1;
945d2e
+            } else if (num1 > num2) {
945d2e
+                return 1;
945d2e
+            } else if (len1 < len2) {
945d2e
+                return -1;
945d2e
+            } else if (len1 > len2) {
945d2e
+                return 1;
945d2e
+            }
945d2e
+            s1 = end1;
945d2e
+            s2 = end2;
945d2e
+        } else {
945d2e
+            // Compare non-digits case-insensitively
945d2e
+            int lower1 = tolower(*s1);
945d2e
+            int lower2 = tolower(*s2);
945d2e
+
945d2e
+            if (lower1 < lower2) {
945d2e
+                return -1;
945d2e
+            } else if (lower1 > lower2) {
945d2e
+                return 1;
945d2e
+            }
945d2e
+            ++s1;
945d2e
+            ++s2;
945d2e
+        }
945d2e
+    }
945d2e
+    if (!*s1 && *s2) {
945d2e
+        return -1;
945d2e
+    } else if (*s1 && !*s2) {
945d2e
+        return 1;
945d2e
+    }
945d2e
+    return 0;
945d2e
+}
945d2e
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
945d2e
index ce3c260..584def4 100644
945d2e
--- a/lib/pengine/utils.c
945d2e
+++ b/lib/pengine/utils.c
945d2e
@@ -13,7 +13,6 @@
945d2e
 #include <crm/common/xml.h>
945d2e
 #include <crm/common/util.h>
945d2e
 
945d2e
-#include <ctype.h>
945d2e
 #include <glib.h>
945d2e
 #include <stdbool.h>
945d2e
 
945d2e
@@ -214,53 +213,8 @@ pe__node_list2table(GList *list)
945d2e
 gint
945d2e
 sort_node_uname(gconstpointer a, gconstpointer b)
945d2e
 {
945d2e
-    const char *name_a = ((const pe_node_t *) a)->details->uname;
945d2e
-    const char *name_b = ((const pe_node_t *) b)->details->uname;
945d2e
-
945d2e
-    while (*name_a && *name_b) {
945d2e
-        if (isdigit(*name_a) && isdigit(*name_b)) {
945d2e
-            // If node names contain a number, sort numerically
945d2e
-
945d2e
-            char *end_a = NULL;
945d2e
-            char *end_b = NULL;
945d2e
-            long num_a = strtol(name_a, &end_a, 10);
945d2e
-            long num_b = strtol(name_b, &end_b, 10);
945d2e
-
945d2e
-            // allow ordering e.g. 007 > 7
945d2e
-            size_t len_a = end_a - name_a;
945d2e
-            size_t len_b = end_b - name_b;
945d2e
-
945d2e
-            if (num_a < num_b) {
945d2e
-                return -1;
945d2e
-            } else if (num_a > num_b) {
945d2e
-                return 1;
945d2e
-            } else if (len_a < len_b) {
945d2e
-                return -1;
945d2e
-            } else if (len_a > len_b) {
945d2e
-                return 1;
945d2e
-            }
945d2e
-            name_a = end_a;
945d2e
-            name_b = end_b;
945d2e
-        } else {
945d2e
-            // Compare non-digits case-insensitively
945d2e
-            int lower_a = tolower(*name_a);
945d2e
-            int lower_b = tolower(*name_b);
945d2e
-
945d2e
-            if (lower_a < lower_b) {
945d2e
-                return -1;
945d2e
-            } else if (lower_a > lower_b) {
945d2e
-                return 1;
945d2e
-            }
945d2e
-            ++name_a;
945d2e
-            ++name_b;
945d2e
-        }
945d2e
-    }
945d2e
-    if (!*name_a && *name_b) {
945d2e
-        return -1;
945d2e
-    } else if (*name_a && !*name_b) {
945d2e
-        return 1;
945d2e
-    }
945d2e
-    return 0;
945d2e
+    return pcmk_numeric_strcasecmp(((const pe_node_t *) a)->details->uname,
945d2e
+                                   ((const pe_node_t *) b)->details->uname);
945d2e
 }
945d2e
 
945d2e
 /*!
945d2e
-- 
945d2e
1.8.3.1
945d2e
945d2e
945d2e
From 8461509158e06365122dc741c527c83c94e966ce Mon Sep 17 00:00:00 2001
945d2e
From: Ken Gaillot <kgaillot@redhat.com>
945d2e
Date: Fri, 24 Apr 2020 19:35:19 -0500
945d2e
Subject: [PATCH 3/3] Fix: tools: crm_node -l and -p now work from Pacemaker
945d2e
 Remote nodes
945d2e
945d2e
crm_node now asks the controller for the cluster node list, instead of
945d2e
pacemakerd. This allows it to work from Pacemaker Remote nodes, since
945d2e
controller IPC is proxied but pacemakerd IPC is not.
945d2e
---
945d2e
 tools/crm_node.c | 176 +++++++++++++++++++++----------------------------------
945d2e
 1 file changed, 67 insertions(+), 109 deletions(-)
945d2e
945d2e
diff --git a/tools/crm_node.c b/tools/crm_node.c
945d2e
index 57c2ee5..146454d 100644
945d2e
--- a/tools/crm_node.c
945d2e
+++ b/tools/crm_node.c
945d2e
@@ -130,6 +130,16 @@ remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError *
945d2e
     return TRUE;
945d2e
 }
945d2e
 
945d2e
+static gint
945d2e
+sort_node(gconstpointer a, gconstpointer b)
945d2e
+{
945d2e
+    const pcmk_controld_api_node_t *node_a = a;
945d2e
+    const pcmk_controld_api_node_t *node_b = b;
945d2e
+
945d2e
+    return pcmk_numeric_strcasecmp((node_a->uname? node_a->uname : ""),
945d2e
+                                   (node_b->uname? node_b->uname : ""));
945d2e
+}
945d2e
+
945d2e
 static void
945d2e
 controller_event_cb(pcmk_ipc_api_t *controld_api,
945d2e
                     enum pcmk_ipc_event event_type, crm_exit_t status,
945d2e
@@ -157,15 +167,16 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
945d2e
                 crm_exit_str(status));
945d2e
         goto done;
945d2e
     }
945d2e
-    if (reply->reply_type != pcmk_controld_reply_info) {
945d2e
-        fprintf(stderr, "error: Unknown reply type %d from controller\n",
945d2e
-                reply->reply_type);
945d2e
-        goto done;
945d2e
-    }
945d2e
 
945d2e
     // Parse desired info from reply and display to user
945d2e
     switch (options.command) {
945d2e
         case 'i':
945d2e
+            if (reply->reply_type != pcmk_controld_reply_info) {
945d2e
+                fprintf(stderr,
945d2e
+                        "error: Unknown reply type %d from controller\n",
945d2e
+                        reply->reply_type);
945d2e
+                goto done;
945d2e
+            }
945d2e
             if (reply->data.node_info.id == 0) {
945d2e
                 fprintf(stderr,
945d2e
                         "error: Controller reply did not contain node ID\n");
945d2e
@@ -177,6 +188,12 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
945d2e
 
945d2e
         case 'n':
945d2e
         case 'N':
945d2e
+            if (reply->reply_type != pcmk_controld_reply_info) {
945d2e
+                fprintf(stderr,
945d2e
+                        "error: Unknown reply type %d from controller\n",
945d2e
+                        reply->reply_type);
945d2e
+                goto done;
945d2e
+            }
945d2e
             if (reply->data.node_info.uname == NULL) {
945d2e
                 fprintf(stderr, "Node is not known to cluster\n");
945d2e
                 exit_code = CRM_EX_NOHOST;
945d2e
@@ -186,6 +203,12 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
945d2e
             break;
945d2e
 
945d2e
         case 'q':
945d2e
+            if (reply->reply_type != pcmk_controld_reply_info) {
945d2e
+                fprintf(stderr,
945d2e
+                        "error: Unknown reply type %d from controller\n",
945d2e
+                        reply->reply_type);
945d2e
+                goto done;
945d2e
+            }
945d2e
             printf("%d\n", reply->data.node_info.have_quorum);
945d2e
             if (!(reply->data.node_info.have_quorum)) {
945d2e
                 exit_code = CRM_EX_QUORUM;
945d2e
@@ -193,6 +216,36 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
945d2e
             }
945d2e
             break;
945d2e
 
945d2e
+        case 'l':
945d2e
+        case 'p':
945d2e
+            if (reply->reply_type != pcmk_controld_reply_nodes) {
945d2e
+                fprintf(stderr,
945d2e
+                        "error: Unknown reply type %d from controller\n",
945d2e
+                        reply->reply_type);
945d2e
+                goto done;
945d2e
+            }
945d2e
+            reply->data.nodes = g_list_sort(reply->data.nodes, sort_node);
945d2e
+            for (GList *node_iter = reply->data.nodes;
945d2e
+                 node_iter != NULL; node_iter = node_iter->next) {
945d2e
+
945d2e
+                pcmk_controld_api_node_t *node = node_iter->data;
945d2e
+                const char *uname = (node->uname? node->uname : "");
945d2e
+                const char *state = (node->state? node->state : "");
945d2e
+
945d2e
+                if (options.command == 'l') {
945d2e
+                    printf("%lu %s %s\n",
945d2e
+                           (unsigned long) node->id, uname, state);
945d2e
+
945d2e
+                // i.e. CRM_NODE_MEMBER, but we don't want to include cluster.h
945d2e
+                } else if (!strcmp(state, "member")) {
945d2e
+                    printf("%s ", uname);
945d2e
+                }
945d2e
+            }
945d2e
+            if (options.command == 'p') {
945d2e
+                printf("\n");
945d2e
+            }
945d2e
+            break;
945d2e
+
945d2e
         default:
945d2e
             fprintf(stderr, "internal error: Controller reply not expected\n");
945d2e
             exit_code = CRM_EX_SOFTWARE;
945d2e
@@ -207,7 +260,7 @@ done:
945d2e
 }
945d2e
 
945d2e
 static void
945d2e
-run_controller_mainloop(uint32_t nodeid)
945d2e
+run_controller_mainloop(uint32_t nodeid, bool list_nodes)
945d2e
 {
945d2e
     pcmk_ipc_api_t *controld_api = NULL;
945d2e
     int rc;
945d2e
@@ -233,7 +286,11 @@ run_controller_mainloop(uint32_t nodeid)
945d2e
         return;
945d2e
     }
945d2e
 
945d2e
-    rc = pcmk_controld_api_node_info(controld_api, nodeid);
945d2e
+    if (list_nodes) {
945d2e
+        rc = pcmk_controld_api_list_nodes(controld_api);
945d2e
+    } else {
945d2e
+        rc = pcmk_controld_api_node_info(controld_api, nodeid);
945d2e
+    }
945d2e
     if (rc != pcmk_rc_ok) {
945d2e
         fprintf(stderr, "error: Could not ping controller: %s\n",
945d2e
                 pcmk_rc_str(rc));
945d2e
@@ -263,7 +320,7 @@ print_node_name(void)
945d2e
 
945d2e
     } else {
945d2e
         // Otherwise ask the controller
945d2e
-        run_controller_mainloop(0);
945d2e
+        run_controller_mainloop(0, false);
945d2e
     }
945d2e
 }
945d2e
 
945d2e
@@ -444,105 +501,6 @@ remove_node(const char *target_uname)
945d2e
     exit_code = CRM_EX_OK;
945d2e
 }
945d2e
 
945d2e
-static gint
945d2e
-compare_node_xml(gconstpointer a, gconstpointer b)
945d2e
-{
945d2e
-    const char *a_name = crm_element_value((xmlNode*) a, "uname");
945d2e
-    const char *b_name = crm_element_value((xmlNode*) b, "uname");
945d2e
-
945d2e
-    return strcmp((a_name? a_name : ""), (b_name? b_name : ""));
945d2e
-}
945d2e
-
945d2e
-static int
945d2e
-node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
945d2e
-{
945d2e
-    GList *nodes = NULL;
945d2e
-    xmlNode *node = NULL;
945d2e
-    xmlNode *msg = string2xml(buffer);
945d2e
-    const char *uname;
945d2e
-    const char *state;
945d2e
-
945d2e
-    if (msg == NULL) {
945d2e
-        fprintf(stderr, "error: Could not understand pacemakerd response\n");
945d2e
-        exit_code = CRM_EX_PROTOCOL;
945d2e
-        g_main_loop_quit(mainloop);
945d2e
-        return 0;
945d2e
-    }
945d2e
-
945d2e
-    crm_log_xml_trace(msg, "message");
945d2e
-
945d2e
-    for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
945d2e
-        nodes = g_list_insert_sorted(nodes, node, compare_node_xml);
945d2e
-    }
945d2e
-
945d2e
-    for (GList *iter = nodes; iter; iter = iter->next) {
945d2e
-        node = (xmlNode*) iter->data;
945d2e
-        uname = crm_element_value(node, "uname");
945d2e
-        state = crm_element_value(node, "state");
945d2e
-
945d2e
-        if (options.command == 'l') {
945d2e
-            int id = 0;
945d2e
-
945d2e
-            crm_element_value_int(node, "id", &id;;
945d2e
-            printf("%d %s %s\n", id, (uname? uname : ""), (state? state : ""));
945d2e
-
945d2e
-        // This is CRM_NODE_MEMBER but we don't want to include cluster header
945d2e
-        } else if ((options.command == 'p') && safe_str_eq(state, "member")) {
945d2e
-            printf("%s ", (uname? uname : ""));
945d2e
-        }
945d2e
-    }
945d2e
-    if (options.command == 'p') {
945d2e
-        fprintf(stdout, "\n");
945d2e
-    }
945d2e
-
945d2e
-    free_xml(msg);
945d2e
-    exit_code = CRM_EX_OK;
945d2e
-    g_main_loop_quit(mainloop);
945d2e
-    return 0;
945d2e
-}
945d2e
-
945d2e
-static void
945d2e
-lost_pacemakerd(gpointer user_data)
945d2e
-{
945d2e
-    fprintf(stderr, "error: Lost connection to cluster\n");
945d2e
-    exit_code = CRM_EX_DISCONNECT;
945d2e
-    g_main_loop_quit(mainloop);
945d2e
-}
945d2e
-
945d2e
-static void
945d2e
-run_pacemakerd_mainloop(void)
945d2e
-{
945d2e
-    crm_ipc_t *ipc = NULL;
945d2e
-    xmlNode *poke = NULL;
945d2e
-    mainloop_io_t *source = NULL;
945d2e
-
945d2e
-    struct ipc_client_callbacks ipc_callbacks = {
945d2e
-        .dispatch = node_mcp_dispatch,
945d2e
-        .destroy = lost_pacemakerd
945d2e
-    };
945d2e
-
945d2e
-    source = mainloop_add_ipc_client(CRM_SYSTEM_MCP, G_PRIORITY_DEFAULT, 0,
945d2e
-                                     NULL, &ipc_callbacks);
945d2e
-    ipc = mainloop_get_ipc_client(source);
945d2e
-    if (ipc == NULL) {
945d2e
-        fprintf(stderr,
945d2e
-                "error: Could not connect to cluster (is it running?)\n");
945d2e
-        exit_code = CRM_EX_DISCONNECT;
945d2e
-        return;
945d2e
-    }
945d2e
-
945d2e
-    // Sending anything will get us a list of nodes
945d2e
-    poke = create_xml_node(NULL, "poke");
945d2e
-    crm_ipc_send(ipc, poke, 0, 0, NULL);
945d2e
-    free_xml(poke);
945d2e
-
945d2e
-    // Handle reply via node_mcp_dispatch()
945d2e
-    mainloop = g_main_loop_new(NULL, FALSE);
945d2e
-    g_main_loop_run(mainloop);
945d2e
-    g_main_loop_unref(mainloop);
945d2e
-    mainloop = NULL;
945d2e
-}
945d2e
-
945d2e
 static GOptionContext *
945d2e
 build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) {
945d2e
     GOptionContext *context = NULL;
945d2e
@@ -627,11 +585,11 @@ main(int argc, char **argv)
945d2e
         case 'i':
945d2e
         case 'q':
945d2e
         case 'N':
945d2e
-            run_controller_mainloop(options.nodeid);
945d2e
+            run_controller_mainloop(options.nodeid, false);
945d2e
             break;
945d2e
         case 'l':
945d2e
         case 'p':
945d2e
-            run_pacemakerd_mainloop();
945d2e
+            run_controller_mainloop(0, true);
945d2e
             break;
945d2e
         default:
945d2e
             break;
945d2e
-- 
945d2e
1.8.3.1
945d2e