root/lib/common/messages.c

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

DEFINITIONS

This source file includes following definitions.
  1. create_request_adv
  2. create_reply_adv
  3. pcmk__message_name
  4. pcmk__register_handlers
  5. pcmk__process_request
  6. pcmk__reset_request
  7. add_message_xml
  8. get_message_xml

   1 /*
   2  * Copyright 2004-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 <stdio.h>
  13 #include <sys/types.h>
  14 
  15 #include <glib.h>
  16 #include <libxml/tree.h>
  17 
  18 #include <crm/common/xml.h>
  19 #include <crm/common/xml_internal.h>
  20 
  21 /*!
  22  * \brief Create a Pacemaker request (for IPC or cluster layer)
  23  *
  24  * \param[in] task          What to set as the request's task
  25  * \param[in] msg_data      What to add as the request's data contents
  26  * \param[in] host_to       What to set as the request's destination host
  27  * \param[in] sys_to        What to set as the request's destination system
  28  * \param[in] sys_from      If not NULL, set as request's origin system
  29  * \param[in] uuid_from     If not NULL, use in request's origin system
  30  * \param[in] origin        Name of function that called this one
  31  *
  32  * \return XML of new request
  33  *
  34  * \note One of sys_from or uuid_from must be non-NULL
  35  * \note This function should not be called directly, but via the
  36  *       create_request() wrapper.
  37  * \note The caller is responsible for freeing the result using free_xml().
  38  */
  39 xmlNode *
  40 create_request_adv(const char *task, xmlNode *msg_data,
     /* [previous][next][first][last][top][bottom][index][help] */
  41                    const char *host_to, const char *sys_to,
  42                    const char *sys_from, const char *uuid_from,
  43                    const char *origin)
  44 {
  45     static uint ref_counter = 0;
  46 
  47     char *true_from = NULL;
  48     xmlNode *request = NULL;
  49     char *reference = crm_strdup_printf("%s-%s-%lld-%u",
  50                                         (task? task : "_empty_"),
  51                                         (sys_from? sys_from : "_empty_"),
  52                                         (long long) time(NULL), ref_counter++);
  53 
  54     if (uuid_from != NULL) {
  55         true_from = crm_strdup_printf("%s_%s", uuid_from,
  56                                       (sys_from? sys_from : "none"));
  57     } else if (sys_from != NULL) {
  58         true_from = strdup(sys_from);
  59     } else {
  60         crm_err("Cannot create IPC request: No originating system specified");
  61     }
  62 
  63     // host_from will get set for us if necessary by the controller when routed
  64     request = pcmk__xe_create(NULL, __func__);
  65     crm_xml_add(request, PCMK_XA_ORIGIN, origin);
  66     crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_CRMD);
  67     crm_xml_add(request, PCMK_XA_VERSION, CRM_FEATURE_SET);
  68     crm_xml_add(request, PCMK__XA_SUBT, PCMK__VALUE_REQUEST);
  69     crm_xml_add(request, PCMK_XA_REFERENCE, reference);
  70     crm_xml_add(request, PCMK__XA_CRM_TASK, task);
  71     crm_xml_add(request, PCMK__XA_CRM_SYS_TO, sys_to);
  72     crm_xml_add(request, PCMK__XA_CRM_SYS_FROM, true_from);
  73 
  74     /* HOSTTO will be ignored if it is to the DC anyway. */
  75     if (host_to != NULL && strlen(host_to) > 0) {
  76         crm_xml_add(request, PCMK__XA_CRM_HOST_TO, host_to);
  77     }
  78 
  79     if (msg_data != NULL) {
  80         xmlNode *wrapper = pcmk__xe_create(request, PCMK__XE_CRM_XML);
  81 
  82         pcmk__xml_copy(wrapper, msg_data);
  83     }
  84     free(reference);
  85     free(true_from);
  86 
  87     return request;
  88 }
  89 
  90 /*!
  91  * \brief Create a Pacemaker reply (for IPC or cluster layer)
  92  *
  93  * \param[in] original_request   XML of request this is a reply to
  94  * \param[in] xml_response_data  XML to copy as data section of reply
  95  * \param[in] origin             Name of function that called this one
  96  *
  97  * \return XML of new reply
  98  *
  99  * \note This function should not be called directly, but via the
 100  *       create_reply() wrapper.
 101  * \note The caller is responsible for freeing the result using free_xml().
 102  */
 103 xmlNode *
 104 create_reply_adv(const xmlNode *original_request, xmlNode *xml_response_data,
     /* [previous][next][first][last][top][bottom][index][help] */
 105                  const char *origin)
 106 {
 107     xmlNode *reply = NULL;
 108 
 109     const char *host_from = crm_element_value(original_request, PCMK__XA_SRC);
 110     const char *sys_from = crm_element_value(original_request,
 111                                              PCMK__XA_CRM_SYS_FROM);
 112     const char *sys_to = crm_element_value(original_request,
 113                                            PCMK__XA_CRM_SYS_TO);
 114     const char *type = crm_element_value(original_request, PCMK__XA_SUBT);
 115     const char *operation = crm_element_value(original_request,
 116                                               PCMK__XA_CRM_TASK);
 117     const char *crm_msg_reference = crm_element_value(original_request,
 118                                                       PCMK_XA_REFERENCE);
 119 
 120     if (type == NULL) {
 121         crm_err("Cannot create new_message, no message type in original message");
 122         CRM_ASSERT(type != NULL);
 123         return NULL;
 124     }
 125 
 126     if (strcmp(type, PCMK__VALUE_REQUEST) != 0) {
 127         /* Replies should only be generated for request messages, but it's possible
 128          * we expect replies to other messages right now so this can't be enforced.
 129          */
 130         crm_trace("Creating a reply for a non-request original message");
 131     }
 132 
 133     reply = pcmk__xe_create(NULL, __func__);
 134     crm_xml_add(reply, PCMK_XA_ORIGIN, origin);
 135     crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_CRMD);
 136     crm_xml_add(reply, PCMK_XA_VERSION, CRM_FEATURE_SET);
 137     crm_xml_add(reply, PCMK__XA_SUBT, PCMK__VALUE_RESPONSE);
 138     crm_xml_add(reply, PCMK_XA_REFERENCE, crm_msg_reference);
 139     crm_xml_add(reply, PCMK__XA_CRM_TASK, operation);
 140 
 141     /* since this is a reply, we reverse the from and to */
 142     crm_xml_add(reply, PCMK__XA_CRM_SYS_TO, sys_from);
 143     crm_xml_add(reply, PCMK__XA_CRM_SYS_FROM, sys_to);
 144 
 145     /* HOSTTO will be ignored if it is to the DC anyway. */
 146     if (host_from != NULL && strlen(host_from) > 0) {
 147         crm_xml_add(reply, PCMK__XA_CRM_HOST_TO, host_from);
 148     }
 149 
 150     if (xml_response_data != NULL) {
 151         xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CRM_XML);
 152 
 153         pcmk__xml_copy(wrapper, xml_response_data);
 154     }
 155 
 156     return reply;
 157 }
 158 
 159 /*!
 160  * \brief Get name to be used as identifier for cluster messages
 161  *
 162  * \param[in] name  Actual system name to check
 163  *
 164  * \return Non-NULL cluster message identifier corresponding to name
 165  *
 166  * \note The Pacemaker daemons were renamed in version 2.0.0, but the old names
 167  *       must continue to be used as the identifier for cluster messages, so
 168  *       that mixed-version clusters are possible during a rolling upgrade.
 169  */
 170 const char *
 171 pcmk__message_name(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 172 {
 173     if (name == NULL) {
 174         return "unknown";
 175 
 176     } else if (!strcmp(name, "pacemaker-attrd")) {
 177         return "attrd";
 178 
 179     } else if (!strcmp(name, "pacemaker-based")) {
 180         return CRM_SYSTEM_CIB;
 181 
 182     } else if (!strcmp(name, "pacemaker-controld")) {
 183         return CRM_SYSTEM_CRMD;
 184 
 185     } else if (!strcmp(name, "pacemaker-execd")) {
 186         return CRM_SYSTEM_LRMD;
 187 
 188     } else if (!strcmp(name, "pacemaker-fenced")) {
 189         return "stonith-ng";
 190 
 191     } else if (!strcmp(name, "pacemaker-schedulerd")) {
 192         return CRM_SYSTEM_PENGINE;
 193 
 194     } else {
 195         return name;
 196     }
 197 }
 198 
 199 /*!
 200  * \internal
 201  * \brief Register handlers for server commands
 202  *
 203  * \param[in] handlers  Array of handler functions for supported server commands
 204  *                      (the final entry must have a NULL command name, and if
 205  *                      it has a handler it will be used as the default handler
 206  *                      for unrecognized commands)
 207  *
 208  * \return Newly created hash table with commands and handlers
 209  * \note The caller is responsible for freeing the return value with
 210  *       g_hash_table_destroy().
 211  */
 212 GHashTable *
 213 pcmk__register_handlers(const pcmk__server_command_t handlers[])
     /* [previous][next][first][last][top][bottom][index][help] */
 214 {
 215     GHashTable *commands = g_hash_table_new(g_str_hash, g_str_equal);
 216 
 217     if (handlers != NULL) {
 218         int i;
 219 
 220         for (i = 0; handlers[i].command != NULL; ++i) {
 221             g_hash_table_insert(commands, (gpointer) handlers[i].command,
 222                                 handlers[i].handler);
 223         }
 224         if (handlers[i].handler != NULL) {
 225             // g_str_hash() can't handle NULL, so use empty string for default
 226             g_hash_table_insert(commands, (gpointer) "", handlers[i].handler);
 227         }
 228     }
 229     return commands;
 230 }
 231 
 232 /*!
 233  * \internal
 234  * \brief Process an incoming request
 235  *
 236  * \param[in,out] request   Request to process
 237  * \param[in]     handlers  Command table created by pcmk__register_handlers()
 238  *
 239  * \return XML to send as reply (or NULL if no reply is needed)
 240  */
 241 xmlNode *
 242 pcmk__process_request(pcmk__request_t *request, GHashTable *handlers)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244     xmlNode *(*handler)(pcmk__request_t *request) = NULL;
 245 
 246     CRM_CHECK((request != NULL) && (request->op != NULL) && (handlers != NULL),
 247               return NULL);
 248 
 249     if (pcmk_is_set(request->flags, pcmk__request_sync)
 250         && (request->ipc_client != NULL)) {
 251         CRM_CHECK(request->ipc_client->request_id == request->ipc_id,
 252                   return NULL);
 253     }
 254 
 255     handler = g_hash_table_lookup(handlers, request->op);
 256     if (handler == NULL) {
 257         handler = g_hash_table_lookup(handlers, ""); // Default handler
 258         if (handler == NULL) {
 259             crm_info("Ignoring %s request from %s %s with no handler",
 260                      request->op, pcmk__request_origin_type(request),
 261                      pcmk__request_origin(request));
 262             return NULL;
 263         }
 264     }
 265 
 266     return (*handler)(request);
 267 }
 268 
 269 /*!
 270  * \internal
 271  * \brief Free memory used within a request (but not the request itself)
 272  *
 273  * \param[in,out] request  Request to reset
 274  */
 275 void
 276 pcmk__reset_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 277 {
 278     free(request->op);
 279     request->op = NULL;
 280 
 281     pcmk__reset_result(&(request->result));
 282 }
 283 
 284 // Deprecated functions kept only for backward API compatibility
 285 // LCOV_EXCL_START
 286 
 287 #include <crm/common/xml_compat.h>
 288 
 289 gboolean
 290 add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 291 {
 292     xmlNode *holder = pcmk__xe_create(msg, field);
 293 
 294     pcmk__xml_copy(holder, xml);
 295     return TRUE;
 296 }
 297 
 298 xmlNode *
 299 get_message_xml(const xmlNode *msg, const char *field)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301     xmlNode *child = pcmk__xe_first_child(msg, field, NULL, NULL);
 302 
 303     return pcmk__xe_first_child(child, NULL, NULL, NULL);
 304 }
 305 
 306 // LCOV_EXCL_STOP
 307 // End deprecated API

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