root/lib/common/ipc_pacemakerd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk_pacemakerd_api_daemon_state_text2enum
  2. pcmk_pacemakerd_api_daemon_state_enum2text
  3. pcmk__pcmkd_state_enum2friendly
  4. pcmk__pcmkd_api_reply2str
  5. new_data
  6. free_data
  7. post_connect
  8. post_disconnect
  9. reply_expected
  10. dispatch
  11. pcmk__pacemakerd_api_methods
  12. do_pacemakerd_api_call
  13. pcmk_pacemakerd_api_ping
  14. pcmk_pacemakerd_api_shutdown

   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 <stdlib.h>
  13 #include <time.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/xml.h>
  17 #include <crm/common/ipc.h>
  18 #include <crm/common/ipc_internal.h>
  19 #include <crm/common/ipc_pacemakerd.h>
  20 #include "crmcommon_private.h"
  21 
  22 typedef struct pacemakerd_api_private_s {
  23     enum pcmk_pacemakerd_state state;
  24     char *client_uuid;
  25 } pacemakerd_api_private_t;
  26 
  27 static const char *pacemakerd_state_str[] = {
  28     PCMK__VALUE_INIT,
  29     PCMK__VALUE_STARTING_DAEMONS,
  30     PCMK__VALUE_WAIT_FOR_PING,
  31     PCMK__VALUE_RUNNING,
  32     PCMK__VALUE_SHUTTING_DOWN,
  33     PCMK__VALUE_SHUTDOWN_COMPLETE,
  34     PCMK_VALUE_REMOTE,
  35 };
  36 
  37 enum pcmk_pacemakerd_state
  38 pcmk_pacemakerd_api_daemon_state_text2enum(const char *state)
     /* [previous][next][first][last][top][bottom][index][help] */
  39 {
  40     int i;
  41 
  42     if (state == NULL) {
  43         return pcmk_pacemakerd_state_invalid;
  44     }
  45     for (i=pcmk_pacemakerd_state_init; i <= pcmk_pacemakerd_state_max;
  46          i++) {
  47         if (pcmk__str_eq(state, pacemakerd_state_str[i], pcmk__str_none)) {
  48             return i;
  49         }
  50     }
  51     return pcmk_pacemakerd_state_invalid;
  52 }
  53 
  54 const char *
  55 pcmk_pacemakerd_api_daemon_state_enum2text(
     /* [previous][next][first][last][top][bottom][index][help] */
  56     enum pcmk_pacemakerd_state state)
  57 {
  58     if ((state >= pcmk_pacemakerd_state_init) &&
  59         (state <= pcmk_pacemakerd_state_max)) {
  60         return pacemakerd_state_str[state];
  61     }
  62     return "invalid";
  63 }
  64 
  65 /*!
  66  * \internal
  67  * \brief Return a friendly string representation of a \p pacemakerd state
  68  *
  69  * \param[in] state  \p pacemakerd state
  70  *
  71  * \return A user-friendly string representation of \p state, or
  72  *         <tt>"Invalid pacemakerd state"</tt>
  73  */
  74 const char *
  75 pcmk__pcmkd_state_enum2friendly(enum pcmk_pacemakerd_state state)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     switch (state) {
  78         case pcmk_pacemakerd_state_init:
  79             return "Initializing pacemaker";
  80         case pcmk_pacemakerd_state_starting_daemons:
  81             return "Pacemaker daemons are starting";
  82         case pcmk_pacemakerd_state_wait_for_ping:
  83             return "Waiting for startup trigger from SBD";
  84         case pcmk_pacemakerd_state_running:
  85             return "Pacemaker is running";
  86         case pcmk_pacemakerd_state_shutting_down:
  87             return "Pacemaker daemons are shutting down";
  88         case pcmk_pacemakerd_state_shutdown_complete:
  89             /* Assuming pacemakerd won't process messages while in
  90              * shutdown_complete state unless reporting to SBD
  91              */
  92             return "Pacemaker daemons are shut down (reporting to SBD)";
  93         case pcmk_pacemakerd_state_remote:
  94             return PCMK__SERVER_REMOTED " is running "
  95                    "(on a Pacemaker Remote node)";
  96         default:
  97             return "Invalid pacemakerd state";
  98     }
  99 }
 100 
 101 /*!
 102  * \internal
 103  * \brief Get a string representation of a \p pacemakerd API reply type
 104  *
 105  * \param[in] reply  \p pacemakerd API reply type
 106  *
 107  * \return String representation of a \p pacemakerd API reply type
 108  */
 109 const char *
 110 pcmk__pcmkd_api_reply2str(enum pcmk_pacemakerd_api_reply reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 111 {
 112     switch (reply) {
 113         case pcmk_pacemakerd_reply_ping:
 114             return "ping";
 115         case pcmk_pacemakerd_reply_shutdown:
 116             return "shutdown";
 117         default:
 118             return "unknown";
 119     }
 120 }
 121 
 122 // \return Standard Pacemaker return code
 123 static int
 124 new_data(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126     struct pacemakerd_api_private_s *private = NULL;
 127 
 128     api->api_data = calloc(1, sizeof(struct pacemakerd_api_private_s));
 129 
 130     if (api->api_data == NULL) {
 131         return errno;
 132     }
 133 
 134     private = api->api_data;
 135     private->state = pcmk_pacemakerd_state_invalid;
 136     /* other as with cib, controld, ... we are addressing pacemakerd just
 137        from the local node -> pid is unique and thus sufficient as an ID
 138      */
 139     private->client_uuid = pcmk__getpid_s();
 140 
 141     return pcmk_rc_ok;
 142 }
 143 
 144 static void
 145 free_data(void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147     free(((struct pacemakerd_api_private_s *) data)->client_uuid);
 148     free(data);
 149 }
 150 
 151 // \return Standard Pacemaker return code
 152 static int
 153 post_connect(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 154 {
 155     struct pacemakerd_api_private_s *private = NULL;
 156 
 157     if (api->api_data == NULL) {
 158         return EINVAL;
 159     }
 160     private = api->api_data;
 161     private->state = pcmk_pacemakerd_state_invalid;
 162 
 163     return pcmk_rc_ok;
 164 }
 165 
 166 static void
 167 post_disconnect(pcmk_ipc_api_t *api)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169     struct pacemakerd_api_private_s *private = NULL;
 170 
 171     if (api->api_data == NULL) {
 172         return;
 173     }
 174     private = api->api_data;
 175     private->state = pcmk_pacemakerd_state_invalid;
 176 
 177     return;
 178 }
 179 
 180 static bool
 181 reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 182 {
 183     const char *command = crm_element_value(request, PCMK__XA_CRM_TASK);
 184 
 185     if (command == NULL) {
 186         return false;
 187     }
 188 
 189     // We only need to handle commands that functions in this file can send
 190     return pcmk__str_any_of(command, CRM_OP_PING, CRM_OP_QUIT, NULL);
 191 }
 192 
 193 static bool
 194 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 195 {
 196     crm_exit_t status = CRM_EX_OK;
 197     xmlNode *wrapper = NULL;
 198     xmlNode *msg_data = NULL;
 199     pcmk_pacemakerd_api_reply_t reply_data = {
 200         pcmk_pacemakerd_reply_unknown
 201     };
 202     const char *value = NULL;
 203     long long value_ll = 0;
 204 
 205     if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
 206         long long int ack_status = 0;
 207         const char *status = crm_element_value(reply, PCMK_XA_STATUS);
 208         int rc = pcmk__scan_ll(status, &ack_status, CRM_EX_OK);
 209 
 210         if (rc != pcmk_rc_ok) {
 211             crm_warn("Ack reply from %s has invalid " PCMK_XA_STATUS
 212                      " '%s' (bug?)",
 213                      pcmk_ipc_name(api, true), pcmk__s(status, ""));
 214         }
 215         return ack_status == CRM_EX_INDETERMINATE;
 216     }
 217 
 218     value = crm_element_value(reply, PCMK__XA_T);
 219     if (pcmk__parse_server(value) != pcmk_ipc_pacemakerd) {
 220         /* @COMPAT pacemakerd <3.0.0 sets PCMK__VALUE_CRMD as the message type,
 221          * so we can't enforce this check until we no longer support
 222          * Pacemaker Remote nodes connecting to cluster nodes older than that.
 223          */
 224         crm_trace("Message from %s has unexpected message type '%s' "
 225                   "(bug if not from pacemakerd <3.0.0)",
 226                   pcmk_ipc_name(api, true), pcmk__s(value, ""));
 227     }
 228 
 229     value = crm_element_value(reply, PCMK__XA_SUBT);
 230     if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
 231         crm_info("Unrecognizable message from %s: "
 232                  "message type '%s' not '" PCMK__VALUE_RESPONSE "'",
 233                  pcmk_ipc_name(api, true), pcmk__s(value, ""));
 234         status = CRM_EX_PROTOCOL;
 235         goto done;
 236     }
 237 
 238     if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) {
 239         crm_info("Unrecognizable message from %s: no reference",
 240                  pcmk_ipc_name(api, true));
 241         status = CRM_EX_PROTOCOL;
 242         goto done;
 243     }
 244 
 245     value = crm_element_value(reply, PCMK__XA_CRM_TASK);
 246 
 247     // Parse useful info from reply
 248     wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL);
 249     msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 250 
 251     crm_element_value_ll(msg_data, PCMK_XA_CRM_TIMESTAMP, &value_ll);
 252 
 253     if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) {
 254         reply_data.reply_type = pcmk_pacemakerd_reply_ping;
 255         reply_data.data.ping.state =
 256             pcmk_pacemakerd_api_daemon_state_text2enum(
 257                 crm_element_value(msg_data, PCMK__XA_PACEMAKERD_STATE));
 258         reply_data.data.ping.status =
 259             pcmk__str_eq(crm_element_value(msg_data, PCMK_XA_RESULT), "ok",
 260                          pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error;
 261         reply_data.data.ping.last_good = (value_ll < 0)? 0 : (time_t) value_ll;
 262         reply_data.data.ping.sys_from =
 263             crm_element_value(msg_data, PCMK__XA_CRM_SUBSYSTEM);
 264     } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) {
 265         const char *op_status = crm_element_value(msg_data, PCMK__XA_OP_STATUS);
 266 
 267         reply_data.reply_type = pcmk_pacemakerd_reply_shutdown;
 268         reply_data.data.shutdown.status = atoi(op_status);
 269     } else {
 270         crm_info("Unrecognizable message from %s: unknown command '%s'",
 271                  pcmk_ipc_name(api, true), pcmk__s(value, ""));
 272         status = CRM_EX_PROTOCOL;
 273         goto done;
 274     }
 275 
 276 done:
 277     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
 278     return false;
 279 }
 280 
 281 pcmk__ipc_methods_t *
 282 pcmk__pacemakerd_api_methods(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
 285 
 286     if (cmds != NULL) {
 287         cmds->new_data = new_data;
 288         cmds->free_data = free_data;
 289         cmds->post_connect = post_connect;
 290         cmds->reply_expected = reply_expected;
 291         cmds->dispatch = dispatch;
 292         cmds->post_disconnect = post_disconnect;
 293     }
 294     return cmds;
 295 }
 296 
 297 static int
 298 do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *task)
     /* [previous][next][first][last][top][bottom][index][help] */
 299 {
 300     pacemakerd_api_private_t *private;
 301     char *sender_system = NULL;
 302     xmlNode *cmd;
 303     int rc;
 304 
 305     if (api == NULL) {
 306         return EINVAL;
 307     }
 308 
 309     private = api->api_data;
 310     pcmk__assert(private != NULL);
 311 
 312     sender_system = crm_strdup_printf("%s_%s", private->client_uuid,
 313                                       pcmk__ipc_sys_name(ipc_name, "client"));
 314     cmd = pcmk__new_request(pcmk_ipc_pacemakerd, sender_system, NULL,
 315                             CRM_SYSTEM_MCP, task, NULL);
 316     free(sender_system);
 317 
 318     if (cmd) {
 319         rc = pcmk__send_ipc_request(api, cmd);
 320         if (rc != pcmk_rc_ok) {
 321             crm_debug("Couldn't send request to %s: %s rc=%d",
 322                       pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
 323         }
 324         pcmk__xml_free(cmd);
 325     } else {
 326         rc = ENOMSG;
 327     }
 328 
 329     return rc;
 330 }
 331 
 332 int
 333 pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 334 {
 335     return do_pacemakerd_api_call(api, ipc_name, CRM_OP_PING);
 336 }
 337 
 338 int
 339 pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341     return do_pacemakerd_api_call(api, ipc_name, CRM_OP_QUIT);
 342 }

/* [previous][next][first][last][top][bottom][index][help] */