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. get_message_xml
  4. add_message_xml
  5. pcmk__message_name
  6. pcmk__register_handlers
  7. pcmk__process_request
  8. pcmk__reset_request

   1 /*
   2  * Copyright 2004-2022 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/msg_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 = create_xml_node(NULL, __func__);
  65     crm_xml_add(request, F_CRM_ORIGIN, origin);
  66     crm_xml_add(request, F_TYPE, T_CRM);
  67     crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET);
  68     crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST);
  69     crm_xml_add(request, F_CRM_REFERENCE, reference);
  70     crm_xml_add(request, F_CRM_TASK, task);
  71     crm_xml_add(request, F_CRM_SYS_TO, sys_to);
  72     crm_xml_add(request, F_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, F_CRM_HOST_TO, host_to);
  77     }
  78 
  79     if (msg_data != NULL) {
  80         add_message_xml(request, F_CRM_DATA, msg_data);
  81     }
  82     free(reference);
  83     free(true_from);
  84 
  85     return request;
  86 }
  87 
  88 /*!
  89  * \brief Create a Pacemaker reply (for IPC or cluster layer)
  90  *
  91  * \param[in] original_request   XML of request this is a reply to
  92  * \param[in] xml_response_data  XML to copy as data section of reply
  93  * \param[in] origin             Name of function that called this one
  94  *
  95  * \return XML of new reply
  96  *
  97  * \note This function should not be called directly, but via the
  98  *       create_reply() wrapper.
  99  * \note The caller is responsible for freeing the result using free_xml().
 100  */
 101 xmlNode *
 102 create_reply_adv(const xmlNode *original_request, xmlNode *xml_response_data,
     /* [previous][next][first][last][top][bottom][index][help] */
 103                  const char *origin)
 104 {
 105     xmlNode *reply = NULL;
 106 
 107     const char *host_from = crm_element_value(original_request, F_CRM_HOST_FROM);
 108     const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM);
 109     const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO);
 110     const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE);
 111     const char *operation = crm_element_value(original_request, F_CRM_TASK);
 112     const char *crm_msg_reference = crm_element_value(original_request, F_CRM_REFERENCE);
 113 
 114     if (type == NULL) {
 115         crm_err("Cannot create new_message, no message type in original message");
 116         CRM_ASSERT(type != NULL);
 117         return NULL;
 118 #if 0
 119     } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) {
 120         crm_err("Cannot create new_message, original message was not a request");
 121         return NULL;
 122 #endif
 123     }
 124     reply = create_xml_node(NULL, __func__);
 125     if (reply == NULL) {
 126         crm_err("Cannot create new_message, malloc failed");
 127         return NULL;
 128     }
 129 
 130     crm_xml_add(reply, F_CRM_ORIGIN, origin);
 131     crm_xml_add(reply, F_TYPE, T_CRM);
 132     crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET);
 133     crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE);
 134     crm_xml_add(reply, F_CRM_REFERENCE, crm_msg_reference);
 135     crm_xml_add(reply, F_CRM_TASK, operation);
 136 
 137     /* since this is a reply, we reverse the from and to */
 138     crm_xml_add(reply, F_CRM_SYS_TO, sys_from);
 139     crm_xml_add(reply, F_CRM_SYS_FROM, sys_to);
 140 
 141     /* HOSTTO will be ignored if it is to the DC anyway. */
 142     if (host_from != NULL && strlen(host_from) > 0) {
 143         crm_xml_add(reply, F_CRM_HOST_TO, host_from);
 144     }
 145 
 146     if (xml_response_data != NULL) {
 147         add_message_xml(reply, F_CRM_DATA, xml_response_data);
 148     }
 149 
 150     return reply;
 151 }
 152 
 153 xmlNode *
 154 get_message_xml(const xmlNode *msg, const char *field)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     return pcmk__xml_first_child(first_named_child(msg, field));
 157 }
 158 
 159 gboolean
 160 add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162     xmlNode *holder = create_xml_node(msg, field);
 163 
 164     add_node_copy(holder, xml);
 165     return TRUE;
 166 }
 167 
 168 /*!
 169  * \brief Get name to be used as identifier for cluster messages
 170  *
 171  * \param[in] name  Actual system name to check
 172  *
 173  * \return Non-NULL cluster message identifier corresponding to name
 174  *
 175  * \note The Pacemaker daemons were renamed in version 2.0.0, but the old names
 176  *       must continue to be used as the identifier for cluster messages, so
 177  *       that mixed-version clusters are possible during a rolling upgrade.
 178  */
 179 const char *
 180 pcmk__message_name(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     if (name == NULL) {
 183         return "unknown";
 184 
 185     } else if (!strcmp(name, "pacemaker-attrd")) {
 186         return "attrd";
 187 
 188     } else if (!strcmp(name, "pacemaker-based")) {
 189         return CRM_SYSTEM_CIB;
 190 
 191     } else if (!strcmp(name, "pacemaker-controld")) {
 192         return CRM_SYSTEM_CRMD;
 193 
 194     } else if (!strcmp(name, "pacemaker-execd")) {
 195         return CRM_SYSTEM_LRMD;
 196 
 197     } else if (!strcmp(name, "pacemaker-fenced")) {
 198         return "stonith-ng";
 199 
 200     } else if (!strcmp(name, "pacemaker-schedulerd")) {
 201         return CRM_SYSTEM_PENGINE;
 202 
 203     } else {
 204         return name;
 205     }
 206 }
 207 
 208 /*!
 209  * \internal
 210  * \brief Register handlers for server commands
 211  *
 212  * \param[in] handlers  Array of handler functions for supported server commands
 213  *                      (the final entry must have a NULL command name, and if
 214  *                      it has a handler it will be used as the default handler
 215  *                      for unrecognized commands)
 216  *
 217  * \return Newly created hash table with commands and handlers
 218  * \note The caller is responsible for freeing the return value with
 219  *       g_hash_table_destroy().
 220  */
 221 GHashTable *
 222 pcmk__register_handlers(const pcmk__server_command_t handlers[])
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224     GHashTable *commands = g_hash_table_new(g_str_hash, g_str_equal);
 225 
 226     if (handlers != NULL) {
 227         int i;
 228 
 229         for (i = 0; handlers[i].command != NULL; ++i) {
 230             g_hash_table_insert(commands, (gpointer) handlers[i].command,
 231                                 handlers[i].handler);
 232         }
 233         if (handlers[i].handler != NULL) {
 234             // g_str_hash() can't handle NULL, so use empty string for default
 235             g_hash_table_insert(commands, (gpointer) "", handlers[i].handler);
 236         }
 237     }
 238     return commands;
 239 }
 240 
 241 /*!
 242  * \internal
 243  * \brief Process an incoming request
 244  *
 245  * \param[in,out] request   Request to process
 246  * \param[in]     handlers  Command table created by pcmk__register_handlers()
 247  *
 248  * \return XML to send as reply (or NULL if no reply is needed)
 249  */
 250 xmlNode *
 251 pcmk__process_request(pcmk__request_t *request, GHashTable *handlers)
     /* [previous][next][first][last][top][bottom][index][help] */
 252 {
 253     xmlNode *(*handler)(pcmk__request_t *request) = NULL;
 254 
 255     CRM_CHECK((request != NULL) && (request->op != NULL) && (handlers != NULL),
 256               return NULL);
 257 
 258     if (pcmk_is_set(request->flags, pcmk__request_sync)
 259         && (request->ipc_client != NULL)) {
 260         CRM_CHECK(request->ipc_client->request_id == request->ipc_id,
 261                   return NULL);
 262     }
 263 
 264     handler = g_hash_table_lookup(handlers, request->op);
 265     if (handler == NULL) {
 266         handler = g_hash_table_lookup(handlers, ""); // Default handler
 267         if (handler == NULL) {
 268             crm_info("Ignoring %s request from %s %s with no handler",
 269                      request->op, pcmk__request_origin_type(request),
 270                      pcmk__request_origin(request));
 271             return NULL;
 272         }
 273     }
 274 
 275     return (*handler)(request);
 276 }
 277 
 278 /*!
 279  * \internal
 280  * \brief Free memory used within a request (but not the request itself)
 281  *
 282  * \param[in,out] request  Request to reset
 283  */
 284 void
 285 pcmk__reset_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287     free(request->op);
 288     request->op = NULL;
 289 
 290     pcmk__reset_result(&(request->result));
 291 }

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