Line data Source code
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 0 : create_request_adv(const char *task, xmlNode *msg_data,
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 0 : char *true_from = NULL;
48 0 : xmlNode *request = NULL;
49 0 : char *reference = crm_strdup_printf("%s-%s-%lld-%u",
50 : (task? task : "_empty_"),
51 : (sys_from? sys_from : "_empty_"),
52 0 : (long long) time(NULL), ref_counter++);
53 :
54 0 : if (uuid_from != NULL) {
55 0 : true_from = crm_strdup_printf("%s_%s", uuid_from,
56 : (sys_from? sys_from : "none"));
57 0 : } else if (sys_from != NULL) {
58 0 : true_from = strdup(sys_from);
59 : } else {
60 0 : 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 0 : request = pcmk__xe_create(NULL, __func__);
65 0 : crm_xml_add(request, PCMK_XA_ORIGIN, origin);
66 0 : crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_CRMD);
67 0 : crm_xml_add(request, PCMK_XA_VERSION, CRM_FEATURE_SET);
68 0 : crm_xml_add(request, PCMK__XA_SUBT, PCMK__VALUE_REQUEST);
69 0 : crm_xml_add(request, PCMK_XA_REFERENCE, reference);
70 0 : crm_xml_add(request, PCMK__XA_CRM_TASK, task);
71 0 : crm_xml_add(request, PCMK__XA_CRM_SYS_TO, sys_to);
72 0 : 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 0 : if (host_to != NULL && strlen(host_to) > 0) {
76 0 : crm_xml_add(request, PCMK__XA_CRM_HOST_TO, host_to);
77 : }
78 :
79 0 : if (msg_data != NULL) {
80 0 : xmlNode *wrapper = pcmk__xe_create(request, PCMK__XE_CRM_XML);
81 :
82 0 : pcmk__xml_copy(wrapper, msg_data);
83 : }
84 0 : free(reference);
85 0 : free(true_from);
86 :
87 0 : 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 0 : create_reply_adv(const xmlNode *original_request, xmlNode *xml_response_data,
105 : const char *origin)
106 : {
107 0 : xmlNode *reply = NULL;
108 :
109 0 : const char *host_from = crm_element_value(original_request, PCMK__XA_SRC);
110 0 : const char *sys_from = crm_element_value(original_request,
111 : PCMK__XA_CRM_SYS_FROM);
112 0 : const char *sys_to = crm_element_value(original_request,
113 : PCMK__XA_CRM_SYS_TO);
114 0 : const char *type = crm_element_value(original_request, PCMK__XA_SUBT);
115 0 : const char *operation = crm_element_value(original_request,
116 : PCMK__XA_CRM_TASK);
117 0 : const char *crm_msg_reference = crm_element_value(original_request,
118 : PCMK_XA_REFERENCE);
119 :
120 0 : if (type == NULL) {
121 0 : crm_err("Cannot create new_message, no message type in original message");
122 0 : CRM_ASSERT(type != NULL);
123 0 : return NULL;
124 : }
125 :
126 0 : 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 0 : crm_trace("Creating a reply for a non-request original message");
131 : }
132 :
133 0 : reply = pcmk__xe_create(NULL, __func__);
134 0 : crm_xml_add(reply, PCMK_XA_ORIGIN, origin);
135 0 : crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_CRMD);
136 0 : crm_xml_add(reply, PCMK_XA_VERSION, CRM_FEATURE_SET);
137 0 : crm_xml_add(reply, PCMK__XA_SUBT, PCMK__VALUE_RESPONSE);
138 0 : crm_xml_add(reply, PCMK_XA_REFERENCE, crm_msg_reference);
139 0 : crm_xml_add(reply, PCMK__XA_CRM_TASK, operation);
140 :
141 : /* since this is a reply, we reverse the from and to */
142 0 : crm_xml_add(reply, PCMK__XA_CRM_SYS_TO, sys_from);
143 0 : 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 0 : if (host_from != NULL && strlen(host_from) > 0) {
147 0 : crm_xml_add(reply, PCMK__XA_CRM_HOST_TO, host_from);
148 : }
149 :
150 0 : if (xml_response_data != NULL) {
151 0 : xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CRM_XML);
152 :
153 0 : pcmk__xml_copy(wrapper, xml_response_data);
154 : }
155 :
156 0 : 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 0 : pcmk__message_name(const char *name)
172 : {
173 0 : if (name == NULL) {
174 0 : return "unknown";
175 :
176 0 : } else if (!strcmp(name, "pacemaker-attrd")) {
177 0 : return "attrd";
178 :
179 0 : } else if (!strcmp(name, "pacemaker-based")) {
180 0 : return CRM_SYSTEM_CIB;
181 :
182 0 : } else if (!strcmp(name, "pacemaker-controld")) {
183 0 : return CRM_SYSTEM_CRMD;
184 :
185 0 : } else if (!strcmp(name, "pacemaker-execd")) {
186 0 : return CRM_SYSTEM_LRMD;
187 :
188 0 : } else if (!strcmp(name, "pacemaker-fenced")) {
189 0 : return "stonith-ng";
190 :
191 0 : } else if (!strcmp(name, "pacemaker-schedulerd")) {
192 0 : return CRM_SYSTEM_PENGINE;
193 :
194 : } else {
195 0 : 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 0 : pcmk__register_handlers(const pcmk__server_command_t handlers[])
214 : {
215 0 : GHashTable *commands = g_hash_table_new(g_str_hash, g_str_equal);
216 :
217 0 : if (handlers != NULL) {
218 : int i;
219 :
220 0 : for (i = 0; handlers[i].command != NULL; ++i) {
221 0 : g_hash_table_insert(commands, (gpointer) handlers[i].command,
222 0 : handlers[i].handler);
223 : }
224 0 : if (handlers[i].handler != NULL) {
225 : // g_str_hash() can't handle NULL, so use empty string for default
226 0 : g_hash_table_insert(commands, (gpointer) "", handlers[i].handler);
227 : }
228 : }
229 0 : 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 0 : pcmk__process_request(pcmk__request_t *request, GHashTable *handlers)
243 : {
244 0 : xmlNode *(*handler)(pcmk__request_t *request) = NULL;
245 :
246 0 : CRM_CHECK((request != NULL) && (request->op != NULL) && (handlers != NULL),
247 : return NULL);
248 :
249 0 : if (pcmk_is_set(request->flags, pcmk__request_sync)
250 0 : && (request->ipc_client != NULL)) {
251 0 : CRM_CHECK(request->ipc_client->request_id == request->ipc_id,
252 : return NULL);
253 : }
254 :
255 0 : handler = g_hash_table_lookup(handlers, request->op);
256 0 : if (handler == NULL) {
257 0 : handler = g_hash_table_lookup(handlers, ""); // Default handler
258 0 : if (handler == NULL) {
259 0 : 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 0 : return NULL;
263 : }
264 : }
265 :
266 0 : 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 0 : pcmk__reset_request(pcmk__request_t *request)
277 : {
278 0 : free(request->op);
279 0 : request->op = NULL;
280 :
281 0 : pcmk__reset_result(&(request->result));
282 0 : }
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)
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)
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
|