diff --git a/include/pcmki/pcmki_scheduler.h b/include/pcmki/pcmki_scheduler.h
index 1f0729eca6..3f66cea0a8 100644
--- a/include/pcmki/pcmki_scheduler.h
+++ b/include/pcmki/pcmki_scheduler.h
@@ -1,48 +1,47 @@
 /*
  * Copyright 2014-2023 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 PCMK__PCMKI_PCMKI_SCHEDULER__H
-#  define PCMK__PCMKI_PCMKI_SCHEDULER__H
+#define PCMK__PCMKI_PCMKI_SCHEDULER__H
 
-#  include <glib.h>                 // GList
-#  include <stdbool.h>              // bool
-#  include <libxml/tree.h>          // xmlNode
+#include <glib.h>               // GList
+#include <stdbool.h>            // bool
+#include <libxml/tree.h>        // xmlNode
 
-#  include <crm/crm.h>
-#  include <crm/lrmd.h>             // lrmd_event_data_t
-#  include <crm/pengine/status.h>   // pe_resource_t, pe_working_set_t
+#include <crm/lrmd.h>           // lrmd_event_data_t
+#include <crm/pengine/status.h> // pe_resource_t, pe_working_set_t
 
 typedef struct {
     const char *id;
     const char *node_attribute;
     pe_resource_t *dependent;   // The resource being colocated
     pe_resource_t *primary;     // The resource the dependent is colocated with
 
     int dependent_role; // Colocation applies only if dependent has this role
     int primary_role;   // Colocation applies only if primary has this role
 
     int score;
     bool influence; // Whether dependent influences active primary placement
 } pcmk__colocation_t;
 
 void pcmk__unpack_constraints(pe_working_set_t *data_set);
 
 void pcmk__schedule_actions(xmlNode *cib, unsigned long long flags,
                             pe_working_set_t *data_set);
 
 GList *pcmk__with_this_colocations(const pe_resource_t *rsc);
 GList *pcmk__this_with_colocations(const pe_resource_t *rsc);
 
 GList *pcmk__copy_node_list(const GList *list, bool reset);
 
 xmlNode *pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *event,
-                                 const char *caller_version, int target_rc,
-                                 const char *node, const char *origin);
+                                  const char *caller_version, int target_rc,
+                                  const char *node, const char *origin);
 
 #endif
diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c
index 91a14fd1a5..6b8289a638 100644
--- a/lib/pacemaker/pcmk_cluster_queries.c
+++ b/lib/pacemaker/pcmk_cluster_queries.c
@@ -1,897 +1,896 @@
 /*
  * Copyright 2020-2023 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 <glib.h>               // GMainLoop, etc.
 #include <libxml/tree.h>        // xmlNode
 
 #include <pacemaker.h>
 #include <pacemaker-internal.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/cib/internal.h>
 #include <crm/msg_xml.h>
 #include <crm/common/output_internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/ipc_controld.h>
 #include <crm/common/ipc_pacemakerd.h>
 
 //! Object to store node info from the controller API
 typedef struct {
     /* Adapted from pcmk_controld_api_reply_t:data:node_info.
      * (char **) are convenient here for use within callbacks: we can skip
      * copying strings unless the caller passes a non-NULL value.
      */
     uint32_t id;
     char **node_name;
     char **uuid;
     char **state;
     bool have_quorum;
     bool is_remote;
 } node_info_t;
 
 //! Object to store API results, a timeout, and an output object
 typedef struct {
     pcmk__output_t *out;
     bool show_output;
     int rc;
     unsigned int message_timeout_ms;
     enum pcmk_pacemakerd_state pcmkd_state;
     node_info_t node_info;
 } data_t;
 
 /*!
  * \internal
  * \brief Validate that an IPC API event is a good reply
  *
  * \param[in,out] data        API results and options
  * \param[in]     api         IPC API connection
  * \param[in]     event_type  Type of event that occurred
  * \param[in]     status      Event status
  *
  * \return Standard Pacemaker return code
  */
 static int
 validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
                      enum pcmk_ipc_event event_type, crm_exit_t status)
 {
     pcmk__output_t *out = data->out;
 
     switch (event_type) {
         case pcmk_ipc_event_reply:
             break;
 
         case pcmk_ipc_event_disconnect:
             if (data->rc == ECONNRESET) { // Unexpected
                 out->err(out, "error: Lost connection to %s",
                          pcmk_ipc_name(api, true));
             }
             // Nothing bad but not the reply we're looking for
             return ENOTSUP;
 
         default:
             // Ditto
             return ENOTSUP;
     }
 
     if (status != CRM_EX_OK) {
         out->err(out, "error: Bad reply from %s: %s",
                  pcmk_ipc_name(api, true), crm_exit_str(status));
         data->rc = EBADMSG;
         return data->rc;
     }
     return pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Validate that a controller API event is a good reply of expected type
  *
  * \param[in,out] data           API results and options
  * \param[in]     api            Controller connection
  * \param[in]     event_type     Type of event that occurred
  * \param[in]     status         Event status
  * \param[in]     event_data     Event-specific data
  * \param[in]     expected_type  Expected reply type
  *
  * \return Standard Pacemaker return code
  */
 static int
 validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
                         enum pcmk_ipc_event event_type, crm_exit_t status,
                         const void *event_data,
                         enum pcmk_controld_api_reply expected_type)
 {
     pcmk__output_t *out = data->out;
     int rc = pcmk_rc_ok;
     const pcmk_controld_api_reply_t *reply = NULL;
 
     rc = validate_reply_event(data, api, event_type, status);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     reply = (const pcmk_controld_api_reply_t *) event_data;
 
     if (reply->reply_type != expected_type) {
         out->err(out, "error: Unexpected reply type '%s' from controller",
                  pcmk__controld_api_reply2str(reply->reply_type));
         data->rc = EBADMSG;
         return data->rc;
     }
 
     return pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Validate that a \p pacemakerd API event is a good reply of expected
  *        type
  *
  * \param[in,out] data           API results and options
  * \param[in]     api            \p pacemakerd connection
  * \param[in]     event_type     Type of event that occurred
  * \param[in]     status         Event status
  * \param[in]     event_data     Event-specific data
  * \param[in]     expected_type  Expected reply type
  *
  * \return Standard Pacemaker return code
  */
 static int
 validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
                      enum pcmk_ipc_event event_type, crm_exit_t status,
                      const void *event_data,
                      enum pcmk_pacemakerd_api_reply expected_type)
 {
     pcmk__output_t *out = data->out;
     const pcmk_pacemakerd_api_reply_t *reply = NULL;
     int rc = validate_reply_event(data, api, event_type, status);
 
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 
     if (reply->reply_type != expected_type) {
         out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
                  pcmk__pcmkd_api_reply2str(reply->reply_type));
         data->rc = EBADMSG;
         return data->rc;
     }
 
     return pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Process a controller status IPC event
  *
  * \param[in,out] controld_api  Controller connection
  * \param[in]     event_type    Type of event that occurred
  * \param[in]     status        Event status
  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
  *                              event-specific data
  * \param[in,out] user_data     \p data_t object for API results and options
  */
 static void
 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
                            enum pcmk_ipc_event event_type, crm_exit_t status,
                            void *event_data, void *user_data)
 {
     data_t *data = (data_t *) user_data;
     pcmk__output_t *out = data->out;
     const pcmk_controld_api_reply_t *reply = NULL;
 
     int rc = validate_controld_reply(data, controld_api, event_type, status,
                                      event_data, pcmk_controld_reply_ping);
 
     if (rc != pcmk_rc_ok) {
         return;
     }
 
     reply = (const pcmk_controld_api_reply_t *) event_data;
     out->message(out, "health",
                  reply->data.ping.sys_from, reply->host_from,
                  reply->data.ping.fsa_state, reply->data.ping.result);
     data->rc = pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Process a designated controller IPC event
  *
  * \param[in,out] controld_api  Controller connection
  * \param[in]     event_type    Type of event that occurred
  * \param[in]     status        Event status
  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
  *                              event-specific data
  * \param[in,out] user_data     \p data_t object for API results and options
  */
 static void
 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
                                enum pcmk_ipc_event event_type,
                                crm_exit_t status, void *event_data,
                                void *user_data)
 {
     data_t *data = (data_t *) user_data;
     pcmk__output_t *out = data->out;
     const pcmk_controld_api_reply_t *reply = NULL;
 
     int rc = validate_controld_reply(data, controld_api, event_type, status,
                                      event_data, pcmk_controld_reply_ping);
 
     if (rc != pcmk_rc_ok) {
         return;
     }
 
     reply = (const pcmk_controld_api_reply_t *) event_data;
     out->message(out, "dc", reply->host_from);
     data->rc = pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Process a node info IPC event
  *
  * \param[in,out] controld_api  Controller connection
  * \param[in]     event_type    Type of event that occurred
  * \param[in]     status        Event status
  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
  *                              event-specific data
  * \param[in,out] user_data     \p data_t object for API results and options
  */
 static void
 node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
                    crm_exit_t status, void *event_data, void *user_data)
 {
     data_t *data = (data_t *) user_data;
     pcmk__output_t *out = data->out;
 
     const pcmk_controld_api_reply_t *reply = NULL;
 
     int rc = validate_controld_reply(data, controld_api, event_type, status,
                                      event_data, pcmk_controld_reply_info);
 
     if (rc != pcmk_rc_ok) {
         return;
     }
 
     reply = (const pcmk_controld_api_reply_t *) event_data;
 
     if (reply->data.node_info.uname == NULL) {
         out->err(out, "Node is not known to cluster");
         data->rc = pcmk_rc_node_unknown;
         return;
     }
 
     data->node_info.have_quorum = reply->data.node_info.have_quorum;
     data->node_info.is_remote = reply->data.node_info.is_remote;
     data->node_info.id = (uint32_t) reply->data.node_info.id;
 
     pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
     pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
     pcmk__str_update(data->node_info.state, reply->data.node_info.state);
 
     if (data->show_output) {
         out->message(out, "node-info",
                      reply->data.node_info.id, reply->data.node_info.uname,
                      reply->data.node_info.uuid, reply->data.node_info.state,
                      reply->data.node_info.have_quorum,
                      reply->data.node_info.is_remote);
     }
 
     data->rc = pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Process a \p pacemakerd status IPC event
  *
  * \param[in,out] pacemakerd_api  \p pacemakerd connection
  * \param[in]     event_type      Type of event that occurred
  * \param[in]     status          Event status
  * \param[in,out] event_data      \p pcmk_pacemakerd_api_reply_t object
  *                                containing event-specific data
  * \param[in,out] user_data       \p data_t object for API results and options
  */
 static void
 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
                     enum pcmk_ipc_event event_type, crm_exit_t status,
                     void *event_data, void *user_data)
 {
     data_t *data = user_data;
     pcmk__output_t *out = data->out;
     const pcmk_pacemakerd_api_reply_t *reply = NULL;
 
     int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
                                   event_data, pcmk_pacemakerd_reply_ping);
 
     if (rc != pcmk_rc_ok) {
         return;
     }
 
     // Parse desired information from reply
     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
 
     data->pcmkd_state = reply->data.ping.state;
     data->rc = pcmk_rc_ok;
 
     if (!data->show_output) {
         return;
     }
 
     if (reply->data.ping.status == pcmk_rc_ok) {
         out->message(out, "pacemakerd-health",
                      reply->data.ping.sys_from, reply->data.ping.state, NULL,
                      reply->data.ping.last_good);
     } else {
         out->message(out, "pacemakerd-health",
                      reply->data.ping.sys_from, reply->data.ping.state,
                      "query failed", time(NULL));
     }
 }
 
 static pcmk_ipc_api_t *
 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
             enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
 {
     int rc;
     pcmk__output_t *out = data->out;
     pcmk_ipc_api_t *api = NULL;
 
     rc = pcmk_new_ipc_api(&api, server);
     if (api == NULL) {
         out->err(out, "error: Could not connect to %s: %s",
                 pcmk_ipc_name(api, true),
                 pcmk_rc_str(rc));
         data->rc = rc;
         return NULL;
     }
     if (cb != NULL) {
         pcmk_register_ipc_callback(api, cb, data);
     }
 
     rc = pcmk_connect_ipc(api, dispatch_type);
 
     if (rc != pcmk_rc_ok) {
         if (rc == EREMOTEIO) {
             data->pcmkd_state = pcmk_pacemakerd_state_remote;
             if (eremoteio_ok) {
                 /* EREMOTEIO may be expected and acceptable for some callers
                  * on a Pacemaker Remote node
                  */
                 rc = pcmk_rc_ok;
             } else {
                 out->err(out, "error: Could not connect to %s: %s",
                          pcmk_ipc_name(api, true), pcmk_rc_str(rc));
             }
         }
         data->rc = rc;
         pcmk_free_ipc_api(api);
         return NULL;
     }
 
     return api;
 }
 
 /*!
  * \internal
  * \brief Poll an IPC API connection until timeout or a reply is received
  *
  * \param[in,out] data     API results and options
  * \param[in,out] api      IPC API connection
  * \param[in]     on_node  If not \p NULL, name of the node to poll (used only
  *                         for logging)
  *
  * \note Sets the \p rc member of \p data on error
  */
 static void
 poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
 {
     pcmk__output_t *out = data->out;
 
     uint64_t start_nsec = qb_util_nano_current_get();
     uint64_t end_nsec = start_nsec;
     uint64_t elapsed_ms = 0;
     uint64_t remaining_ms = data->message_timeout_ms;
 
     while (remaining_ms > 0) {
         int rc = pcmk_poll_ipc(api, remaining_ms);
 
         if (rc == EAGAIN) {
             // Poll timed out
             break;
         }
 
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Failed to poll %s API%s%s: %s",
                      pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
                      pcmk__s(on_node, ""), pcmk_rc_str(rc));
             data->rc = rc;
             return;
         }
 
         pcmk_dispatch_ipc(api);
 
         if (data->rc != EAGAIN) {
             // Received a reply
             return;
         }
         end_nsec = qb_util_nano_current_get();
         elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
         remaining_ms = data->message_timeout_ms - elapsed_ms;
     }
 
     out->err(out,
              "error: Timed out after %ums waiting for reply from %s API%s%s",
              data->message_timeout_ms, pcmk_ipc_name(api, true),
              (on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
     data->rc = EAGAIN;
 }
 
 /*!
  * \internal
  * \brief Get and output controller status
  *
  * \param[in,out] out                 Output object
  * \param[in]     node_name           Name of node whose status is desired
  *                                    (\p NULL for DC)
  * \param[in]     message_timeout_ms  How long to wait for a reply from the
  *                                    \p pacemaker-controld API. If 0,
  *                                    \p pcmk_ipc_dispatch_sync will be used.
  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
  *                                    be used.
  *
  * \return Standard Pacemaker return code
  */
 int
 pcmk__controller_status(pcmk__output_t *out, const char *node_name,
                         unsigned int message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .rc = EAGAIN,
         .message_timeout_ms = message_timeout_ms,
     };
     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     pcmk_ipc_api_t *controld_api = NULL;
 
     if (message_timeout_ms == 0) {
         dispatch_type = pcmk_ipc_dispatch_sync;
     }
     controld_api = ipc_connect(&data, pcmk_ipc_controld,
                                controller_status_event_cb, dispatch_type,
                                false);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_ping(controld_api, node_name);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Could not ping controller API on %s: %s",
                      pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         if (dispatch_type == pcmk_ipc_dispatch_poll) {
             poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
         }
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
 
 
 // Documented in header
 int
 pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
                        unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__xml_output_new(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__controller_status(out, node_name, message_timeout_ms);
     pcmk__xml_output_finish(out, xml);
     return rc;
 }
 
 /*!
  * \internal
  * \brief Get and output designated controller node name
  *
  * \param[in,out] out                 Output object
  * \param[in]     message_timeout_ms  How long to wait for a reply from the
  *                                    \p pacemaker-controld API. If 0,
  *                                    \p pcmk_ipc_dispatch_sync will be used.
  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
  *                                    be used.
  *
  * \return Standard Pacemaker return code
  */
 int
 pcmk__designated_controller(pcmk__output_t *out,
                             unsigned int message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .rc = EAGAIN,
         .message_timeout_ms = message_timeout_ms,
     };
     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     pcmk_ipc_api_t *controld_api = NULL;
 
     if (message_timeout_ms == 0) {
         dispatch_type = pcmk_ipc_dispatch_sync;
     }
     controld_api = ipc_connect(&data, pcmk_ipc_controld,
                                designated_controller_event_cb, dispatch_type,
                                false);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_ping(controld_api, NULL);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Could not ping controller API on DC: %s",
                      pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         if (dispatch_type == pcmk_ipc_dispatch_poll) {
             poll_until_reply(&data, controld_api, "DC");
         }
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
 
 // Documented in header
 int
 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__xml_output_new(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__designated_controller(out, message_timeout_ms);
     pcmk__xml_output_finish(out, xml);
     return rc;
 }
 
 /*!
  * \internal
  * \brief Get and optionally output node info corresponding to a node ID from
  *        the controller
  *
  * \param[in,out] out                 Output object
  * \param[in,out] node_id             ID of node whose name to get. If \p NULL
  *                                    or 0, get the local node name. If not
  *                                    \p NULL, store the true node ID here on
  *                                    success.
  * \param[out]    node_name           If not \p NULL, where to store the node
  *                                    name
  * \param[out]    uuid                If not \p NULL, where to store the node
  *                                    UUID
  * \param[out]    state               If not \p NULL, where to store the
  *                                    membership state
  * \param[out]    is_remote           If not \p NULL, where to store whether the
  *                                    node is a Pacemaker Remote node
  * \param[out]    have_quorum         If not \p NULL, where to store whether the
  *                                    node has quorum
  * \param[in]     show_output         Whether to show the node info
  * \param[in]     message_timeout_ms  How long to wait for a reply from the
  *                                    \p pacemaker-controld API. If 0,
  *                                    \p pcmk_ipc_dispatch_sync will be used.
  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
  *                                    be used.
  *
  * \return Standard Pacemaker return code
  *
  * \note The caller is responsible for freeing \p *node_name, \p *uuid, and
  *       \p *state using \p free().
  */
 int
 pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id, char **node_name,
                       char **uuid, char **state, bool *have_quorum,
                       bool *is_remote, bool show_output,
                       unsigned int message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .show_output = show_output,
         .rc = EAGAIN,
         .message_timeout_ms = message_timeout_ms,
         .node_info = {
             .id = (node_id == NULL)? 0 : *node_id,
             .node_name = node_name,
             .uuid = uuid,
             .state = state,
         },
     };
     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     pcmk_ipc_api_t *controld_api = NULL;
 
     if (node_name != NULL) {
         *node_name = NULL;
     }
     if (uuid != NULL) {
         *uuid = NULL;
     }
     if (state != NULL) {
         *state = NULL;
     }
 
     if (message_timeout_ms == 0) {
         dispatch_type = pcmk_ipc_dispatch_sync;
     }
     controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
                                dispatch_type, false);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_node_info(controld_api,
                                              (node_id != NULL)? *node_id : 0);
 
         if (rc != pcmk_rc_ok) {
             out->err(out,
                      "error: Could not send request to controller API on local "
                      "node: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         if (dispatch_type == pcmk_ipc_dispatch_poll) {
             poll_until_reply(&data, controld_api, "local node");
         }
         pcmk_free_ipc_api(controld_api);
     }
 
     if (data.rc != pcmk_rc_ok) {
         return data.rc;
     }
 
     // String outputs are set in callback
     if (node_id != NULL) {
         *node_id = data.node_info.id;
     }
     if (have_quorum != NULL) {
         *have_quorum = data.node_info.have_quorum;
     }
     if (is_remote != NULL) {
         *is_remote = data.node_info.is_remote;
     }
 
     return data.rc;
 }
 
 // Documented in header
 int
 pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
                      char **uuid, char **state, bool *have_quorum,
                      bool *is_remote, bool show_output,
                      unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     CRM_ASSERT(node_name != NULL);
 
     rc = pcmk__xml_output_new(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
                                have_quorum, is_remote, show_output,
                                message_timeout_ms);
     pcmk__xml_output_finish(out, xml);
     return rc;
 }
 
 /*!
  * \internal
  * \brief Get and optionally output \p pacemakerd status
  *
  * \param[in,out] out                 Output object
  * \param[in]     ipc_name            IPC name for request
  * \param[in]     message_timeout_ms  How long to wait for a reply from the
  *                                    \p pacemakerd API. If 0,
  *                                    \p pcmk_ipc_dispatch_sync will be used.
  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
  *                                    be used.
  * \param[in]     show_output         Whether to output the \p pacemakerd state
  * \param[out]    state               Where to store the \p pacemakerd state, if
  *                                    not \p NULL
  *
  * \return Standard Pacemaker return code
  *
  * \note This function sets \p state to \p pcmk_pacemakerd_state_remote and
  *       returns \p pcmk_rc_ok if the IPC connection attempt returns
  *       \p EREMOTEIO. That code indicates that this is a Pacemaker Remote node
  *       with \p pacemaker-remoted running. The node may be connected to the
  *       cluster.
  */
 int
 pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
                         unsigned int message_timeout_ms, bool show_output,
                         enum pcmk_pacemakerd_state *state)
 {
     data_t data = {
         .out = out,
         .show_output = show_output,
         .rc = EAGAIN,
         .message_timeout_ms = message_timeout_ms,
         .pcmkd_state = pcmk_pacemakerd_state_invalid,
     };
     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     pcmk_ipc_api_t *pacemakerd_api = NULL;
 
     if (message_timeout_ms == 0) {
         dispatch_type = pcmk_ipc_dispatch_sync;
     }
     pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
                                  pacemakerd_event_cb, dispatch_type, true);
 
     if (pacemakerd_api != NULL) {
         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Could not ping launcher API: %s",
                      pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         if (dispatch_type == pcmk_ipc_dispatch_poll) {
             poll_until_reply(&data, pacemakerd_api, NULL);
         }
         pcmk_free_ipc_api(pacemakerd_api);
 
     } else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
                && show_output) {
         // No API connection so the callback wasn't run
         out->message(out, "pacemakerd-health",
                      NULL, data.pcmkd_state, NULL, time(NULL));
     }
 
     if (state != NULL) {
         *state = data.pcmkd_state;
     }
     return data.rc;
 }
 
 // Documented in header
 int
 pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
                        unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__xml_output_new(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
     pcmk__xml_output_finish(out, xml);
     return rc;
 }
 
 /* user data for looping through remote node xpath searches */
 struct node_data {
     pcmk__output_t *out;
     int found;
     const char *field;  /* XML attribute to check for node name */
     const char *type;
     bool bash_export;
 };
 
 static void
 remote_node_print_helper(xmlNode *result, void *user_data)
 {
     struct node_data *data = user_data;
     pcmk__output_t *out = data->out;
     const char *name = crm_element_value(result, XML_ATTR_UNAME);
     const char *id = crm_element_value(result, data->field);
 
     // node name and node id are the same for remote/guest nodes
     out->message(out, "crmadmin-node", data->type,
                  pcmk__s(name, id), id, data->bash_export);
     data->found++;
 }
 
 // \return Standard Pacemaker return code
 int
 pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export)
 {
     xmlNode *xml_node = NULL;
     int rc;
 
     rc = cib__signon_query(out, NULL, &xml_node);
 
     if (rc == pcmk_rc_ok) {
         struct node_data data = {
             .out = out,
             .found = 0,
             .bash_export = bash_export
         };
 
         out->begin_list(out, NULL, NULL, "nodes");
 
         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
             node_types = NULL;
         }
 
         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
             data.field = "id";
             data.type = "cluster";
             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
                                      remote_node_print_helper, &data);
         }
 
         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
             data.field = "value";
             data.type = "guest";
             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
                                      remote_node_print_helper, &data);
         }
 
         if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
             data.field = "id";
             data.type = "remote";
             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
                                      remote_node_print_helper, &data);
         }
 
         out->end_list(out);
 
         if (data.found == 0) {
             out->info(out, "No nodes configured");
         }
 
         free_xml(xml_node);
     }
 
     return rc;
 }
 
 int
 pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__xml_output_new(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__list_nodes(out, node_types, FALSE);
     pcmk__xml_output_finish(out, xml);
     return rc;
 }