Line data Source code
1 : /*
2 : * Copyright 2020-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 <stdbool.h>
14 : #include <errno.h>
15 : #include <libxml/tree.h>
16 :
17 : #include <crm/crm.h>
18 : #include <crm/common/xml.h>
19 : #include <crm/common/ipc.h>
20 : #include <crm/common/ipc_internal.h>
21 : #include <crm/common/ipc_controld.h>
22 : #include "crmcommon_private.h"
23 :
24 : struct controld_api_private_s {
25 : char *client_uuid;
26 : unsigned int replies_expected;
27 : };
28 :
29 : /*!
30 : * \internal
31 : * \brief Get a string representation of a controller API reply type
32 : *
33 : * \param[in] reply Controller API reply type
34 : *
35 : * \return String representation of a controller API reply type
36 : */
37 : const char *
38 0 : pcmk__controld_api_reply2str(enum pcmk_controld_api_reply reply)
39 : {
40 0 : switch (reply) {
41 0 : case pcmk_controld_reply_reprobe:
42 0 : return "reprobe";
43 0 : case pcmk_controld_reply_info:
44 0 : return "info";
45 0 : case pcmk_controld_reply_resource:
46 0 : return "resource";
47 0 : case pcmk_controld_reply_ping:
48 0 : return "ping";
49 0 : case pcmk_controld_reply_nodes:
50 0 : return "nodes";
51 0 : default:
52 0 : return "unknown";
53 : }
54 : }
55 :
56 : // \return Standard Pacemaker return code
57 : static int
58 0 : new_data(pcmk_ipc_api_t *api)
59 : {
60 0 : struct controld_api_private_s *private = NULL;
61 :
62 0 : api->api_data = calloc(1, sizeof(struct controld_api_private_s));
63 :
64 0 : if (api->api_data == NULL) {
65 0 : return errno;
66 : }
67 :
68 0 : private = api->api_data;
69 :
70 : /* This is set to the PID because that's how it was always done, but PIDs
71 : * are not unique because clients can be remote. The value appears to be
72 : * unused other than as part of PCMK__XA_CRM_SYS_FROM in IPC requests, which
73 : * is only compared against the internal system names (CRM_SYSTEM_TENGINE,
74 : * etc.), so it shouldn't be a problem.
75 : */
76 0 : private->client_uuid = pcmk__getpid_s();
77 :
78 : /* @TODO Implement a call ID model similar to the CIB, executor, and fencer
79 : * IPC APIs, so that requests and replies can be matched, and
80 : * duplicate replies can be discarded.
81 : */
82 0 : return pcmk_rc_ok;
83 : }
84 :
85 : static void
86 0 : free_data(void *data)
87 : {
88 0 : free(((struct controld_api_private_s *) data)->client_uuid);
89 0 : free(data);
90 0 : }
91 :
92 : // \return Standard Pacemaker return code
93 : static int
94 0 : post_connect(pcmk_ipc_api_t *api)
95 : {
96 : /* The controller currently requires clients to register via a hello
97 : * request, but does not reply back.
98 : */
99 0 : struct controld_api_private_s *private = api->api_data;
100 0 : const char *client_name = crm_system_name? crm_system_name : "client";
101 : xmlNode *hello;
102 : int rc;
103 :
104 0 : hello = create_hello_message(private->client_uuid, client_name,
105 : PCMK__CONTROLD_API_MAJOR,
106 : PCMK__CONTROLD_API_MINOR);
107 0 : rc = pcmk__send_ipc_request(api, hello);
108 0 : free_xml(hello);
109 0 : if (rc != pcmk_rc_ok) {
110 0 : crm_info("Could not send IPC hello to %s: %s " CRM_XS " rc=%s",
111 : pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
112 : } else {
113 0 : crm_debug("Sent IPC hello to %s", pcmk_ipc_name(api, true));
114 : }
115 0 : return rc;
116 : }
117 :
118 : static void
119 0 : set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
120 : {
121 0 : data->reply_type = pcmk_controld_reply_info;
122 0 : if (msg_data == NULL) {
123 0 : return;
124 : }
125 0 : data->data.node_info.have_quorum =
126 0 : pcmk__xe_attr_is_true(msg_data, PCMK_XA_HAVE_QUORUM);
127 0 : data->data.node_info.is_remote =
128 0 : pcmk__xe_attr_is_true(msg_data, PCMK_XA_REMOTE_NODE);
129 :
130 : /* Integer node_info.id is currently valid only for Corosync nodes.
131 : *
132 : * @TODO: Improve handling after crm_node_t is refactored to handle layer-
133 : * specific data better.
134 : */
135 0 : crm_element_value_int(msg_data, PCMK_XA_ID, &(data->data.node_info.id));
136 :
137 0 : data->data.node_info.uuid = crm_element_value(msg_data, PCMK_XA_ID);
138 0 : data->data.node_info.uname = crm_element_value(msg_data, PCMK_XA_UNAME);
139 0 : data->data.node_info.state = crm_element_value(msg_data, PCMK_XA_CRMD);
140 : }
141 :
142 : static void
143 0 : set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
144 : {
145 0 : data->reply_type = pcmk_controld_reply_ping;
146 0 : if (msg_data == NULL) {
147 0 : return;
148 : }
149 0 : data->data.ping.sys_from = crm_element_value(msg_data,
150 : PCMK__XA_CRM_SUBSYSTEM);
151 0 : data->data.ping.fsa_state = crm_element_value(msg_data,
152 : PCMK__XA_CRMD_STATE);
153 0 : data->data.ping.result = crm_element_value(msg_data, PCMK_XA_RESULT);
154 : }
155 :
156 : static void
157 0 : set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
158 : {
159 : pcmk_controld_api_node_t *node_info;
160 :
161 0 : data->reply_type = pcmk_controld_reply_nodes;
162 0 : for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
163 : NULL);
164 0 : node != NULL; node = pcmk__xe_next_same(node)) {
165 :
166 0 : long long id_ll = 0;
167 :
168 0 : node_info = pcmk__assert_alloc(1, sizeof(pcmk_controld_api_node_t));
169 0 : crm_element_value_ll(node, PCMK_XA_ID, &id_ll);
170 0 : if (id_ll > 0) {
171 0 : node_info->id = id_ll;
172 : }
173 0 : node_info->uname = crm_element_value(node, PCMK_XA_UNAME);
174 0 : node_info->state = crm_element_value(node, PCMK__XA_IN_CCM);
175 0 : data->data.nodes = g_list_prepend(data->data.nodes, node_info);
176 : }
177 0 : }
178 :
179 : static bool
180 0 : reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
181 : {
182 : // We only need to handle commands that API functions can send
183 0 : return pcmk__str_any_of(crm_element_value(request, PCMK__XA_CRM_TASK),
184 : PCMK__CONTROLD_CMD_NODES,
185 : CRM_OP_LRM_DELETE,
186 : CRM_OP_LRM_FAIL,
187 : CRM_OP_NODE_INFO,
188 : CRM_OP_PING,
189 : CRM_OP_REPROBE,
190 : CRM_OP_RM_NODE_CACHE,
191 : NULL);
192 : }
193 :
194 : static bool
195 0 : dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
196 : {
197 0 : struct controld_api_private_s *private = api->api_data;
198 0 : crm_exit_t status = CRM_EX_OK;
199 0 : xmlNode *wrapper = NULL;
200 0 : xmlNode *msg_data = NULL;
201 0 : const char *value = NULL;
202 0 : pcmk_controld_api_reply_t reply_data = {
203 : pcmk_controld_reply_unknown, NULL, NULL,
204 : };
205 :
206 0 : if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
207 : /* ACKs are trivial responses that do not count toward expected replies,
208 : * and do not have all the fields that validation requires, so skip that
209 : * processing.
210 : */
211 0 : return private->replies_expected > 0;
212 : }
213 :
214 0 : if (private->replies_expected > 0) {
215 0 : private->replies_expected--;
216 : }
217 :
218 : // Do some basic validation of the reply
219 :
220 : /* @TODO We should be able to verify that value is always a response, but
221 : * currently the controller doesn't always properly set the type. Even
222 : * if we fix the controller, we'll still need to handle replies from
223 : * old versions (feature set could be used to differentiate).
224 : */
225 0 : value = crm_element_value(reply, PCMK__XA_SUBT);
226 0 : if (!pcmk__str_any_of(value, PCMK__VALUE_REQUEST, PCMK__VALUE_RESPONSE,
227 : NULL)) {
228 0 : crm_info("Unrecognizable message from controller: "
229 : "invalid message type '%s'", pcmk__s(value, ""));
230 0 : status = CRM_EX_PROTOCOL;
231 0 : goto done;
232 : }
233 :
234 0 : if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) {
235 0 : crm_info("Unrecognizable message from controller: no reference");
236 0 : status = CRM_EX_PROTOCOL;
237 0 : goto done;
238 : }
239 :
240 0 : value = crm_element_value(reply, PCMK__XA_CRM_TASK);
241 0 : if (pcmk__str_empty(value)) {
242 0 : crm_info("Unrecognizable message from controller: no command name");
243 0 : status = CRM_EX_PROTOCOL;
244 0 : goto done;
245 : }
246 :
247 : // Parse useful info from reply
248 :
249 0 : reply_data.feature_set = crm_element_value(reply, PCMK_XA_VERSION);
250 0 : reply_data.host_from = crm_element_value(reply, PCMK__XA_SRC);
251 :
252 0 : wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL);
253 0 : msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
254 :
255 0 : if (!strcmp(value, CRM_OP_REPROBE)) {
256 0 : reply_data.reply_type = pcmk_controld_reply_reprobe;
257 :
258 0 : } else if (!strcmp(value, CRM_OP_NODE_INFO)) {
259 0 : set_node_info_data(&reply_data, msg_data);
260 :
261 0 : } else if (!strcmp(value, CRM_OP_INVOKE_LRM)) {
262 0 : reply_data.reply_type = pcmk_controld_reply_resource;
263 0 : reply_data.data.resource.node_state = msg_data;
264 :
265 0 : } else if (!strcmp(value, CRM_OP_PING)) {
266 0 : set_ping_data(&reply_data, msg_data);
267 :
268 0 : } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) {
269 0 : set_nodes_data(&reply_data, msg_data);
270 :
271 : } else {
272 0 : crm_info("Unrecognizable message from controller: unknown command '%s'",
273 : value);
274 0 : status = CRM_EX_PROTOCOL;
275 : }
276 :
277 0 : done:
278 0 : pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
279 :
280 : // Free any reply data that was allocated
281 0 : if (pcmk__str_eq(value, PCMK__CONTROLD_CMD_NODES, pcmk__str_casei)) {
282 0 : g_list_free_full(reply_data.data.nodes, free);
283 : }
284 :
285 0 : return false; // No further replies needed
286 : }
287 :
288 : pcmk__ipc_methods_t *
289 0 : pcmk__controld_api_methods(void)
290 : {
291 0 : pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
292 :
293 0 : if (cmds != NULL) {
294 0 : cmds->new_data = new_data;
295 0 : cmds->free_data = free_data;
296 0 : cmds->post_connect = post_connect;
297 0 : cmds->reply_expected = reply_expected;
298 0 : cmds->dispatch = dispatch;
299 : }
300 0 : return cmds;
301 : }
302 :
303 : /*!
304 : * \internal
305 : * \brief Create XML for a controller IPC request
306 : *
307 : * \param[in] api Controller connection
308 : * \param[in] op Controller IPC command name
309 : * \param[in] node Node name to set as destination host
310 : * \param[in] msg_data XML to attach to request as message data
311 : *
312 : * \return Newly allocated XML for request
313 : */
314 : static xmlNode *
315 0 : create_controller_request(const pcmk_ipc_api_t *api, const char *op,
316 : const char *node, xmlNode *msg_data)
317 : {
318 0 : struct controld_api_private_s *private = NULL;
319 0 : const char *sys_to = NULL;
320 :
321 0 : if (api == NULL) {
322 0 : return NULL;
323 : }
324 0 : private = api->api_data;
325 0 : if ((node == NULL) && !strcmp(op, CRM_OP_PING)) {
326 0 : sys_to = CRM_SYSTEM_DC;
327 : } else {
328 0 : sys_to = CRM_SYSTEM_CRMD;
329 : }
330 0 : return create_request(op, msg_data, node, sys_to,
331 : (crm_system_name? crm_system_name : "client"),
332 : private->client_uuid);
333 : }
334 :
335 : // \return Standard Pacemaker return code
336 : static int
337 0 : send_controller_request(pcmk_ipc_api_t *api, const xmlNode *request,
338 : bool reply_is_expected)
339 : {
340 0 : if (crm_element_value(request, PCMK_XA_REFERENCE) == NULL) {
341 0 : return EINVAL;
342 : }
343 0 : if (reply_is_expected) {
344 0 : struct controld_api_private_s *private = api->api_data;
345 :
346 0 : private->replies_expected++;
347 : }
348 0 : return pcmk__send_ipc_request(api, request);
349 : }
350 :
351 : static xmlNode *
352 0 : create_reprobe_message_data(const char *target_node, const char *router_node)
353 : {
354 : xmlNode *msg_data;
355 :
356 0 : msg_data = pcmk__xe_create(NULL, "data_for_" CRM_OP_REPROBE);
357 0 : crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node);
358 0 : if ((router_node != NULL) && !pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
359 0 : crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node);
360 : }
361 0 : return msg_data;
362 : }
363 :
364 : /*!
365 : * \brief Send a reprobe controller operation
366 : *
367 : * \param[in,out] api Controller connection
368 : * \param[in] target_node Name of node to reprobe
369 : * \param[in] router_node Router node for host
370 : *
371 : * \return Standard Pacemaker return code
372 : * \note Event callback will get a reply of type pcmk_controld_reply_reprobe.
373 : */
374 : int
375 0 : pcmk_controld_api_reprobe(pcmk_ipc_api_t *api, const char *target_node,
376 : const char *router_node)
377 : {
378 : xmlNode *request;
379 : xmlNode *msg_data;
380 0 : int rc = pcmk_rc_ok;
381 :
382 0 : if (api == NULL) {
383 0 : return EINVAL;
384 : }
385 0 : if (router_node == NULL) {
386 0 : router_node = target_node;
387 : }
388 0 : crm_debug("Sending %s IPC request to reprobe %s via %s",
389 : pcmk_ipc_name(api, true), pcmk__s(target_node, "local node"),
390 : pcmk__s(router_node, "local node"));
391 0 : msg_data = create_reprobe_message_data(target_node, router_node);
392 0 : request = create_controller_request(api, CRM_OP_REPROBE, router_node,
393 : msg_data);
394 0 : rc = send_controller_request(api, request, true);
395 0 : free_xml(msg_data);
396 0 : free_xml(request);
397 0 : return rc;
398 : }
399 :
400 : /*!
401 : * \brief Send a "node info" controller operation
402 : *
403 : * \param[in,out] api Controller connection
404 : * \param[in] nodeid ID of node to get info for (or 0 for local node)
405 : *
406 : * \return Standard Pacemaker return code
407 : * \note Event callback will get a reply of type pcmk_controld_reply_info.
408 : */
409 : int
410 0 : pcmk_controld_api_node_info(pcmk_ipc_api_t *api, uint32_t nodeid)
411 : {
412 : xmlNode *request;
413 0 : int rc = pcmk_rc_ok;
414 :
415 0 : request = create_controller_request(api, CRM_OP_NODE_INFO, NULL, NULL);
416 0 : if (request == NULL) {
417 0 : return EINVAL;
418 : }
419 0 : if (nodeid > 0) {
420 0 : crm_xml_set_id(request, "%lu", (unsigned long) nodeid);
421 : }
422 :
423 0 : rc = send_controller_request(api, request, true);
424 0 : free_xml(request);
425 0 : return rc;
426 : }
427 :
428 : /*!
429 : * \brief Ask the controller for status
430 : *
431 : * \param[in,out] api Controller connection
432 : * \param[in] node_name Name of node whose status is desired (NULL for DC)
433 : *
434 : * \return Standard Pacemaker return code
435 : * \note Event callback will get a reply of type pcmk_controld_reply_ping.
436 : */
437 : int
438 0 : pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
439 : {
440 : xmlNode *request;
441 0 : int rc = pcmk_rc_ok;
442 :
443 0 : request = create_controller_request(api, CRM_OP_PING, node_name, NULL);
444 0 : if (request == NULL) {
445 0 : return EINVAL;
446 : }
447 0 : rc = send_controller_request(api, request, true);
448 0 : free_xml(request);
449 0 : return rc;
450 : }
451 :
452 : /*!
453 : * \brief Ask the controller for cluster information
454 : *
455 : * \param[in,out] api Controller connection
456 : *
457 : * \return Standard Pacemaker return code
458 : * \note Event callback will get a reply of type pcmk_controld_reply_nodes.
459 : */
460 : int
461 0 : pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api)
462 : {
463 : xmlNode *request;
464 0 : int rc = EINVAL;
465 :
466 0 : request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL,
467 : NULL);
468 0 : if (request != NULL) {
469 0 : rc = send_controller_request(api, request, true);
470 0 : free_xml(request);
471 : }
472 0 : return rc;
473 : }
474 :
475 : // \return Standard Pacemaker return code
476 : static int
477 0 : controller_resource_op(pcmk_ipc_api_t *api, const char *op,
478 : const char *target_node, const char *router_node,
479 : bool cib_only, const char *rsc_id,
480 : const char *rsc_long_id, const char *standard,
481 : const char *provider, const char *type)
482 : {
483 0 : int rc = pcmk_rc_ok;
484 : char *key;
485 : xmlNode *request, *msg_data, *xml_rsc, *params;
486 :
487 0 : if (api == NULL) {
488 0 : return EINVAL;
489 : }
490 0 : if (router_node == NULL) {
491 0 : router_node = target_node;
492 : }
493 :
494 0 : msg_data = pcmk__xe_create(NULL, PCMK__XE_RSC_OP);
495 :
496 : /* The controller logs the transition key from resource op requests, so we
497 : * need to have *something* for it.
498 : * @TODO don't use "crm-resource"
499 : */
500 0 : key = pcmk__transition_key(0, getpid(), 0,
501 : "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx");
502 0 : crm_xml_add(msg_data, PCMK__XA_TRANSITION_KEY, key);
503 0 : free(key);
504 :
505 0 : crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node);
506 0 : if (!pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
507 0 : crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node);
508 : }
509 :
510 0 : if (cib_only) {
511 : // Indicate that only the CIB needs to be cleaned
512 0 : crm_xml_add(msg_data, PCMK__XA_MODE, PCMK__VALUE_CIB);
513 : }
514 :
515 0 : xml_rsc = pcmk__xe_create(msg_data, PCMK_XE_PRIMITIVE);
516 0 : crm_xml_add(xml_rsc, PCMK_XA_ID, rsc_id);
517 0 : crm_xml_add(xml_rsc, PCMK__XA_LONG_ID, rsc_long_id);
518 0 : crm_xml_add(xml_rsc, PCMK_XA_CLASS, standard);
519 0 : crm_xml_add(xml_rsc, PCMK_XA_PROVIDER, provider);
520 0 : crm_xml_add(xml_rsc, PCMK_XA_TYPE, type);
521 :
522 0 : params = pcmk__xe_create(msg_data, PCMK__XE_ATTRIBUTES);
523 0 : crm_xml_add(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
524 :
525 : // The controller parses the timeout from the request
526 0 : key = crm_meta_name(PCMK_META_TIMEOUT);
527 0 : crm_xml_add(params, key, "60000"); /* 1 minute */ //@TODO pass as arg
528 0 : free(key);
529 :
530 0 : request = create_controller_request(api, op, router_node, msg_data);
531 0 : rc = send_controller_request(api, request, true);
532 0 : free_xml(msg_data);
533 0 : free_xml(request);
534 0 : return rc;
535 : }
536 :
537 : /*!
538 : * \brief Ask the controller to fail a resource
539 : *
540 : * \param[in,out] api Controller connection
541 : * \param[in] target_node Name of node resource is on
542 : * \param[in] router_node Router node for target
543 : * \param[in] rsc_id ID of resource to fail
544 : * \param[in] rsc_long_id Long ID of resource (if any)
545 : * \param[in] standard Standard of resource
546 : * \param[in] provider Provider of resource (if any)
547 : * \param[in] type Type of resource to fail
548 : *
549 : * \return Standard Pacemaker return code
550 : * \note Event callback will get a reply of type pcmk_controld_reply_resource.
551 : */
552 : int
553 0 : pcmk_controld_api_fail(pcmk_ipc_api_t *api,
554 : const char *target_node, const char *router_node,
555 : const char *rsc_id, const char *rsc_long_id,
556 : const char *standard, const char *provider,
557 : const char *type)
558 : {
559 0 : crm_debug("Sending %s IPC request to fail %s (a.k.a. %s) on %s via %s",
560 : pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
561 : pcmk__s(rsc_long_id, "no other names"),
562 : pcmk__s(target_node, "unspecified node"),
563 : pcmk__s(router_node, "unspecified node"));
564 0 : return controller_resource_op(api, CRM_OP_LRM_FAIL, target_node,
565 : router_node, false, rsc_id, rsc_long_id,
566 : standard, provider, type);
567 : }
568 :
569 : /*!
570 : * \brief Ask the controller to refresh a resource
571 : *
572 : * \param[in,out] api Controller connection
573 : * \param[in] target_node Name of node resource is on
574 : * \param[in] router_node Router node for target
575 : * \param[in] rsc_id ID of resource to refresh
576 : * \param[in] rsc_long_id Long ID of resource (if any)
577 : * \param[in] standard Standard of resource
578 : * \param[in] provider Provider of resource (if any)
579 : * \param[in] type Type of resource
580 : * \param[in] cib_only If true, clean resource from CIB only
581 : *
582 : * \return Standard Pacemaker return code
583 : * \note Event callback will get a reply of type pcmk_controld_reply_resource.
584 : */
585 : int
586 0 : pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node,
587 : const char *router_node,
588 : const char *rsc_id, const char *rsc_long_id,
589 : const char *standard, const char *provider,
590 : const char *type, bool cib_only)
591 : {
592 0 : crm_debug("Sending %s IPC request to refresh %s (a.k.a. %s) on %s via %s",
593 : pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
594 : pcmk__s(rsc_long_id, "no other names"),
595 : pcmk__s(target_node, "unspecified node"),
596 : pcmk__s(router_node, "unspecified node"));
597 0 : return controller_resource_op(api, CRM_OP_LRM_DELETE, target_node,
598 : router_node, cib_only, rsc_id, rsc_long_id,
599 : standard, provider, type);
600 : }
601 :
602 : /*!
603 : * \brief Get the number of IPC replies currently expected from the controller
604 : *
605 : * \param[in] api Controller IPC API connection
606 : *
607 : * \return Number of replies expected
608 : */
609 : unsigned int
610 0 : pcmk_controld_api_replies_expected(const pcmk_ipc_api_t *api)
611 : {
612 0 : struct controld_api_private_s *private = api->api_data;
613 :
614 0 : return private->replies_expected;
615 : }
616 :
617 : /*!
618 : * \brief Create XML for a controller IPC "hello" message
619 : *
620 : * \deprecated This function is deprecated as part of the public C API.
621 : */
622 : // \todo make this static to this file when breaking API backward compatibility
623 : xmlNode *
624 0 : create_hello_message(const char *uuid, const char *client_name,
625 : const char *major_version, const char *minor_version)
626 : {
627 0 : xmlNode *hello_node = NULL;
628 0 : xmlNode *hello = NULL;
629 :
630 0 : if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name)
631 0 : || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) {
632 0 : crm_err("Could not create IPC hello message from %s (UUID %s): "
633 : "missing information",
634 : client_name? client_name : "unknown client",
635 : uuid? uuid : "unknown");
636 0 : return NULL;
637 : }
638 :
639 0 : hello_node = pcmk__xe_create(NULL, PCMK__XE_OPTIONS);
640 0 : crm_xml_add(hello_node, PCMK__XA_MAJOR_VERSION, major_version);
641 0 : crm_xml_add(hello_node, PCMK__XA_MINOR_VERSION, minor_version);
642 0 : crm_xml_add(hello_node, PCMK__XA_CLIENT_NAME, client_name);
643 :
644 : // @TODO Nothing uses this. Drop, or keep for debugging?
645 0 : crm_xml_add(hello_node, PCMK__XA_CLIENT_UUID, uuid);
646 :
647 0 : hello = create_request(CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid);
648 0 : if (hello == NULL) {
649 0 : crm_err("Could not create IPC hello message from %s (UUID %s): "
650 : "Request creation failed", client_name, uuid);
651 0 : return NULL;
652 : }
653 0 : free_xml(hello_node);
654 :
655 0 : crm_trace("Created hello message from %s (UUID %s)", client_name, uuid);
656 0 : return hello;
657 : }
|