root/daemons/attrd/attrd_messages.c

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

DEFINITIONS

This source file includes following definitions.
  1. is_sync_point_attr
  2. remove_sync_point_attribute
  3. remove_unsupported_sync_points
  4. handle_unknown_request
  5. handle_clear_failure_request
  6. handle_confirm_request
  7. handle_query_request
  8. handle_remove_request
  9. handle_refresh_request
  10. handle_sync_response_request
  11. handle_update_request
  12. attrd_register_handlers
  13. attrd_unregister_handlers
  14. attrd_handle_request
  15. attrd_send_protocol
  16. attrd_send_message

   1 /*
   2  * Copyright 2022-2025 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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <inttypes.h>   // PRIu32
  13 #include <glib.h>
  14 
  15 #include <crm/common/messages_internal.h>
  16 #include <crm/cluster/internal.h>   // pcmk__get_node()
  17 #include <crm/common/xml.h>
  18 
  19 #include "pacemaker-attrd.h"
  20 
  21 int minimum_protocol_version = -1;
  22 
  23 static GHashTable *attrd_handlers = NULL;
  24 
  25 static bool
  26 is_sync_point_attr(xmlAttrPtr attr, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     return pcmk__str_eq((const char *) attr->name, PCMK__XA_ATTR_SYNC_POINT, pcmk__str_none);
  29 }
  30 
  31 static int
  32 remove_sync_point_attribute(xmlNode *xml, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     pcmk__xe_remove_matching_attrs(xml, false, is_sync_point_attr, NULL);
  35     pcmk__xe_foreach_child(xml, PCMK_XE_OP, remove_sync_point_attribute, NULL);
  36     return pcmk_rc_ok;
  37 }
  38 
  39 /* Sync points on a multi-update IPC message to an attrd too old to support
  40  * multi-update messages won't work.  Strip the sync point attribute off here
  41  * so we don't pretend to support this situation and instead ACK the client
  42  * immediately.
  43  */
  44 static void
  45 remove_unsupported_sync_points(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     if (request->xml->children != NULL && !ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version) &&
  48         attrd_request_has_sync_point(request->xml)) {
  49         crm_warn("Ignoring sync point in request from %s because not all nodes support it",
  50                  pcmk__request_origin(request));
  51         remove_sync_point_attribute(request->xml, NULL);
  52     }
  53 }
  54 
  55 static xmlNode *
  56 handle_unknown_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  57 {
  58     crm_err("Unknown IPC request %s from %s %s",
  59             request->op, pcmk__request_origin_type(request),
  60             pcmk__request_origin(request));
  61     pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
  62                         "Unknown request type '%s' (bug?)", request->op);
  63     return NULL;
  64 }
  65 
  66 static xmlNode *
  67 handle_clear_failure_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69     if (request->peer != NULL) {
  70         /* It is not currently possible to receive this as a peer command,
  71          * but will be, if we one day enable propagating this operation.
  72          */
  73         attrd_peer_clear_failure(request);
  74         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
  75         return NULL;
  76     } else {
  77         remove_unsupported_sync_points(request);
  78 
  79         if (attrd_request_has_sync_point(request->xml)) {
  80             /* If this client supplied a sync point it wants to wait for, add it to
  81              * the wait list.  Clients on this list will not receive an ACK until
  82              * their sync point is hit which will result in the client stalled there
  83              * until it receives a response.
  84              *
  85              * All other clients will receive the expected response as normal.
  86              */
  87             attrd_add_client_to_waitlist(request);
  88 
  89         } else {
  90             /* If the client doesn't want to wait for a sync point, go ahead and send
  91              * the ACK immediately.  Otherwise, we'll send the ACK when the appropriate
  92              * sync point is reached.
  93              */
  94             attrd_send_ack(request->ipc_client, request->ipc_id,
  95                            request->ipc_flags);
  96         }
  97 
  98         return attrd_client_clear_failure(request);
  99     }
 100 }
 101 
 102 static xmlNode *
 103 handle_confirm_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     if (request->peer != NULL) {
 106         int callid;
 107 
 108         crm_debug("Received confirmation from %s", request->peer);
 109 
 110         if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID,
 111                                   &callid) == -1) {
 112             pcmk__set_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
 113                              "Could not get callid from XML");
 114         } else {
 115             attrd_handle_confirmation(callid, request->peer);
 116         }
 117 
 118         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 119         return NULL;
 120     } else {
 121         return handle_unknown_request(request);
 122     }
 123 }
 124 
 125 static xmlNode *
 126 handle_query_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128     if (request->peer != NULL) {
 129         return handle_unknown_request(request);
 130     } else {
 131         return attrd_client_query(request);
 132     }
 133 }
 134 
 135 static xmlNode *
 136 handle_remove_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138     if (request->peer != NULL) {
 139         const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST);
 140         bool reap = false;
 141 
 142         if (pcmk__xe_get_bool_attr(request->xml, PCMK__XA_REAP,
 143                                    &reap) != pcmk_rc_ok) {
 144             reap = true; // Default to true for backward compatibility
 145         }
 146         attrd_peer_remove(host, reap, request->peer);
 147         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 148         return NULL;
 149     } else {
 150         return attrd_client_peer_remove(request);
 151     }
 152 }
 153 
 154 static xmlNode *
 155 handle_refresh_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157     if (request->peer != NULL) {
 158         return handle_unknown_request(request);
 159     } else {
 160         return attrd_client_refresh(request);
 161     }
 162 }
 163 
 164 static xmlNode *
 165 handle_sync_response_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167     if (request->ipc_client != NULL) {
 168         return handle_unknown_request(request);
 169     } else {
 170         if (request->peer != NULL) {
 171             pcmk__node_status_t *peer =
 172                 pcmk__get_node(0, request->peer, NULL,
 173                                pcmk__node_search_cluster_member);
 174             bool peer_won = attrd_check_for_new_writer(peer, request->xml);
 175 
 176             if (!pcmk__str_eq(peer->name, attrd_cluster->priv->node_name,
 177                               pcmk__str_casei)) {
 178                 attrd_peer_sync_response(peer, peer_won, request->xml);
 179             }
 180         }
 181 
 182         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 183         return NULL;
 184     }
 185 }
 186 
 187 static xmlNode *
 188 handle_update_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190     if (request->peer != NULL) {
 191         const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST);
 192         pcmk__node_status_t *peer =
 193             pcmk__get_node(0, request->peer, NULL,
 194                            pcmk__node_search_cluster_member);
 195 
 196         attrd_peer_update(peer, request->xml, host, false);
 197         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 198         return NULL;
 199 
 200     } else {
 201         remove_unsupported_sync_points(request);
 202 
 203         if (attrd_request_has_sync_point(request->xml)) {
 204             /* If this client supplied a sync point it wants to wait for, add it to
 205              * the wait list.  Clients on this list will not receive an ACK until
 206              * their sync point is hit which will result in the client stalled there
 207              * until it receives a response.
 208              *
 209              * All other clients will receive the expected response as normal.
 210              */
 211             attrd_add_client_to_waitlist(request);
 212 
 213         } else {
 214             /* If the client doesn't want to wait for a sync point, go ahead and send
 215              * the ACK immediately.  Otherwise, we'll send the ACK when the appropriate
 216              * sync point is reached.
 217              *
 218              * In the normal case, attrd_client_update can be called recursively which
 219              * makes where to send the ACK tricky.  Doing it here ensures the client
 220              * only ever receives one.
 221              */
 222             attrd_send_ack(request->ipc_client, request->ipc_id,
 223                            request->flags|crm_ipc_client_response);
 224         }
 225 
 226         return attrd_client_update(request);
 227     }
 228 }
 229 
 230 static void
 231 attrd_register_handlers(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 232 {
 233     pcmk__server_command_t handlers[] = {
 234         { PCMK__ATTRD_CMD_CLEAR_FAILURE, handle_clear_failure_request },
 235         { PCMK__ATTRD_CMD_CONFIRM, handle_confirm_request },
 236         { PCMK__ATTRD_CMD_PEER_REMOVE, handle_remove_request },
 237         { PCMK__ATTRD_CMD_QUERY, handle_query_request },
 238         { PCMK__ATTRD_CMD_REFRESH, handle_refresh_request },
 239         { PCMK__ATTRD_CMD_SYNC_RESPONSE, handle_sync_response_request },
 240         { PCMK__ATTRD_CMD_UPDATE, handle_update_request },
 241         { PCMK__ATTRD_CMD_UPDATE_DELAY, handle_update_request },
 242         { PCMK__ATTRD_CMD_UPDATE_BOTH, handle_update_request },
 243         { NULL, handle_unknown_request },
 244     };
 245 
 246     attrd_handlers = pcmk__register_handlers(handlers);
 247 }
 248 
 249 void
 250 attrd_unregister_handlers(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252     if (attrd_handlers != NULL) {
 253         g_hash_table_destroy(attrd_handlers);
 254         attrd_handlers = NULL;
 255     }
 256 }
 257 
 258 void
 259 attrd_handle_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 260 {
 261     xmlNode *reply = NULL;
 262     char *log_msg = NULL;
 263     const char *reason = NULL;
 264 
 265     if (attrd_handlers == NULL) {
 266         attrd_register_handlers();
 267     }
 268 
 269     reply = pcmk__process_request(request, attrd_handlers);
 270 
 271     if (reply != NULL) {
 272         crm_log_xml_trace(reply, "Reply");
 273 
 274         if (request->ipc_client != NULL) {
 275             pcmk__ipc_send_xml(request->ipc_client, request->ipc_id, reply,
 276                                request->ipc_flags);
 277         } else {
 278             crm_err("Not sending CPG reply to client");
 279         }
 280 
 281         pcmk__xml_free(reply);
 282     }
 283 
 284     reason = request->result.exit_reason;
 285     log_msg = crm_strdup_printf("Processed %s request from %s %s: %s%s%s%s",
 286                                 request->op, pcmk__request_origin_type(request),
 287                                 pcmk__request_origin(request),
 288                                 pcmk_exec_status_str(request->result.execution_status),
 289                                 (reason == NULL)? "" : " (",
 290                                 pcmk__s(reason, ""),
 291                                 (reason == NULL)? "" : ")");
 292 
 293     if (!pcmk__result_ok(&request->result)) {
 294         crm_warn("%s", log_msg);
 295     } else {
 296         crm_debug("%s", log_msg);
 297     }
 298 
 299     free(log_msg);
 300     pcmk__reset_request(request);
 301 }
 302 
 303 /*!
 304     \internal
 305     \brief Send or broadcast private attribute for local node with protocol version
 306 */
 307 void
 308 attrd_send_protocol(const pcmk__node_status_t *peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 309 {
 310     xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
 311 
 312     crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD);
 313     crm_xml_add(attrd_op, PCMK__XA_SRC, crm_system_name);
 314     crm_xml_add(attrd_op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 315     crm_xml_add(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL);
 316     crm_xml_add(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION);
 317     crm_xml_add_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1);
 318     crm_xml_add(attrd_op, PCMK__XA_ATTR_HOST, attrd_cluster->priv->node_name);
 319     crm_xml_add(attrd_op, PCMK__XA_ATTR_HOST_ID,
 320                 attrd_cluster->priv->node_xml_id);
 321 
 322     if (peer == NULL) {
 323         crm_debug("Broadcasting attrd protocol version %s for node %s[%" PRIu32
 324                   "]",
 325                   ATTRD_PROTOCOL_VERSION,
 326                   pcmk__s(attrd_cluster->priv->node_name, "unknown"),
 327                   attrd_cluster->priv->node_id);
 328 
 329     } else {
 330         crm_debug("Sending attrd protocol version %s for node %s[%" PRIu32
 331                   "] to node %s[%" PRIu32 "]",
 332                   ATTRD_PROTOCOL_VERSION,
 333                   pcmk__s(attrd_cluster->priv->node_name, "unknown"),
 334                   attrd_cluster->priv->node_id,
 335                   pcmk__s(peer->name, "unknown"),
 336                   peer->cluster_layer_id);
 337     }
 338 
 339     attrd_send_message(peer, attrd_op, false); /* ends up at attrd_peer_message() */
 340 
 341     pcmk__xml_free(attrd_op);
 342 }
 343 
 344 gboolean
 345 attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     const char *op = crm_element_value(data, PCMK_XA_TASK);
 348 
 349     crm_xml_add(data, PCMK__XA_T, PCMK__VALUE_ATTRD);
 350     crm_xml_add(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
 351 
 352     /* Request a confirmation from the destination peer node (which could
 353      * be all if node is NULL) that the message has been received and
 354      * acted upon.
 355      */
 356     if (!pcmk__str_eq(op, PCMK__ATTRD_CMD_CONFIRM, pcmk__str_none)) {
 357         pcmk__xe_set_bool_attr(data, PCMK__XA_CONFIRM, confirm);
 358     }
 359 
 360     attrd_xml_add_writer(data);
 361     return pcmk__cluster_send_message(node, pcmk_ipc_attrd, data);
 362 }

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