summaryrefslogtreecommitdiffstats
path: root/lib/common/messages.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common/messages.c')
-rw-r--r--lib/common/messages.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/lib/common/messages.c b/lib/common/messages.c
new file mode 100644
index 0000000..2c01eed
--- /dev/null
+++ b/lib/common/messages.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2004-2022 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 <sys/types.h>
+
+#include <glib.h>
+#include <libxml/tree.h>
+
+#include <crm/msg_xml.h>
+#include <crm/common/xml_internal.h>
+
+/*!
+ * \brief Create a Pacemaker request (for IPC or cluster layer)
+ *
+ * \param[in] task What to set as the request's task
+ * \param[in] msg_data What to add as the request's data contents
+ * \param[in] host_to What to set as the request's destination host
+ * \param[in] sys_to What to set as the request's destination system
+ * \param[in] sys_from If not NULL, set as request's origin system
+ * \param[in] uuid_from If not NULL, use in request's origin system
+ * \param[in] origin Name of function that called this one
+ *
+ * \return XML of new request
+ *
+ * \note One of sys_from or uuid_from must be non-NULL
+ * \note This function should not be called directly, but via the
+ * create_request() wrapper.
+ * \note The caller is responsible for freeing the result using free_xml().
+ */
+xmlNode *
+create_request_adv(const char *task, xmlNode *msg_data,
+ const char *host_to, const char *sys_to,
+ const char *sys_from, const char *uuid_from,
+ const char *origin)
+{
+ static uint ref_counter = 0;
+
+ char *true_from = NULL;
+ xmlNode *request = NULL;
+ char *reference = crm_strdup_printf("%s-%s-%lld-%u",
+ (task? task : "_empty_"),
+ (sys_from? sys_from : "_empty_"),
+ (long long) time(NULL), ref_counter++);
+
+ if (uuid_from != NULL) {
+ true_from = crm_strdup_printf("%s_%s", uuid_from,
+ (sys_from? sys_from : "none"));
+ } else if (sys_from != NULL) {
+ true_from = strdup(sys_from);
+ } else {
+ crm_err("Cannot create IPC request: No originating system specified");
+ }
+
+ // host_from will get set for us if necessary by the controller when routed
+ request = create_xml_node(NULL, __func__);
+ crm_xml_add(request, F_CRM_ORIGIN, origin);
+ crm_xml_add(request, F_TYPE, T_CRM);
+ crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST);
+ crm_xml_add(request, F_CRM_REFERENCE, reference);
+ crm_xml_add(request, F_CRM_TASK, task);
+ crm_xml_add(request, F_CRM_SYS_TO, sys_to);
+ crm_xml_add(request, F_CRM_SYS_FROM, true_from);
+
+ /* HOSTTO will be ignored if it is to the DC anyway. */
+ if (host_to != NULL && strlen(host_to) > 0) {
+ crm_xml_add(request, F_CRM_HOST_TO, host_to);
+ }
+
+ if (msg_data != NULL) {
+ add_message_xml(request, F_CRM_DATA, msg_data);
+ }
+ free(reference);
+ free(true_from);
+
+ return request;
+}
+
+/*!
+ * \brief Create a Pacemaker reply (for IPC or cluster layer)
+ *
+ * \param[in] original_request XML of request this is a reply to
+ * \param[in] xml_response_data XML to copy as data section of reply
+ * \param[in] origin Name of function that called this one
+ *
+ * \return XML of new reply
+ *
+ * \note This function should not be called directly, but via the
+ * create_reply() wrapper.
+ * \note The caller is responsible for freeing the result using free_xml().
+ */
+xmlNode *
+create_reply_adv(const xmlNode *original_request, xmlNode *xml_response_data,
+ const char *origin)
+{
+ xmlNode *reply = NULL;
+
+ const char *host_from = crm_element_value(original_request, F_CRM_HOST_FROM);
+ const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM);
+ const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO);
+ const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE);
+ const char *operation = crm_element_value(original_request, F_CRM_TASK);
+ const char *crm_msg_reference = crm_element_value(original_request, F_CRM_REFERENCE);
+
+ if (type == NULL) {
+ crm_err("Cannot create new_message, no message type in original message");
+ CRM_ASSERT(type != NULL);
+ return NULL;
+#if 0
+ } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) {
+ crm_err("Cannot create new_message, original message was not a request");
+ return NULL;
+#endif
+ }
+ reply = create_xml_node(NULL, __func__);
+ if (reply == NULL) {
+ crm_err("Cannot create new_message, malloc failed");
+ return NULL;
+ }
+
+ crm_xml_add(reply, F_CRM_ORIGIN, origin);
+ crm_xml_add(reply, F_TYPE, T_CRM);
+ crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE);
+ crm_xml_add(reply, F_CRM_REFERENCE, crm_msg_reference);
+ crm_xml_add(reply, F_CRM_TASK, operation);
+
+ /* since this is a reply, we reverse the from and to */
+ crm_xml_add(reply, F_CRM_SYS_TO, sys_from);
+ crm_xml_add(reply, F_CRM_SYS_FROM, sys_to);
+
+ /* HOSTTO will be ignored if it is to the DC anyway. */
+ if (host_from != NULL && strlen(host_from) > 0) {
+ crm_xml_add(reply, F_CRM_HOST_TO, host_from);
+ }
+
+ if (xml_response_data != NULL) {
+ add_message_xml(reply, F_CRM_DATA, xml_response_data);
+ }
+
+ return reply;
+}
+
+xmlNode *
+get_message_xml(const xmlNode *msg, const char *field)
+{
+ return pcmk__xml_first_child(first_named_child(msg, field));
+}
+
+gboolean
+add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
+{
+ xmlNode *holder = create_xml_node(msg, field);
+
+ add_node_copy(holder, xml);
+ return TRUE;
+}
+
+/*!
+ * \brief Get name to be used as identifier for cluster messages
+ *
+ * \param[in] name Actual system name to check
+ *
+ * \return Non-NULL cluster message identifier corresponding to name
+ *
+ * \note The Pacemaker daemons were renamed in version 2.0.0, but the old names
+ * must continue to be used as the identifier for cluster messages, so
+ * that mixed-version clusters are possible during a rolling upgrade.
+ */
+const char *
+pcmk__message_name(const char *name)
+{
+ if (name == NULL) {
+ return "unknown";
+
+ } else if (!strcmp(name, "pacemaker-attrd")) {
+ return "attrd";
+
+ } else if (!strcmp(name, "pacemaker-based")) {
+ return CRM_SYSTEM_CIB;
+
+ } else if (!strcmp(name, "pacemaker-controld")) {
+ return CRM_SYSTEM_CRMD;
+
+ } else if (!strcmp(name, "pacemaker-execd")) {
+ return CRM_SYSTEM_LRMD;
+
+ } else if (!strcmp(name, "pacemaker-fenced")) {
+ return "stonith-ng";
+
+ } else if (!strcmp(name, "pacemaker-schedulerd")) {
+ return CRM_SYSTEM_PENGINE;
+
+ } else {
+ return name;
+ }
+}
+
+/*!
+ * \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));
+}