LCOV - code coverage report
Current view: top level - pacemaker - pcmk_cluster_queries.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 323 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 20 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2020-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <libxml/tree.h>        // xmlNode
      13             : 
      14             : #include <pacemaker.h>
      15             : #include <pacemaker-internal.h>
      16             : 
      17             : #include <crm/crm.h>
      18             : #include <crm/cib.h>
      19             : #include <crm/cib/internal.h>
      20             : #include <crm/common/output_internal.h>
      21             : #include <crm/common/xml.h>
      22             : #include <crm/common/xml_internal.h>
      23             : #include <crm/common/iso8601.h>
      24             : #include <crm/common/ipc_controld.h>
      25             : #include <crm/common/ipc_pacemakerd.h>
      26             : 
      27             : //! Object to store node info from the controller API
      28             : typedef struct {
      29             :     /* Adapted from pcmk_controld_api_reply_t:data:node_info.
      30             :      * (char **) are convenient here for use within callbacks: we can skip
      31             :      * copying strings unless the caller passes a non-NULL value.
      32             :      */
      33             :     uint32_t id;
      34             :     char **node_name;
      35             :     char **uuid;
      36             :     char **state;
      37             :     bool have_quorum;
      38             :     bool is_remote;
      39             : } node_info_t;
      40             : 
      41             : //! Object to store API results, a timeout, and an output object
      42             : typedef struct {
      43             :     pcmk__output_t *out;
      44             :     bool show_output;
      45             :     int rc;
      46             :     unsigned int message_timeout_ms;
      47             :     enum pcmk_pacemakerd_state pcmkd_state;
      48             :     node_info_t node_info;
      49             : } data_t;
      50             : 
      51             : /*!
      52             :  * \internal
      53             :  * \brief Validate that an IPC API event is a good reply
      54             :  *
      55             :  * \param[in,out] data        API results and options
      56             :  * \param[in]     api         IPC API connection
      57             :  * \param[in]     event_type  Type of event that occurred
      58             :  * \param[in]     status      Event status
      59             :  *
      60             :  * \return Standard Pacemaker return code
      61             :  */
      62             : static int
      63           0 : validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
      64             :                      enum pcmk_ipc_event event_type, crm_exit_t status)
      65             : {
      66           0 :     pcmk__output_t *out = data->out;
      67             : 
      68           0 :     switch (event_type) {
      69           0 :         case pcmk_ipc_event_reply:
      70           0 :             break;
      71             : 
      72           0 :         case pcmk_ipc_event_disconnect:
      73           0 :             if (data->rc == ECONNRESET) { // Unexpected
      74           0 :                 out->err(out, "error: Lost connection to %s",
      75             :                          pcmk_ipc_name(api, true));
      76             :             }
      77             :             // Nothing bad but not the reply we're looking for
      78           0 :             return ENOTSUP;
      79             : 
      80           0 :         default:
      81             :             // Ditto
      82           0 :             return ENOTSUP;
      83             :     }
      84             : 
      85           0 :     if (status != CRM_EX_OK) {
      86           0 :         out->err(out, "error: Bad reply from %s: %s",
      87             :                  pcmk_ipc_name(api, true), crm_exit_str(status));
      88           0 :         data->rc = EBADMSG;
      89           0 :         return data->rc;
      90             :     }
      91           0 :     return pcmk_rc_ok;
      92             : }
      93             : 
      94             : /*!
      95             :  * \internal
      96             :  * \brief Validate that a controller API event is a good reply of expected type
      97             :  *
      98             :  * \param[in,out] data           API results and options
      99             :  * \param[in]     api            Controller connection
     100             :  * \param[in]     event_type     Type of event that occurred
     101             :  * \param[in]     status         Event status
     102             :  * \param[in]     event_data     Event-specific data
     103             :  * \param[in]     expected_type  Expected reply type
     104             :  *
     105             :  * \return Standard Pacemaker return code
     106             :  */
     107             : static int
     108           0 : validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
     109             :                         enum pcmk_ipc_event event_type, crm_exit_t status,
     110             :                         const void *event_data,
     111             :                         enum pcmk_controld_api_reply expected_type)
     112             : {
     113           0 :     pcmk__output_t *out = data->out;
     114           0 :     int rc = pcmk_rc_ok;
     115           0 :     const pcmk_controld_api_reply_t *reply = NULL;
     116             : 
     117           0 :     rc = validate_reply_event(data, api, event_type, status);
     118           0 :     if (rc != pcmk_rc_ok) {
     119           0 :         return rc;
     120             :     }
     121             : 
     122           0 :     reply = (const pcmk_controld_api_reply_t *) event_data;
     123             : 
     124           0 :     if (reply->reply_type != expected_type) {
     125           0 :         out->err(out, "error: Unexpected reply type '%s' from controller",
     126           0 :                  pcmk__controld_api_reply2str(reply->reply_type));
     127           0 :         data->rc = EBADMSG;
     128           0 :         return data->rc;
     129             :     }
     130             : 
     131           0 :     return pcmk_rc_ok;
     132             : }
     133             : 
     134             : /*!
     135             :  * \internal
     136             :  * \brief Validate that a \p pacemakerd API event is a good reply of expected
     137             :  *        type
     138             :  *
     139             :  * \param[in,out] data           API results and options
     140             :  * \param[in]     api            \p pacemakerd connection
     141             :  * \param[in]     event_type     Type of event that occurred
     142             :  * \param[in]     status         Event status
     143             :  * \param[in]     event_data     Event-specific data
     144             :  * \param[in]     expected_type  Expected reply type
     145             :  *
     146             :  * \return Standard Pacemaker return code
     147             :  */
     148             : static int
     149           0 : validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
     150             :                      enum pcmk_ipc_event event_type, crm_exit_t status,
     151             :                      const void *event_data,
     152             :                      enum pcmk_pacemakerd_api_reply expected_type)
     153             : {
     154           0 :     pcmk__output_t *out = data->out;
     155           0 :     const pcmk_pacemakerd_api_reply_t *reply = NULL;
     156           0 :     int rc = validate_reply_event(data, api, event_type, status);
     157             : 
     158           0 :     if (rc != pcmk_rc_ok) {
     159           0 :         return rc;
     160             :     }
     161             : 
     162           0 :     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
     163             : 
     164           0 :     if (reply->reply_type != expected_type) {
     165           0 :         out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
     166           0 :                  pcmk__pcmkd_api_reply2str(reply->reply_type));
     167           0 :         data->rc = EBADMSG;
     168           0 :         return data->rc;
     169             :     }
     170             : 
     171           0 :     return pcmk_rc_ok;
     172             : }
     173             : 
     174             : /*!
     175             :  * \internal
     176             :  * \brief Process a controller status IPC event
     177             :  *
     178             :  * \param[in,out] controld_api  Controller connection
     179             :  * \param[in]     event_type    Type of event that occurred
     180             :  * \param[in]     status        Event status
     181             :  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
     182             :  *                              event-specific data
     183             :  * \param[in,out] user_data     \p data_t object for API results and options
     184             :  */
     185             : static void
     186           0 : controller_status_event_cb(pcmk_ipc_api_t *controld_api,
     187             :                            enum pcmk_ipc_event event_type, crm_exit_t status,
     188             :                            void *event_data, void *user_data)
     189             : {
     190           0 :     data_t *data = (data_t *) user_data;
     191           0 :     pcmk__output_t *out = data->out;
     192           0 :     const pcmk_controld_api_reply_t *reply = NULL;
     193             : 
     194           0 :     int rc = validate_controld_reply(data, controld_api, event_type, status,
     195             :                                      event_data, pcmk_controld_reply_ping);
     196             : 
     197           0 :     if (rc != pcmk_rc_ok) {
     198           0 :         return;
     199             :     }
     200             : 
     201           0 :     reply = (const pcmk_controld_api_reply_t *) event_data;
     202           0 :     out->message(out, "health",
     203           0 :                  reply->data.ping.sys_from, reply->host_from,
     204           0 :                  reply->data.ping.fsa_state, reply->data.ping.result);
     205           0 :     data->rc = pcmk_rc_ok;
     206             : }
     207             : 
     208             : /*!
     209             :  * \internal
     210             :  * \brief Process a designated controller IPC event
     211             :  *
     212             :  * \param[in,out] controld_api  Controller connection
     213             :  * \param[in]     event_type    Type of event that occurred
     214             :  * \param[in]     status        Event status
     215             :  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
     216             :  *                              event-specific data
     217             :  * \param[in,out] user_data     \p data_t object for API results and options
     218             :  */
     219             : static void
     220           0 : designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
     221             :                                enum pcmk_ipc_event event_type,
     222             :                                crm_exit_t status, void *event_data,
     223             :                                void *user_data)
     224             : {
     225           0 :     data_t *data = (data_t *) user_data;
     226           0 :     pcmk__output_t *out = data->out;
     227           0 :     const pcmk_controld_api_reply_t *reply = NULL;
     228             : 
     229           0 :     int rc = validate_controld_reply(data, controld_api, event_type, status,
     230             :                                      event_data, pcmk_controld_reply_ping);
     231             : 
     232           0 :     if (rc != pcmk_rc_ok) {
     233           0 :         return;
     234             :     }
     235             : 
     236           0 :     reply = (const pcmk_controld_api_reply_t *) event_data;
     237           0 :     out->message(out, "dc", reply->host_from);
     238           0 :     data->rc = pcmk_rc_ok;
     239             : }
     240             : 
     241             : /*!
     242             :  * \internal
     243             :  * \brief Process a node info IPC event
     244             :  *
     245             :  * \param[in,out] controld_api  Controller connection
     246             :  * \param[in]     event_type    Type of event that occurred
     247             :  * \param[in]     status        Event status
     248             :  * \param[in,out] event_data    \p pcmk_controld_api_reply_t object containing
     249             :  *                              event-specific data
     250             :  * \param[in,out] user_data     \p data_t object for API results and options
     251             :  */
     252             : static void
     253           0 : node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
     254             :                    crm_exit_t status, void *event_data, void *user_data)
     255             : {
     256           0 :     data_t *data = (data_t *) user_data;
     257           0 :     pcmk__output_t *out = data->out;
     258             : 
     259           0 :     const pcmk_controld_api_reply_t *reply = NULL;
     260             : 
     261           0 :     int rc = validate_controld_reply(data, controld_api, event_type, status,
     262             :                                      event_data, pcmk_controld_reply_info);
     263             : 
     264           0 :     if (rc != pcmk_rc_ok) {
     265           0 :         return;
     266             :     }
     267             : 
     268           0 :     reply = (const pcmk_controld_api_reply_t *) event_data;
     269             : 
     270           0 :     if (reply->data.node_info.uname == NULL) {
     271           0 :         out->err(out, "Node is not known to cluster");
     272           0 :         data->rc = pcmk_rc_node_unknown;
     273           0 :         return;
     274             :     }
     275             : 
     276           0 :     data->node_info.have_quorum = reply->data.node_info.have_quorum;
     277           0 :     data->node_info.is_remote = reply->data.node_info.is_remote;
     278           0 :     data->node_info.id = (uint32_t) reply->data.node_info.id;
     279             : 
     280           0 :     pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
     281           0 :     pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
     282           0 :     pcmk__str_update(data->node_info.state, reply->data.node_info.state);
     283             : 
     284           0 :     if (data->show_output) {
     285           0 :         out->message(out, "node-info",
     286           0 :                      (uint32_t) reply->data.node_info.id, reply->data.node_info.uname,
     287           0 :                      reply->data.node_info.uuid, reply->data.node_info.state,
     288           0 :                      reply->data.node_info.have_quorum,
     289           0 :                      reply->data.node_info.is_remote);
     290             :     }
     291             : 
     292           0 :     data->rc = pcmk_rc_ok;
     293             : }
     294             : 
     295             : /*!
     296             :  * \internal
     297             :  * \brief Process a \p pacemakerd status IPC event
     298             :  *
     299             :  * \param[in,out] pacemakerd_api  \p pacemakerd connection
     300             :  * \param[in]     event_type      Type of event that occurred
     301             :  * \param[in]     status          Event status
     302             :  * \param[in,out] event_data      \p pcmk_pacemakerd_api_reply_t object
     303             :  *                                containing event-specific data
     304             :  * \param[in,out] user_data       \p data_t object for API results and options
     305             :  */
     306             : static void
     307           0 : pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     308             :                     enum pcmk_ipc_event event_type, crm_exit_t status,
     309             :                     void *event_data, void *user_data)
     310             : {
     311           0 :     data_t *data = user_data;
     312           0 :     pcmk__output_t *out = data->out;
     313           0 :     const pcmk_pacemakerd_api_reply_t *reply = NULL;
     314             : 
     315           0 :     int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
     316             :                                   event_data, pcmk_pacemakerd_reply_ping);
     317             : 
     318           0 :     if (rc != pcmk_rc_ok) {
     319           0 :         return;
     320             :     }
     321             : 
     322             :     // Parse desired information from reply
     323           0 :     reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
     324             : 
     325           0 :     data->pcmkd_state = reply->data.ping.state;
     326           0 :     data->rc = pcmk_rc_ok;
     327             : 
     328           0 :     if (!data->show_output) {
     329           0 :         return;
     330             :     }
     331             : 
     332           0 :     if (reply->data.ping.status == pcmk_rc_ok) {
     333           0 :         out->message(out, "pacemakerd-health",
     334           0 :                      reply->data.ping.sys_from, reply->data.ping.state, NULL,
     335           0 :                      reply->data.ping.last_good);
     336             :     } else {
     337           0 :         out->message(out, "pacemakerd-health",
     338           0 :                      reply->data.ping.sys_from, reply->data.ping.state,
     339             :                      "query failed", time(NULL));
     340             :     }
     341             : }
     342             : 
     343             : static pcmk_ipc_api_t *
     344           0 : ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
     345             :             enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
     346             : {
     347             :     int rc;
     348           0 :     pcmk__output_t *out = data->out;
     349           0 :     pcmk_ipc_api_t *api = NULL;
     350             : 
     351           0 :     rc = pcmk_new_ipc_api(&api, server);
     352           0 :     if (api == NULL) {
     353           0 :         out->err(out, "error: Could not connect to %s: %s",
     354             :                 pcmk_ipc_name(api, true),
     355             :                 pcmk_rc_str(rc));
     356           0 :         data->rc = rc;
     357           0 :         return NULL;
     358             :     }
     359           0 :     if (cb != NULL) {
     360           0 :         pcmk_register_ipc_callback(api, cb, data);
     361             :     }
     362             : 
     363           0 :     rc = pcmk__connect_ipc(api, dispatch_type, 5);
     364           0 :     if (rc != pcmk_rc_ok) {
     365           0 :         if (rc == EREMOTEIO) {
     366           0 :             data->pcmkd_state = pcmk_pacemakerd_state_remote;
     367           0 :             if (eremoteio_ok) {
     368             :                 /* EREMOTEIO may be expected and acceptable for some callers
     369             :                  * on a Pacemaker Remote node
     370             :                  */
     371           0 :                 crm_debug("Ignoring %s connection failure: No "
     372             :                           "Pacemaker Remote connection",
     373             :                           pcmk_ipc_name(api, true));
     374           0 :                 rc = pcmk_rc_ok;
     375             :             } else {
     376           0 :                 out->err(out, "error: Could not connect to %s: %s",
     377             :                          pcmk_ipc_name(api, true), pcmk_rc_str(rc));
     378             :             }
     379             :         }
     380           0 :         data->rc = rc;
     381           0 :         pcmk_free_ipc_api(api);
     382           0 :         return NULL;
     383             :     }
     384             : 
     385           0 :     return api;
     386             : }
     387             : 
     388             : /*!
     389             :  * \internal
     390             :  * \brief Poll an IPC API connection until timeout or a reply is received
     391             :  *
     392             :  * \param[in,out] data     API results and options
     393             :  * \param[in,out] api      IPC API connection
     394             :  * \param[in]     on_node  If not \p NULL, name of the node to poll (used only
     395             :  *                         for logging)
     396             :  *
     397             :  * \note Sets the \p rc member of \p data on error
     398             :  */
     399             : static void
     400           0 : poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
     401             : {
     402           0 :     pcmk__output_t *out = data->out;
     403             : 
     404           0 :     uint64_t start_nsec = qb_util_nano_current_get();
     405           0 :     uint64_t end_nsec = 0;
     406           0 :     uint64_t elapsed_ms = 0;
     407           0 :     uint64_t remaining_ms = data->message_timeout_ms;
     408             : 
     409           0 :     while (remaining_ms > 0) {
     410           0 :         int rc = pcmk_poll_ipc(api, remaining_ms);
     411             : 
     412           0 :         if (rc == EAGAIN) {
     413             :             // Poll timed out
     414           0 :             break;
     415             :         }
     416             : 
     417           0 :         if (rc != pcmk_rc_ok) {
     418           0 :             out->err(out, "error: Failed to poll %s API%s%s: %s",
     419             :                      pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
     420             :                      pcmk__s(on_node, ""), pcmk_rc_str(rc));
     421           0 :             data->rc = rc;
     422           0 :             return;
     423             :         }
     424             : 
     425           0 :         pcmk_dispatch_ipc(api);
     426             : 
     427           0 :         if (data->rc != EAGAIN) {
     428             :             // Received a reply
     429           0 :             return;
     430             :         }
     431           0 :         end_nsec = qb_util_nano_current_get();
     432           0 :         elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
     433           0 :         remaining_ms = data->message_timeout_ms - elapsed_ms;
     434             :     }
     435             : 
     436           0 :     out->err(out,
     437             :              "error: Timed out after %ums waiting for reply from %s API%s%s",
     438             :              data->message_timeout_ms, pcmk_ipc_name(api, true),
     439             :              (on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
     440           0 :     data->rc = EAGAIN;
     441             : }
     442             : 
     443             : /*!
     444             :  * \internal
     445             :  * \brief Get and output controller status
     446             :  *
     447             :  * \param[in,out] out                 Output object
     448             :  * \param[in]     node_name           Name of node whose status is desired
     449             :  *                                    (\p NULL for DC)
     450             :  * \param[in]     message_timeout_ms  How long to wait for a reply from the
     451             :  *                                    \p pacemaker-controld API. If 0,
     452             :  *                                    \p pcmk_ipc_dispatch_sync will be used.
     453             :  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
     454             :  *                                    be used.
     455             :  *
     456             :  * \return Standard Pacemaker return code
     457             :  */
     458             : int
     459           0 : pcmk__controller_status(pcmk__output_t *out, const char *node_name,
     460             :                         unsigned int message_timeout_ms)
     461             : {
     462           0 :     data_t data = {
     463             :         .out = out,
     464             :         .rc = EAGAIN,
     465             :         .message_timeout_ms = message_timeout_ms,
     466             :     };
     467           0 :     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     468           0 :     pcmk_ipc_api_t *controld_api = NULL;
     469             : 
     470           0 :     if (message_timeout_ms == 0) {
     471           0 :         dispatch_type = pcmk_ipc_dispatch_sync;
     472             :     }
     473           0 :     controld_api = ipc_connect(&data, pcmk_ipc_controld,
     474             :                                controller_status_event_cb, dispatch_type,
     475             :                                false);
     476             : 
     477           0 :     if (controld_api != NULL) {
     478           0 :         int rc = pcmk_controld_api_ping(controld_api, node_name);
     479           0 :         if (rc != pcmk_rc_ok) {
     480           0 :             out->err(out, "error: Could not ping controller API on %s: %s",
     481             :                      pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
     482           0 :             data.rc = rc;
     483             :         }
     484             : 
     485           0 :         if (dispatch_type == pcmk_ipc_dispatch_poll) {
     486           0 :             poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
     487             :         }
     488           0 :         pcmk_free_ipc_api(controld_api);
     489             :     }
     490             : 
     491           0 :     return data.rc;
     492             : }
     493             : 
     494             : 
     495             : // Documented in header
     496             : int
     497           0 : pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
     498             :                        unsigned int message_timeout_ms)
     499             : {
     500           0 :     pcmk__output_t *out = NULL;
     501           0 :     int rc = pcmk_rc_ok;
     502             : 
     503           0 :     rc = pcmk__xml_output_new(&out, xml);
     504           0 :     if (rc != pcmk_rc_ok) {
     505           0 :         return rc;
     506             :     }
     507             : 
     508           0 :     pcmk__register_lib_messages(out);
     509             : 
     510           0 :     rc = pcmk__controller_status(out, node_name, message_timeout_ms);
     511           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     512           0 :     return rc;
     513             : }
     514             : 
     515             : /*!
     516             :  * \internal
     517             :  * \brief Get and output designated controller node name
     518             :  *
     519             :  * \param[in,out] out                 Output object
     520             :  * \param[in]     message_timeout_ms  How long to wait for a reply from the
     521             :  *                                    \p pacemaker-controld API. If 0,
     522             :  *                                    \p pcmk_ipc_dispatch_sync will be used.
     523             :  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
     524             :  *                                    be used.
     525             :  *
     526             :  * \return Standard Pacemaker return code
     527             :  */
     528             : int
     529           0 : pcmk__designated_controller(pcmk__output_t *out,
     530             :                             unsigned int message_timeout_ms)
     531             : {
     532           0 :     data_t data = {
     533             :         .out = out,
     534             :         .rc = EAGAIN,
     535             :         .message_timeout_ms = message_timeout_ms,
     536             :     };
     537           0 :     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     538           0 :     pcmk_ipc_api_t *controld_api = NULL;
     539             : 
     540           0 :     if (message_timeout_ms == 0) {
     541           0 :         dispatch_type = pcmk_ipc_dispatch_sync;
     542             :     }
     543           0 :     controld_api = ipc_connect(&data, pcmk_ipc_controld,
     544             :                                designated_controller_event_cb, dispatch_type,
     545             :                                false);
     546             : 
     547           0 :     if (controld_api != NULL) {
     548           0 :         int rc = pcmk_controld_api_ping(controld_api, NULL);
     549           0 :         if (rc != pcmk_rc_ok) {
     550           0 :             out->err(out, "error: Could not ping controller API on DC: %s",
     551             :                      pcmk_rc_str(rc));
     552           0 :             data.rc = rc;
     553             :         }
     554             : 
     555           0 :         if (dispatch_type == pcmk_ipc_dispatch_poll) {
     556           0 :             poll_until_reply(&data, controld_api, "DC");
     557             :         }
     558           0 :         pcmk_free_ipc_api(controld_api);
     559             :     }
     560             : 
     561           0 :     return data.rc;
     562             : }
     563             : 
     564             : // Documented in header
     565             : int
     566           0 : pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
     567             : {
     568           0 :     pcmk__output_t *out = NULL;
     569           0 :     int rc = pcmk_rc_ok;
     570             : 
     571           0 :     rc = pcmk__xml_output_new(&out, xml);
     572           0 :     if (rc != pcmk_rc_ok) {
     573           0 :         return rc;
     574             :     }
     575             : 
     576           0 :     pcmk__register_lib_messages(out);
     577             : 
     578           0 :     rc = pcmk__designated_controller(out, message_timeout_ms);
     579           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     580           0 :     return rc;
     581             : }
     582             : 
     583             : /*!
     584             :  * \internal
     585             :  * \brief Get and optionally output node info corresponding to a node ID from
     586             :  *        the controller
     587             :  *
     588             :  * \param[in,out] out                 Output object
     589             :  * \param[in,out] node_id             ID of node whose name to get. If \p NULL
     590             :  *                                    or 0, get the local node name. If not
     591             :  *                                    \p NULL, store the true node ID here on
     592             :  *                                    success.
     593             :  * \param[out]    node_name           If not \p NULL, where to store the node
     594             :  *                                    name
     595             :  * \param[out]    uuid                If not \p NULL, where to store the node
     596             :  *                                    UUID
     597             :  * \param[out]    state               If not \p NULL, where to store the
     598             :  *                                    membership state
     599             :  * \param[out]    is_remote           If not \p NULL, where to store whether the
     600             :  *                                    node is a Pacemaker Remote node
     601             :  * \param[out]    have_quorum         If not \p NULL, where to store whether the
     602             :  *                                    node has quorum
     603             :  * \param[in]     show_output         Whether to show the node info
     604             :  * \param[in]     message_timeout_ms  How long to wait for a reply from the
     605             :  *                                    \p pacemaker-controld API. If 0,
     606             :  *                                    \p pcmk_ipc_dispatch_sync will be used.
     607             :  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
     608             :  *                                    be used.
     609             :  *
     610             :  * \return Standard Pacemaker return code
     611             :  *
     612             :  * \note The caller is responsible for freeing \p *node_name, \p *uuid, and
     613             :  *       \p *state using \p free().
     614             :  */
     615             : int
     616           0 : pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id, char **node_name,
     617             :                       char **uuid, char **state, bool *have_quorum,
     618             :                       bool *is_remote, bool show_output,
     619             :                       unsigned int message_timeout_ms)
     620             : {
     621           0 :     data_t data = {
     622             :         .out = out,
     623             :         .show_output = show_output,
     624             :         .rc = EAGAIN,
     625             :         .message_timeout_ms = message_timeout_ms,
     626             :         .node_info = {
     627           0 :             .id = (node_id == NULL)? 0 : *node_id,
     628             :             .node_name = node_name,
     629             :             .uuid = uuid,
     630             :             .state = state,
     631             :         },
     632             :     };
     633           0 :     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     634           0 :     pcmk_ipc_api_t *controld_api = NULL;
     635             : 
     636           0 :     if (node_name != NULL) {
     637           0 :         *node_name = NULL;
     638             :     }
     639           0 :     if (uuid != NULL) {
     640           0 :         *uuid = NULL;
     641             :     }
     642           0 :     if (state != NULL) {
     643           0 :         *state = NULL;
     644             :     }
     645             : 
     646           0 :     if (message_timeout_ms == 0) {
     647           0 :         dispatch_type = pcmk_ipc_dispatch_sync;
     648             :     }
     649           0 :     controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
     650             :                                dispatch_type, false);
     651             : 
     652           0 :     if (controld_api != NULL) {
     653           0 :         int rc = pcmk_controld_api_node_info(controld_api,
     654             :                                              (node_id != NULL)? *node_id : 0);
     655             : 
     656           0 :         if (rc != pcmk_rc_ok) {
     657           0 :             out->err(out,
     658             :                      "error: Could not send request to controller API on local "
     659             :                      "node: %s", pcmk_rc_str(rc));
     660           0 :             data.rc = rc;
     661             :         }
     662             : 
     663           0 :         if (dispatch_type == pcmk_ipc_dispatch_poll) {
     664           0 :             poll_until_reply(&data, controld_api, "local node");
     665             :         }
     666           0 :         pcmk_free_ipc_api(controld_api);
     667             :     }
     668             : 
     669           0 :     if (data.rc != pcmk_rc_ok) {
     670           0 :         return data.rc;
     671             :     }
     672             : 
     673             :     // String outputs are set in callback
     674           0 :     if (node_id != NULL) {
     675           0 :         *node_id = data.node_info.id;
     676             :     }
     677           0 :     if (have_quorum != NULL) {
     678           0 :         *have_quorum = data.node_info.have_quorum;
     679             :     }
     680           0 :     if (is_remote != NULL) {
     681           0 :         *is_remote = data.node_info.is_remote;
     682             :     }
     683             : 
     684           0 :     return data.rc;
     685             : }
     686             : 
     687             : // Documented in header
     688             : int
     689           0 : pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
     690             :                      char **uuid, char **state, bool *have_quorum,
     691             :                      bool *is_remote, bool show_output,
     692             :                      unsigned int message_timeout_ms)
     693             : {
     694           0 :     pcmk__output_t *out = NULL;
     695           0 :     int rc = pcmk_rc_ok;
     696             : 
     697           0 :     CRM_ASSERT(node_name != NULL);
     698             : 
     699           0 :     rc = pcmk__xml_output_new(&out, xml);
     700           0 :     if (rc != pcmk_rc_ok) {
     701           0 :         return rc;
     702             :     }
     703             : 
     704           0 :     pcmk__register_lib_messages(out);
     705             : 
     706           0 :     rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
     707             :                                have_quorum, is_remote, show_output,
     708             :                                message_timeout_ms);
     709           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     710           0 :     return rc;
     711             : }
     712             : 
     713             : /*!
     714             :  * \internal
     715             :  * \brief Get and optionally output \p pacemakerd status
     716             :  *
     717             :  * \param[in,out] out                 Output object
     718             :  * \param[in]     ipc_name            IPC name for request
     719             :  * \param[in]     message_timeout_ms  How long to wait for a reply from the
     720             :  *                                    \p pacemakerd API. If 0,
     721             :  *                                    \p pcmk_ipc_dispatch_sync will be used.
     722             :  *                                    Otherwise, \p pcmk_ipc_dispatch_poll will
     723             :  *                                    be used.
     724             :  * \param[in]     show_output         Whether to output the \p pacemakerd state
     725             :  * \param[out]    state               Where to store the \p pacemakerd state, if
     726             :  *                                    not \p NULL
     727             :  *
     728             :  * \return Standard Pacemaker return code
     729             :  *
     730             :  * \note This function sets \p state to \p pcmk_pacemakerd_state_remote and
     731             :  *       returns \p pcmk_rc_ok if the IPC connection attempt returns
     732             :  *       \p EREMOTEIO. That code indicates that this is a Pacemaker Remote node
     733             :  *       with \p pacemaker-remoted running. The node may be connected to the
     734             :  *       cluster.
     735             :  */
     736             : int
     737           0 : pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
     738             :                         unsigned int message_timeout_ms, bool show_output,
     739             :                         enum pcmk_pacemakerd_state *state)
     740             : {
     741           0 :     data_t data = {
     742             :         .out = out,
     743             :         .show_output = show_output,
     744             :         .rc = EAGAIN,
     745             :         .message_timeout_ms = message_timeout_ms,
     746             :         .pcmkd_state = pcmk_pacemakerd_state_invalid,
     747             :     };
     748           0 :     enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
     749           0 :     pcmk_ipc_api_t *pacemakerd_api = NULL;
     750             : 
     751           0 :     if (message_timeout_ms == 0) {
     752           0 :         dispatch_type = pcmk_ipc_dispatch_sync;
     753             :     }
     754           0 :     pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
     755             :                                  pacemakerd_event_cb, dispatch_type, true);
     756             : 
     757           0 :     if (pacemakerd_api != NULL) {
     758           0 :         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
     759           0 :         if (rc != pcmk_rc_ok) {
     760           0 :             out->err(out, "error: Could not ping launcher API: %s",
     761             :                      pcmk_rc_str(rc));
     762           0 :             data.rc = rc;
     763             :         }
     764             : 
     765           0 :         if (dispatch_type == pcmk_ipc_dispatch_poll) {
     766           0 :             poll_until_reply(&data, pacemakerd_api, NULL);
     767             :         }
     768           0 :         pcmk_free_ipc_api(pacemakerd_api);
     769             : 
     770           0 :     } else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
     771           0 :                && show_output) {
     772             :         // No API connection so the callback wasn't run
     773           0 :         out->message(out, "pacemakerd-health",
     774           0 :                      NULL, data.pcmkd_state, NULL, time(NULL));
     775             :     }
     776             : 
     777           0 :     if (state != NULL) {
     778           0 :         *state = data.pcmkd_state;
     779             :     }
     780           0 :     return data.rc;
     781             : }
     782             : 
     783             : // Documented in header
     784             : int
     785           0 : pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
     786             :                        unsigned int message_timeout_ms)
     787             : {
     788           0 :     pcmk__output_t *out = NULL;
     789           0 :     int rc = pcmk_rc_ok;
     790             : 
     791           0 :     rc = pcmk__xml_output_new(&out, xml);
     792           0 :     if (rc != pcmk_rc_ok) {
     793           0 :         return rc;
     794             :     }
     795             : 
     796           0 :     pcmk__register_lib_messages(out);
     797             : 
     798           0 :     rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
     799           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     800           0 :     return rc;
     801             : }
     802             : 
     803             : /* user data for looping through remote node xpath searches */
     804             : struct node_data {
     805             :     pcmk__output_t *out;
     806             :     int found;
     807             :     const char *field;  /* XML attribute to check for node name */
     808             :     const char *type;
     809             :     bool bash_export;
     810             : };
     811             : 
     812             : static void
     813           0 : remote_node_print_helper(xmlNode *result, void *user_data)
     814             : {
     815           0 :     struct node_data *data = user_data;
     816           0 :     pcmk__output_t *out = data->out;
     817           0 :     const char *name = crm_element_value(result, PCMK_XA_UNAME);
     818           0 :     const char *id = crm_element_value(result, data->field);
     819             : 
     820             :     // node name and node id are the same for remote/guest nodes
     821           0 :     out->message(out, "crmadmin-node", data->type,
     822           0 :                  pcmk__s(name, id), id, data->bash_export);
     823           0 :     data->found++;
     824           0 : }
     825             : 
     826             : // \return Standard Pacemaker return code
     827             : int
     828           0 : pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export)
     829             : {
     830           0 :     xmlNode *xml_node = NULL;
     831             :     int rc;
     832             : 
     833           0 :     rc = cib__signon_query(out, NULL, &xml_node);
     834             : 
     835           0 :     if (rc == pcmk_rc_ok) {
     836           0 :         struct node_data data = {
     837             :             .out = out,
     838             :             .found = 0,
     839             :             .bash_export = bash_export
     840             :         };
     841             : 
     842             :         /* PCMK_XE_NODES acts as the list's element name for CLI tools that
     843             :          * use pcmk__output_enable_list_element.  Otherwise PCMK_XE_NODES is
     844             :          * the value of the list's PCMK_XA_NAME attribute.
     845             :          */
     846           0 :         out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
     847             : 
     848           0 :         if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
     849           0 :             node_types = NULL;
     850             :         }
     851             : 
     852           0 :         if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
     853           0 :             data.field = PCMK_XA_ID;
     854           0 :             data.type = "cluster";
     855           0 :             crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
     856             :                                      remote_node_print_helper, &data);
     857             :         }
     858             : 
     859           0 :         if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
     860           0 :             data.field = PCMK_XA_VALUE;
     861           0 :             data.type = "guest";
     862           0 :             crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
     863             :                                      remote_node_print_helper, &data);
     864             :         }
     865             : 
     866           0 :         if (pcmk__str_empty(node_types)
     867           0 :             || pcmk__str_eq(node_types, ",|^remote", pcmk__str_regex)) {
     868           0 :             data.field = PCMK_XA_ID;
     869           0 :             data.type = "remote";
     870           0 :             crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
     871             :                                      remote_node_print_helper, &data);
     872             :         }
     873             : 
     874           0 :         out->end_list(out);
     875             : 
     876           0 :         if (data.found == 0) {
     877           0 :             out->info(out, "No nodes configured");
     878             :         }
     879             : 
     880           0 :         free_xml(xml_node);
     881             :     }
     882             : 
     883           0 :     return rc;
     884             : }
     885             : 
     886             : int
     887           0 : pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
     888             : {
     889           0 :     pcmk__output_t *out = NULL;
     890           0 :     int rc = pcmk_rc_ok;
     891             : 
     892           0 :     rc = pcmk__xml_output_new(&out, xml);
     893           0 :     if (rc != pcmk_rc_ok) {
     894           0 :         return rc;
     895             :     }
     896             : 
     897           0 :     pcmk__register_lib_messages(out);
     898             : 
     899           0 :     rc = pcmk__list_nodes(out, node_types, FALSE);
     900           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     901           0 :     return rc;
     902             : }

Generated by: LCOV version 1.14