pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
ipc_attrd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2023 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 
25 static void
26 set_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 
46 static bool
47 reply_expected(pcmk_ipc_api_t *api, const 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 
61 static bool
62 dispatch(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 
105 done:
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 
139 static xmlNode *
140 create_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 
151 static int
152 create_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 
163 static void
164 destroy_api(pcmk_ipc_api_t *api)
165 {
166  pcmk_disconnect_ipc(api);
167  pcmk_free_ipc_api(api);
168  api = NULL;
169 }
170 
171 static int
172 connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
173 {
174  int rc = pcmk_rc_ok;
175 
177  if (rc != pcmk_rc_ok) {
178  crm_err("Could not connect to %s: %s",
179  pcmk_ipc_name(api, true), pcmk_rc_str(rc));
180  return rc;
181  }
182 
183  rc = pcmk__send_ipc_request(api, request);
184  if (rc != pcmk_rc_ok) {
185  crm_err("Could not send request to %s: %s",
186  pcmk_ipc_name(api, true), pcmk_rc_str(rc));
187  return rc;
188  }
189 
190  return pcmk_rc_ok;
191 }
192 
193 static int
194 send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
195 {
196  return pcmk__send_ipc_request(api, request);
197 }
198 
199 int
201  const char *resource, const char *operation,
202  const char *interval_spec, const char *user_name,
203  uint32_t options)
204 {
205  int rc = pcmk_rc_ok;
206  xmlNode *request = create_attrd_op(user_name);
207  const char *interval_desc = NULL;
208  const char *op_desc = NULL;
209  const char *target = pcmk__node_attr_target(node);
210 
211  if (target != NULL) {
212  node = target;
213  }
214 
216  pcmk__xe_add_node(request, node, 0);
217  crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
218  crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
219  crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
222 
223  if (api == NULL) {
224  rc = create_api(&api);
225  if (rc != pcmk_rc_ok) {
226  return rc;
227  }
228 
229  rc = connect_and_send_attrd_request(api, request);
230  destroy_api(api);
231 
232  } else if (!pcmk_ipc_is_connected(api)) {
233  rc = connect_and_send_attrd_request(api, request);
234 
235  } else {
236  rc = send_attrd_request(api, request);
237  }
238 
239  free_xml(request);
240 
241  if (operation) {
242  interval_desc = interval_spec? interval_spec : "nonrecurring";
243  op_desc = operation;
244  } else {
245  interval_desc = "all";
246  op_desc = "operations";
247  }
248 
249  crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
250  interval_desc, op_desc, (resource? resource : "all resources"),
251  (node? node : "all nodes"), pcmk_rc_str(rc), rc);
252 
253  return rc;
254 }
255 
256 int
257 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
258  uint32_t options)
259 {
260  const char *target = NULL;
261 
262  if (name == NULL) {
263  return EINVAL;
264  }
265 
267 
268  if (target != NULL) {
269  node = target;
270  }
271 
272  /* Make sure the right update option is set. */
273  options &= ~pcmk__node_attr_delay;
274  options |= pcmk__node_attr_value;
275 
276  return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
277 }
278 
279 int
280 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
281 {
282  int rc = pcmk_rc_ok;
283  xmlNode *request = NULL;
284  const char *display_host = (node ? node : "localhost");
285  const char *target = pcmk__node_attr_target(node);
286 
287  if (target != NULL) {
288  node = target;
289  }
290 
291  request = create_attrd_op(NULL);
292 
294  pcmk__xe_add_node(request, node, 0);
295 
296  if (api == NULL) {
297  rc = create_api(&api);
298  if (rc != pcmk_rc_ok) {
299  return rc;
300  }
301 
302  rc = connect_and_send_attrd_request(api, request);
303  destroy_api(api);
304 
305  } else if (!pcmk_ipc_is_connected(api)) {
306  rc = connect_and_send_attrd_request(api, request);
307 
308  } else {
309  rc = send_attrd_request(api, request);
310  }
311 
312  free_xml(request);
313 
314  crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
315  display_host, pcmk_rc_str(rc), rc);
316 
317  return rc;
318 }
319 
320 int
321 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
322  uint32_t options)
323 {
324  int rc = pcmk_rc_ok;
325  xmlNode *request = NULL;
326  const char *target = NULL;
327 
328  if (name == NULL) {
329  return EINVAL;
330  }
331 
332  if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
333  node = NULL;
334  } else {
336 
337  if (target != NULL) {
338  node = target;
339  }
340  }
341 
342  request = create_attrd_op(NULL);
343 
346  pcmk__xe_add_node(request, node, 0);
347 
348  rc = send_attrd_request(api, request);
349  free_xml(request);
350 
351  if (node) {
352  crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
353  name, node, pcmk_rc_str(rc), rc);
354  } else {
355  crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
356  name, pcmk_rc_str(rc), rc);
357  }
358 
359  return rc;
360 }
361 
362 int
364 {
365  int rc = pcmk_rc_ok;
366  xmlNode *request = NULL;
367  const char *display_host = (node ? node : "localhost");
368  const char *target = pcmk__node_attr_target(node);
369 
370  if (target != NULL) {
371  node = target;
372  }
373 
374  request = create_attrd_op(NULL);
375 
377  pcmk__xe_add_node(request, node, 0);
378 
379  if (api == NULL) {
380  rc = create_api(&api);
381  if (rc != pcmk_rc_ok) {
382  return rc;
383  }
384 
385  rc = connect_and_send_attrd_request(api, request);
386  destroy_api(api);
387 
388  } else if (!pcmk_ipc_is_connected(api)) {
389  rc = connect_and_send_attrd_request(api, request);
390 
391  } else {
392  rc = send_attrd_request(api, request);
393  }
394 
395  free_xml(request);
396 
397  crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
398  display_host, pcmk_rc_str(rc), rc);
399 
400  return rc;
401 }
402 
403 static void
404 add_op_attr(xmlNode *op, uint32_t options)
405 {
406  if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
408  } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
410  } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
412  }
413 }
414 
415 static void
416 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
417  const char *dampen, const char *set, uint32_t options)
418 {
419  if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
421  } else {
423  }
424 
427  } else {
429  }
430 
431  add_op_attr(op, options);
432 
433  crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
435  pcmk__xe_add_node(op, node, 0);
436  crm_xml_add(op, PCMK__XA_ATTR_SET, set);
441 
442  if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
444  } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
446  }
447 }
448 
449 int
450 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
451  const char *value, const char *dampen, const char *set,
452  const char *user_name, uint32_t options)
453 {
454  int rc = pcmk_rc_ok;
455  xmlNode *request = NULL;
456  const char *display_host = (node ? node : "localhost");
457  const char *target = NULL;
458 
459  if (name == NULL) {
460  return EINVAL;
461  }
462 
464 
465  if (target != NULL) {
466  node = target;
467  }
468 
469  request = create_attrd_op(user_name);
470  populate_update_op(request, node, name, value, dampen, set, options);
471 
472  if (api == NULL) {
473  rc = create_api(&api);
474  if (rc != pcmk_rc_ok) {
475  return rc;
476  }
477 
478  rc = connect_and_send_attrd_request(api, request);
479  destroy_api(api);
480 
481  } else if (!pcmk_ipc_is_connected(api)) {
482  rc = connect_and_send_attrd_request(api, request);
483 
484  } else {
485  rc = send_attrd_request(api, request);
486  }
487 
488  free_xml(request);
489 
490  crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
491  name, display_host, pcmk_rc_str(rc), rc);
492 
493  return rc;
494 }
495 
496 int
497 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
498  const char *set, const char *user_name,
499  uint32_t options)
500 {
501  int rc = pcmk_rc_ok;
502  xmlNode *request = NULL;
503 
504  if (attrs == NULL) {
505  return EINVAL;
506  }
507 
508  /* There are two different ways of handling a list of attributes:
509  *
510  * (1) For messages originating from some command line tool, we have to send
511  * them one at a time. In this loop, we just call pcmk__attrd_api_update
512  * for each, letting it deal with creating the API object if it doesn't
513  * already exist.
514  *
515  * The reason we can't use a single message in this case is that we can't
516  * trust that the server supports it. Remote nodes could be involved
517  * here, and there's no guarantee that a newer client running on a remote
518  * node is talking to (or proxied through) a cluster node with a newer
519  * attrd. We also can't just try sending a single message and then falling
520  * back on multiple. There's no handshake with the attrd server to
521  * determine its version. And then we would need to do that fallback in the
522  * dispatch function for this to work for all connection types (mainloop in
523  * particular), and at that point we won't know what the original message
524  * was in order to break it apart and resend as individual messages.
525  *
526  * (2) For messages between daemons, we can be assured that the local attrd
527  * will support the new message and that it can send to the other attrds
528  * as one request or split up according to the minimum supported version.
529  */
530  for (GList *iter = attrs; iter != NULL; iter = iter->next) {
531  pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
532 
533  if (pcmk__is_daemon) {
534  const char *target = NULL;
535  xmlNode *child = NULL;
536 
537  /* First time through this loop - create the basic request. */
538  if (request == NULL) {
539  request = create_attrd_op(user_name);
540  add_op_attr(request, options);
541  }
542 
543  /* Add a child node for this operation. We add the task to the top
544  * level XML node so attrd_ipc_dispatch doesn't need changes. And
545  * then we also add the task to each child node in populate_update_op
546  * so attrd_client_update knows what form of update is taking place.
547  */
548  child = create_xml_node(request, XML_ATTR_OP);
550 
551  if (target != NULL) {
552  pair->node = target;
553  }
554 
555  populate_update_op(child, pair->node, pair->name, pair->value, dampen,
556  set, options);
557  } else {
558  rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
559  dampen, set, user_name, options);
560  }
561  }
562 
563  /* If we were doing multiple attributes at once, we still need to send the
564  * request. Do that now, creating and destroying the API object if needed.
565  */
566  if (pcmk__is_daemon) {
567  bool created_api = false;
568 
569  if (api == NULL) {
570  rc = create_api(&api);
571  if (rc != pcmk_rc_ok) {
572  return rc;
573  }
574 
575  created_api = true;
576  }
577 
578  rc = connect_and_send_attrd_request(api, request);
579  free_xml(request);
580 
581  if (created_api) {
582  destroy_api(api);
583  }
584  }
585 
586  return rc;
587 }
#define T_ATTRD
Definition: msg_xml.h:107
#define PCMK__XA_ATTR_DAMPENING
Definition: crm_internal.h:61
#define PCMK__ATTRD_CMD_UPDATE_DELAY
Definition: crm_internal.h:112
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:321
#define PCMK__XA_ATTR_NAME
Definition: crm_internal.h:66
A dumping ground.
#define F_TYPE
Definition: msg_xml.h:91
#define PCMK__ATTRD_CMD_UPDATE_BOTH
Definition: crm_internal.h:111
const char * pcmk__node_attr_target(const char *name)
Definition: attrs.c:39
char data[0]
Definition: cpg.c:55
#define PCMK__ATTRD_CMD_PEER_REMOVE
Definition: crm_internal.h:109
const char * name
Definition: cib.c:26
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:497
#define PCMK__XA_TASK
Definition: crm_internal.h:92
#define XML_TAG_UTILIZATION
Definition: msg_xml.h:232
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:363
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:51
#define PCMK__XA_ATTR_SET_TYPE
Definition: crm_internal.h:74
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
#define F_SUBTYPE
Definition: msg_xml.h:87
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:349
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:302
#define PCMK__XA_ATTR_VALUE
Definition: crm_internal.h:78
char * crm_system_name
Definition: utils.c:51
enum crm_exit_e crm_exit_t
#define PCMK__XA_ATTR_RESOURCE
Definition: crm_internal.h:71
#define PCMK__ATTRD_CMD_CLEAR_FAILURE
Definition: crm_internal.h:118
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
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:150
int pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type, int attempts)
Definition: ipc_client.c:501
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:200
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:450
#define XML_TAG_ATTR_SETS
Definition: msg_xml.h:227
#define XML_ATTR_OP
Definition: msg_xml.h:161
Protocol violated.
Definition: results.h:266
union pcmk__attrd_api_reply_t::@0 data
#define crm_debug(fmt, args...)
Definition: logging.h:386
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define PCMK__XA_ATTR_OPERATION
Definition: crm_internal.h:69
#define PCMK__ATTRD_CMD_REFRESH
Definition: crm_internal.h:114
G_GNUC_INTERNAL int pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
Definition: ipc_client.c:668
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
bool pcmk__is_daemon
Definition: logging.c:47
#define PCMK__XA_ATTR_INTERVAL
Definition: crm_internal.h:63
#define PCMK__XA_ATTR_IS_PRIVATE
Definition: crm_internal.h:64
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define PCMK__XA_ATTR_USER
Definition: crm_internal.h:76
Success.
Definition: results.h:240
#define PCMK__ATTRD_CMD_QUERY
Definition: crm_internal.h:113
#define PCMK__ATTRD_CMD_UPDATE
Definition: crm_internal.h:110
#define F_ORIG
Definition: msg_xml.h:79
void free_xml(xmlNode *child)
Definition: xml.c:783
int(* post_connect)(pcmk_ipc_api_t *api)
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:223
enum pcmk__attrd_api_reply reply_type
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:280
const xmlChar * pcmkXmlStr
Definition: xml.h:50
const char * target
Definition: pcmk_fence.c:29
int(* new_data)(pcmk_ipc_api_t *api)
#define PCMK__XA_ATTR_SYNC_POINT
Definition: crm_internal.h:75
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:257
#define PCMK__XA_ATTR_NODE_NAME
Definition: crm_internal.h:68
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
bool(* reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request)
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
Sending a command will wait for any reply.
Definition: ipc.h:91
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:206
#define PCMK__VALUE_CLUSTER
void(* free_data)(void *api_data)
const char * pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
Get the IPC name used with an IPC API connection.
Definition: ipc_client.c:247
#define PCMK__XA_ATTR_IS_REMOTE
Definition: crm_internal.h:65
IPC interface to Pacemaker daemons.
pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition: ipc_attrd.c:117
#define PCMK__XA_ATTR_PATTERN
Definition: crm_internal.h:70
void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
Definition: nodes.c:15
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:83
Attribute manager.
Definition: ipc.h:70
#define PCMK__XA_ATTR_SET
Definition: crm_internal.h:73
#define PCMK__VALUE_LOCAL
#define crm_info(fmt, args...)
Definition: logging.h:384
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:594
bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api)
Check whether an IPC API connection is active.
Definition: ipc_client.c:287
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510