pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
ipc_attrd.c
Go to the documentation of this file.
1/*
2 * Copyright 2011-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#ifndef _GNU_SOURCE
11# define _GNU_SOURCE
12#endif
13
14#include <crm_internal.h>
15
16#include <stdio.h>
17
18#include <crm/crm.h>
19#include <crm/common/ipc.h>
22#include <crm/msg_xml.h>
23#include "crmcommon_private.h"
24
25static void
26set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
27{
28 const char *name = NULL;
30
32
33 for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
34 node != NULL; node = crm_next_same_xml(node)) {
35 pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
36
37 CRM_ASSERT(pair != NULL);
38
40 pair->name = name;
42 data->data.pairs = g_list_prepend(data->data.pairs, pair);
43 }
44}
45
46static bool
47reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
48{
49 const char *command = crm_element_value(request, PCMK__XA_TASK);
50
51 return pcmk__str_any_of(command,
58 NULL);
59}
60
61static bool
62dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
63{
64 const char *value = NULL;
65 crm_exit_t status = CRM_EX_OK;
66
67 pcmk__attrd_api_reply_t reply_data = {
69 };
70
71 if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) {
72 return false;
73 }
74
75 /* Do some basic validation of the reply */
76 value = crm_element_value(reply, F_TYPE);
77 if (pcmk__str_empty(value)
78 || !pcmk__str_eq(value, T_ATTRD, pcmk__str_none)) {
79 crm_info("Unrecognizable message from attribute manager: "
80 "message type '%s' not '" T_ATTRD "'", pcmk__s(value, ""));
81 status = CRM_EX_PROTOCOL;
82 goto done;
83 }
84
85 value = crm_element_value(reply, F_SUBTYPE);
86
87 /* Only the query command gets a reply for now. NULL counts as query for
88 * backward compatibility with attribute managers <2.1.3 that didn't set it.
89 */
90 if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
91 if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
92 status = ENXIO; // Most likely, the attribute doesn't exist
93 goto done;
94 }
96 set_pairs_data(&reply_data, reply);
97
98 } else {
99 crm_info("Unrecognizable message from attribute manager: "
100 "message subtype '%s' unknown", pcmk__s(value, ""));
101 status = CRM_EX_PROTOCOL;
102 goto done;
103 }
104
105done:
106 pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
107
108 /* Free any reply data that was allocated */
109 if (reply_data.data.pairs) {
110 g_list_free_full(reply_data.data.pairs, free);
111 }
112
113 return false;
114}
115
118{
119 pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
120
121 if (cmds != NULL) {
122 cmds->new_data = NULL;
123 cmds->free_data = NULL;
124 cmds->post_connect = NULL;
125 cmds->reply_expected = reply_expected;
126 cmds->dispatch = dispatch;
127 }
128 return cmds;
129}
130
139static xmlNode *
140create_attrd_op(const char *user_name)
141{
142 xmlNode *attrd_op = create_xml_node(NULL, __func__);
143
144 crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
145 crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
146 crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
147
148 return attrd_op;
149}
150
151static int
152create_api(pcmk_ipc_api_t **api)
153{
154 int rc = pcmk_new_ipc_api(api, pcmk_ipc_attrd);
155
156 if (rc != pcmk_rc_ok) {
157 crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
158 }
159
160 return rc;
161}
162
163static void
164destroy_api(pcmk_ipc_api_t *api)
165{
168 api = NULL;
169}
170
171static int
172connect_and_send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
173{
174 int rc = pcmk_rc_ok;
175 int max = 5;
176
177 while (max > 0) {
178 crm_info("Connecting to cluster... %d retries remaining", max);
180
181 if (rc == pcmk_rc_ok) {
182 rc = pcmk__send_ipc_request(api, request);
183 break;
184 } else if (rc == EAGAIN || rc == EALREADY) {
185 sleep(5 - max);
186 max--;
187 } else {
188 crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
189 break;
190 }
191 }
192
193 return rc;
194}
195
196static int
197send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
198{
199 return pcmk__send_ipc_request(api, request);
200}
201
202int
204 const char *resource, const char *operation,
205 const char *interval_spec, const char *user_name,
206 uint32_t options)
207{
208 int rc = pcmk_rc_ok;
209 xmlNode *request = create_attrd_op(user_name);
210 const char *interval_desc = NULL;
211 const char *op_desc = NULL;
212 const char *target = pcmk__node_attr_target(node);
213
214 if (target != NULL) {
215 node = target;
216 }
217
219 pcmk__xe_add_node(request, node, 0);
220 crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
221 crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
222 crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
225
226 if (api == NULL) {
227 rc = create_api(&api);
228 if (rc != pcmk_rc_ok) {
229 return rc;
230 }
231
232 rc = connect_and_send_attrd_request(api, request);
233 destroy_api(api);
234
235 } else if (!pcmk_ipc_is_connected(api)) {
236 rc = connect_and_send_attrd_request(api, request);
237
238 } else {
239 rc = send_attrd_request(api, request);
240 }
241
242 free_xml(request);
243
244 if (operation) {
245 interval_desc = interval_spec? interval_spec : "nonrecurring";
246 op_desc = operation;
247 } else {
248 interval_desc = "all";
249 op_desc = "operations";
250 }
251
252 crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
253 interval_desc, op_desc, (resource? resource : "all resources"),
254 (node? node : "all nodes"), pcmk_rc_str(rc), rc);
255
256 return rc;
257}
258
259int
260pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
261 uint32_t options)
262{
263 const char *target = NULL;
264
265 if (name == NULL) {
266 return EINVAL;
267 }
268
270
271 if (target != NULL) {
272 node = target;
273 }
274
275 /* Make sure the right update option is set. */
276 options &= ~pcmk__node_attr_delay;
277 options |= pcmk__node_attr_value;
278
279 return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
280}
281
282int
284{
285 int rc = pcmk_rc_ok;
286 xmlNode *request = NULL;
287 const char *display_host = (node ? node : "localhost");
288 const char *target = pcmk__node_attr_target(node);
289
290 if (target != NULL) {
291 node = target;
292 }
293
294 request = create_attrd_op(NULL);
295
297 pcmk__xe_add_node(request, node, 0);
298
299 if (api == NULL) {
300 rc = create_api(&api);
301 if (rc != pcmk_rc_ok) {
302 return rc;
303 }
304
305 rc = connect_and_send_attrd_request(api, request);
306 destroy_api(api);
307
308 } else if (!pcmk_ipc_is_connected(api)) {
309 rc = connect_and_send_attrd_request(api, request);
310
311 } else {
312 rc = send_attrd_request(api, request);
313 }
314
315 free_xml(request);
316
317 crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
318 display_host, pcmk_rc_str(rc), rc);
319
320 return rc;
321}
322
323int
324pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
325 uint32_t options)
326{
327 int rc = pcmk_rc_ok;
328 xmlNode *request = NULL;
329 const char *target = NULL;
330
331 if (name == NULL) {
332 return EINVAL;
333 }
334
336
337 if (target != NULL) {
338 node = target;
339 }
340
341 request = create_attrd_op(NULL);
342
345 pcmk__xe_add_node(request, node, 0);
346
347 rc = send_attrd_request(api, request);
348 free_xml(request);
349
350 if (node) {
351 crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
352 name, node, pcmk_rc_str(rc), rc);
353 } else {
354 crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
355 name, pcmk_rc_str(rc), rc);
356 }
357
358 return rc;
359}
360
361int
363{
364 int rc = pcmk_rc_ok;
365 xmlNode *request = NULL;
366 const char *display_host = (node ? node : "localhost");
367 const char *target = pcmk__node_attr_target(node);
368
369 if (target != NULL) {
370 node = target;
371 }
372
373 request = create_attrd_op(NULL);
374
376 pcmk__xe_add_node(request, node, 0);
377
378 if (api == NULL) {
379 rc = create_api(&api);
380 if (rc != pcmk_rc_ok) {
381 return rc;
382 }
383
384 rc = connect_and_send_attrd_request(api, request);
385 destroy_api(api);
386
387 } else if (!pcmk_ipc_is_connected(api)) {
388 rc = connect_and_send_attrd_request(api, request);
389
390 } else {
391 rc = send_attrd_request(api, request);
392 }
393
394 free_xml(request);
395
396 crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
397 display_host, pcmk_rc_str(rc), rc);
398
399 return rc;
400}
401
402static void
403add_op_attr(xmlNode *op, uint32_t options)
404{
405 if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
407 } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
409 } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
411 }
412}
413
414static void
415populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
416 const char *dampen, const char *set, uint32_t options)
417{
418 if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
420 } else {
422 }
423
424 add_op_attr(op, options);
425
428 pcmk__xe_add_node(op, node, 0);
434}
435
436int
437pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
438 const char *value, const char *dampen, const char *set,
439 const char *user_name, uint32_t options)
440{
441 int rc = pcmk_rc_ok;
442 xmlNode *request = NULL;
443 const char *display_host = (node ? node : "localhost");
444 const char *target = NULL;
445
446 if (name == NULL) {
447 return EINVAL;
448 }
449
451
452 if (target != NULL) {
453 node = target;
454 }
455
456 request = create_attrd_op(user_name);
457 populate_update_op(request, node, name, value, dampen, set, options);
458
459 if (api == NULL) {
460 rc = create_api(&api);
461 if (rc != pcmk_rc_ok) {
462 return rc;
463 }
464
465 rc = connect_and_send_attrd_request(api, request);
466 destroy_api(api);
467
468 } else if (!pcmk_ipc_is_connected(api)) {
469 rc = connect_and_send_attrd_request(api, request);
470
471 } else {
472 rc = send_attrd_request(api, request);
473 }
474
475 free_xml(request);
476
477 crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
478 name, display_host, pcmk_rc_str(rc), rc);
479
480 return rc;
481}
482
483int
484pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
485 const char *set, const char *user_name,
486 uint32_t options)
487{
488 int rc = pcmk_rc_ok;
489 xmlNode *request = NULL;
490
491 if (attrs == NULL) {
492 return EINVAL;
493 }
494
495 /* There are two different ways of handling a list of attributes:
496 *
497 * (1) For messages originating from some command line tool, we have to send
498 * them one at a time. In this loop, we just call pcmk__attrd_api_update
499 * for each, letting it deal with creating the API object if it doesn't
500 * already exist.
501 *
502 * The reason we can't use a single message in this case is that we can't
503 * trust that the server supports it. Remote nodes could be involved
504 * here, and there's no guarantee that a newer client running on a remote
505 * node is talking to (or proxied through) a cluster node with a newer
506 * attrd. We also can't just try sending a single message and then falling
507 * back on multiple. There's no handshake with the attrd server to
508 * determine its version. And then we would need to do that fallback in the
509 * dispatch function for this to work for all connection types (mainloop in
510 * particular), and at that point we won't know what the original message
511 * was in order to break it apart and resend as individual messages.
512 *
513 * (2) For messages between daemons, we can be assured that the local attrd
514 * will support the new message and that it can send to the other attrds
515 * as one request or split up according to the minimum supported version.
516 */
517 for (GList *iter = attrs; iter != NULL; iter = iter->next) {
519
520 if (pcmk__is_daemon) {
521 const char *target = NULL;
522 xmlNode *child = NULL;
523
524 /* First time through this loop - create the basic request. */
525 if (request == NULL) {
526 request = create_attrd_op(user_name);
527 add_op_attr(request, options);
528 }
529
530 /* Add a child node for this operation. We add the task to the top
531 * level XML node so attrd_ipc_dispatch doesn't need changes. And
532 * then we also add the task to each child node in populate_update_op
533 * so attrd_client_update knows what form of update is taking place.
534 */
535 child = create_xml_node(request, XML_ATTR_OP);
537
538 if (target != NULL) {
539 pair->node = target;
540 }
541
542 populate_update_op(child, pair->node, pair->name, pair->value, dampen,
543 set, options);
544 } else {
545 rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
546 dampen, set, user_name, options);
547 }
548 }
549
550 /* If we were doing multiple attributes at once, we still need to send the
551 * request. Do that now, creating and destroying the API object if needed.
552 */
553 if (pcmk__is_daemon) {
554 bool created_api = false;
555
556 if (api == NULL) {
557 rc = create_api(&api);
558 if (rc != pcmk_rc_ok) {
559 return rc;
560 }
561
562 created_api = true;
563 }
564
565 rc = connect_and_send_attrd_request(api, request);
566 free_xml(request);
567
568 if (created_api) {
569 destroy_api(api);
570 }
571 }
572
573 return rc;
574}
const char * pcmk__node_attr_target(const char *name)
Definition: attrs.c:39
@ pcmk__node_attr_remote
@ pcmk__node_attr_pattern
@ pcmk__node_attr_delay
@ pcmk__node_attr_private
@ pcmk__node_attr_value
const char * name
Definition: cib.c:24
void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
Definition: nodes.c:15
bool pcmk__is_daemon
Definition: logging.c:47
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
char data[0]
Definition: cpg.c:10
A dumping ground.
char * crm_system_name
Definition: utils.c:51
#define PCMK__XA_ATTR_USER
Definition: crm_internal.h:74
#define PCMK__XA_ATTR_INTERVAL
Definition: crm_internal.h:63
#define PCMK__XA_ATTR_IS_PRIVATE
Definition: crm_internal.h:64
#define PCMK__XA_ATTR_SET
Definition: crm_internal.h:73
#define PCMK__XA_ATTR_NODE_NAME
Definition: crm_internal.h:68
#define PCMK__ATTRD_CMD_UPDATE_BOTH
Definition: crm_internal.h:101
#define PCMK__XA_ATTR_RESOURCE
Definition: crm_internal.h:71
#define PCMK__XA_ATTR_VALUE
Definition: crm_internal.h:76
#define PCMK__ATTRD_CMD_REFRESH
Definition: crm_internal.h:104
#define PCMK__ATTRD_CMD_CLEAR_FAILURE
Definition: crm_internal.h:108
#define PCMK__XA_ATTR_DAMPENING
Definition: crm_internal.h:61
#define PCMK__XA_ATTR_NAME
Definition: crm_internal.h:66
#define PCMK__XA_ATTR_PATTERN
Definition: crm_internal.h:70
#define PCMK__ATTRD_CMD_UPDATE_DELAY
Definition: crm_internal.h:102
#define PCMK__ATTRD_CMD_QUERY
Definition: crm_internal.h:103
#define PCMK__ATTRD_CMD_PEER_REMOVE
Definition: crm_internal.h:99
#define PCMK__XA_ATTR_IS_REMOTE
Definition: crm_internal.h:65
#define PCMK__XA_ATTR_OPERATION
Definition: crm_internal.h:69
#define PCMK__XA_TASK
Definition: crm_internal.h:84
#define PCMK__ATTRD_CMD_UPDATE
Definition: crm_internal.h:100
G_GNUC_INTERNAL int pcmk__send_ipc_request(pcmk_ipc_api_t *api, xmlNode *request)
Definition: ipc_client.c:619
G_GNUC_INTERNAL void pcmk__call_ipc_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
Definition: ipc_client.c:146
IPC interface to Pacemaker daemons.
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:545
int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
Connect to a Pacemaker daemon via IPC.
Definition: ipc_client.c:486
@ pcmk_ipc_event_reply
Daemon's reply to client IPC request.
Definition: ipc.h:83
@ pcmk_ipc_attrd
Attribute manager.
Definition: ipc.h:70
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:202
@ pcmk_ipc_dispatch_sync
Sending a command will wait for any reply.
Definition: ipc.h:91
int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server)
Create a new object for using Pacemaker daemon IPC.
Definition: ipc_client.c:47
bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api)
Check whether an IPC API connection is active.
Definition: ipc_client.c:283
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:283
int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, const char *value, const char *dampen, const char *set, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:437
pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition: ipc_attrd.c:117
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:362
int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen, const char *set, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:484
int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, const char *resource, const char *operation, const char *interval_spec, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:203
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:324
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:260
@ pcmk__attrd_reply_unknown
@ pcmk__attrd_reply_query
#define crm_info(fmt, args...)
Definition: logging.h:362
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define F_SUBTYPE
Definition: msg_xml.h:65
#define T_ATTRD
Definition: msg_xml.h:85
#define F_ORIG
Definition: msg_xml.h:57
#define F_TYPE
Definition: msg_xml.h:69
#define XML_ATTR_OP
Definition: msg_xml.h:140
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:205
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:419
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
const char * target
Definition: pcmk_fence.c:29
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
@ CRM_EX_PROTOCOL
Protocol violated.
Definition: results.h:260
@ CRM_EX_OK
Success.
Definition: results.h:234
@ pcmk_rc_ok
Definition: results.h:148
enum crm_exit_e crm_exit_t
@ pcmk__str_none
@ pcmk__str_null_matches
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
union pcmk__attrd_api_reply_t::@0 data
enum pcmk__attrd_api_reply reply_type
int(* new_data)(pcmk_ipc_api_t *api)
void(* free_data)(void *api_data)
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
bool(* reply_expected)(pcmk_ipc_api_t *api, xmlNode *request)
int(* post_connect)(pcmk_ipc_api_t *api)
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
const xmlChar * pcmkXmlStr
Definition: xml.h:50
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956
void free_xml(xmlNode *child)
Definition: xml.c:885
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749