1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdio.h>
#include <time.h> // time()
#include <sys/types.h>
#include <glib.h>
#include <libxml/tree.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
/*!
* \internal
* \brief Create message XML (for IPC or the cluster layer)
*
* Create standard, generic XML that can be used as a message sent via IPC or
* the cluster layer. Currently, not all IPC and cluster layer messaging uses
* this, but it should (eventually, keeping backward compatibility in mind).
*
* \param[in] origin Name of function that called this one (required)
* \param[in] server Server whose protocol defines message semantics
* \param[in] reply_to If NULL, create message as a request with a
* generated message ID, otherwise create message
* as a reply to this message ID
* \param[in] sender_system Sender's subsystem (required; this is an
* arbitrary string that may have meaning between
* the sender and recipient)
* \param[in] recipient_node If not NULL, add as message's recipient node
* (NULL typically indicates a broadcast message)
* \param[in] recipient_system If not NULL, add as message's recipient
* subsystem (this is an arbitrary string that may
* have meaning between the sender and recipient)
* \param[in] task Add as message's task (required)
* \param[in] data If not NULL, copy as message's data (callers
* should not add attributes to the returned
* message element, but instead pass any desired
* information here, though this is not always
* honored currently)
*
* \return Newly created message XML
*
* \note This function should usually not be called directly, but via the
* pcmk__new_message() wrapper.
* \note The caller is responsible for freeing the return value using
* \c pcmk__xml_free().
*/
xmlNode *
pcmk__new_message_as(const char *origin, enum pcmk_ipc_server server,
const char *reply_to, const char *sender_system,
const char *recipient_node, const char *recipient_system,
const char *task, xmlNode *data)
{
static unsigned int message_counter = 0U;
xmlNode *message = NULL;
char *message_id = NULL;
const char *subtype = PCMK__VALUE_RESPONSE;
CRM_CHECK(!pcmk__str_empty(origin)
&& !pcmk__str_empty(sender_system)
&& !pcmk__str_empty(task),
return NULL);
if (reply_to == NULL) {
subtype = PCMK__VALUE_REQUEST;
message_id = crm_strdup_printf("%s-%s-%llu-%u", task, sender_system,
(unsigned long long) time(NULL),
message_counter++);
reply_to = message_id;
}
message = pcmk__xe_create(NULL, PCMK__XE_MESSAGE);
pcmk__xe_set_props(message,
PCMK_XA_ORIGIN, origin,
PCMK__XA_T, pcmk__server_message_type(server),
PCMK__XA_SUBT, subtype,
PCMK_XA_VERSION, CRM_FEATURE_SET,
PCMK_XA_REFERENCE, reply_to,
PCMK__XA_CRM_SYS_FROM, sender_system,
PCMK__XA_CRM_HOST_TO, recipient_node,
PCMK__XA_CRM_SYS_TO, recipient_system,
PCMK__XA_CRM_TASK, task,
NULL);
if (data != NULL) {
xmlNode *wrapper = pcmk__xe_create(message, PCMK__XE_CRM_XML);
pcmk__xml_copy(wrapper, data);
}
free(message_id);
return message;
}
/*!
* \internal
* \brief Create a Pacemaker reply (for IPC or cluster layer)
*
* \param[in] origin Name of function that called this one
* \param[in] original_request XML of request being replied to
* \param[in] data If not NULL, copy as reply's data (callers
* should not add attributes to the returned
* message element, but instead pass any desired
* information here, though this is not always
* honored currently)
*
* \return Newly created reply XML
*
* \note This function should not be called directly, but via the
* pcmk__new_reply() wrapper.
* \note The caller is responsible for freeing the return value using
* \c pcmk__xml_free().
*/
xmlNode *
pcmk__new_reply_as(const char *origin, const xmlNode *original_request,
xmlNode *data)
{
const char *message_type = crm_element_value(original_request, PCMK__XA_T);
const char *host_from = crm_element_value(original_request, PCMK__XA_SRC);
const char *sys_from = crm_element_value(original_request,
PCMK__XA_CRM_SYS_FROM);
const char *sys_to = crm_element_value(original_request,
PCMK__XA_CRM_SYS_TO);
const char *type = crm_element_value(original_request, PCMK__XA_SUBT);
const char *operation = crm_element_value(original_request,
PCMK__XA_CRM_TASK);
const char *crm_msg_reference = crm_element_value(original_request,
PCMK_XA_REFERENCE);
enum pcmk_ipc_server server = pcmk__parse_server(message_type);
if (server == pcmk_ipc_unknown) {
/* @COMPAT Not all requests currently specify a message type, so use a
* default that preserves past behavior.
*
* @TODO Ensure all requests specify a message type, drop this check
* after we no longer support rolling upgrades or Pacemaker Remote
* connections involving versions before that.
*/
server = pcmk_ipc_controld;
}
if (type == NULL) {
crm_warn("Cannot reply to invalid message: No message type specified");
return NULL;
}
if (strcmp(type, PCMK__VALUE_REQUEST) != 0) {
/* Replies should only be generated for request messages, but it's possible
* we expect replies to other messages right now so this can't be enforced.
*/
crm_trace("Creating a reply for a non-request original message");
}
// Since this is a reply, we reverse the sender and recipient info
return pcmk__new_message_as(origin, server, crm_msg_reference, sys_to,
host_from, sys_from, operation, data);
}
/*!
* \internal
* \brief Register handlers for server commands
*
* \param[in] handlers Array of handler functions for supported server commands
* (the final entry must have a NULL command name, and if
* it has a handler it will be used as the default handler
* for unrecognized commands)
*
* \return Newly created hash table with commands and handlers
* \note The caller is responsible for freeing the return value with
* g_hash_table_destroy().
*/
GHashTable *
pcmk__register_handlers(const pcmk__server_command_t handlers[])
{
GHashTable *commands = g_hash_table_new(g_str_hash, g_str_equal);
if (handlers != NULL) {
int i;
for (i = 0; handlers[i].command != NULL; ++i) {
g_hash_table_insert(commands, (gpointer) handlers[i].command,
handlers[i].handler);
}
if (handlers[i].handler != NULL) {
// g_str_hash() can't handle NULL, so use empty string for default
g_hash_table_insert(commands, (gpointer) "", handlers[i].handler);
}
}
return commands;
}
/*!
* \internal
* \brief Process an incoming request
*
* \param[in,out] request Request to process
* \param[in] handlers Command table created by pcmk__register_handlers()
*
* \return XML to send as reply (or NULL if no reply is needed)
*/
xmlNode *
pcmk__process_request(pcmk__request_t *request, GHashTable *handlers)
{
xmlNode *(*handler)(pcmk__request_t *request) = NULL;
CRM_CHECK((request != NULL) && (request->op != NULL) && (handlers != NULL),
return NULL);
if (pcmk_is_set(request->flags, pcmk__request_sync)
&& (request->ipc_client != NULL)) {
CRM_CHECK(request->ipc_client->request_id == request->ipc_id,
return NULL);
}
handler = g_hash_table_lookup(handlers, request->op);
if (handler == NULL) {
handler = g_hash_table_lookup(handlers, ""); // Default handler
if (handler == NULL) {
crm_info("Ignoring %s request from %s %s with no handler",
request->op, pcmk__request_origin_type(request),
pcmk__request_origin(request));
return NULL;
}
}
return (*handler)(request);
}
/*!
* \internal
* \brief Free memory used within a request (but not the request itself)
*
* \param[in,out] request Request to reset
*/
void
pcmk__reset_request(pcmk__request_t *request)
{
free(request->op);
request->op = NULL;
pcmk__reset_result(&(request->result));
}
|