pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
ipc_attrd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011-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 #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>
20 #include <crm/common/ipc.h>
22 #include <crm/common/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 = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
34  NULL);
35  node != NULL; node = pcmk__xe_next_same(node)) {
36 
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__xe_is(reply, PCMK__XE_ACK)) {
72  return false;
73  }
74 
75  /* Do some basic validation of the reply */
76  value = crm_element_value(reply, PCMK__XA_T);
77  if (pcmk__str_empty(value)
78  || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) {
79  crm_info("Unrecognizable message from attribute manager: "
80  "message type '%s' not '" PCMK__VALUE_ATTRD "'",
81  pcmk__s(value, ""));
82  status = CRM_EX_PROTOCOL;
83  goto done;
84  }
85 
86  value = crm_element_value(reply, PCMK__XA_SUBT);
87 
88  /* Only the query command gets a reply for now. NULL counts as query for
89  * backward compatibility with attribute managers <2.1.3 that didn't set it.
90  */
91  if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
92  if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
93  status = ENXIO; // Most likely, the attribute doesn't exist
94  goto done;
95  }
97  set_pairs_data(&reply_data, reply);
98 
99  } else {
100  crm_info("Unrecognizable message from attribute manager: "
101  "message subtype '%s' unknown", pcmk__s(value, ""));
102  status = CRM_EX_PROTOCOL;
103  goto done;
104  }
105 
106 done:
107  pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
108 
109  /* Free any reply data that was allocated */
110  if (reply_data.data.pairs) {
111  g_list_free_full(reply_data.data.pairs, free);
112  }
113 
114  return false;
115 }
116 
119 {
120  pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
121 
122  if (cmds != NULL) {
123  cmds->new_data = NULL;
124  cmds->free_data = NULL;
125  cmds->post_connect = NULL;
126  cmds->reply_expected = reply_expected;
127  cmds->dispatch = dispatch;
128  }
129  return cmds;
130 }
131 
140 static xmlNode *
141 create_attrd_op(const char *user_name)
142 {
143  xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
144 
146  crm_xml_add(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown"));
147  crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
148 
149  return attrd_op;
150 }
151 
152 static int
153 connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
154 {
155  int rc = pcmk_rc_ok;
156  bool created_api = false;
157 
158  if (api == NULL) {
159  rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd);
160  if (rc != pcmk_rc_ok) {
161  return rc;
162  }
163  created_api = true;
164  }
165 
167  if (rc == pcmk_rc_ok) {
168  rc = pcmk__send_ipc_request(api, request);
169  }
170 
171  if (created_api) {
172  pcmk_free_ipc_api(api);
173  }
174  return rc;
175 }
176 
177 int
179  const char *resource, const char *operation,
180  const char *interval_spec, const char *user_name,
181  uint32_t options)
182 {
183  int rc = pcmk_rc_ok;
184  xmlNode *request = create_attrd_op(user_name);
185  const char *interval_desc = NULL;
186  const char *op_desc = NULL;
187  const char *target = pcmk__node_attr_target(node);
188 
189  if (target != NULL) {
190  node = target;
191  }
192 
193  if (operation) {
194  interval_desc = pcmk__s(interval_spec, "nonrecurring");
195  op_desc = operation;
196  } else {
197  interval_desc = "all";
198  op_desc = "operations";
199  }
200  crm_debug("Asking %s to clear failure of %s %s for %s on %s",
201  pcmk_ipc_name(api, true), interval_desc, op_desc,
202  pcmk__s(resource, "all resources"), pcmk__s(node, "all nodes"));
203 
205  pcmk__xe_add_node(request, node, 0);
206  crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
207  crm_xml_add(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation);
208  crm_xml_add(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec);
211 
212  rc = connect_and_send_attrd_request(api, request);
213 
214  free_xml(request);
215  return rc;
216 }
217 
218 int
219 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
220  uint32_t options)
221 {
222  const char *target = NULL;
223 
224  if (name == NULL) {
225  return EINVAL;
226  }
227 
229 
230  if (target != NULL) {
231  node = target;
232  }
233 
234  /* Make sure the right update option is set. */
235  options &= ~pcmk__node_attr_delay;
236  options |= pcmk__node_attr_value;
237 
238  return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
239 }
240 
241 int
242 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
243 {
244  int rc = pcmk_rc_ok;
245  xmlNode *request = NULL;
246  const char *target = pcmk__node_attr_target(node);
247 
248  if (target != NULL) {
249  node = target;
250  }
251 
252  crm_debug("Asking %s to purge transient attributes%s for %s",
253  pcmk_ipc_name(api, true),
254  (reap? " and node cache entries" : ""),
255  pcmk__s(node, "local node"));
256 
257  request = create_attrd_op(NULL);
258 
260  pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap);
261  pcmk__xe_add_node(request, node, 0);
262 
263  rc = connect_and_send_attrd_request(api, request);
264 
265  free_xml(request);
266  return rc;
267 }
268 
269 int
270 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
271  uint32_t options)
272 {
273  int rc = pcmk_rc_ok;
274  xmlNode *request = NULL;
275  const char *target = NULL;
276 
277  if (name == NULL) {
278  return EINVAL;
279  }
280 
281  if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
282  node = NULL;
283  } else {
285 
286  if (target != NULL) {
287  node = target;
288  }
289  }
290 
291  crm_debug("Querying %s for value of '%s'%s%s",
292  pcmk_ipc_name(api, true), name,
293  ((node == NULL)? "" : " on "), pcmk__s(node, ""));
294 
295  request = create_attrd_op(NULL);
296 
299  pcmk__xe_add_node(request, node, 0);
300 
301  rc = connect_and_send_attrd_request(api, request);
302  free_xml(request);
303  return rc;
304 }
305 
306 int
308 {
309  int rc = pcmk_rc_ok;
310  xmlNode *request = NULL;
311  const char *target = pcmk__node_attr_target(node);
312 
313  if (target != NULL) {
314  node = target;
315  }
316 
317  crm_debug("Asking %s to write all transient attributes for %s to CIB",
318  pcmk_ipc_name(api, true), pcmk__s(node, "local node"));
319 
320  request = create_attrd_op(NULL);
321 
323  pcmk__xe_add_node(request, node, 0);
324 
325  rc = connect_and_send_attrd_request(api, request);
326 
327  free_xml(request);
328  return rc;
329 }
330 
331 static void
332 add_op_attr(xmlNode *op, uint32_t options)
333 {
334  if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
336  } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
338  } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
340  }
341 }
342 
343 static void
344 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
345  const char *dampen, const char *set, uint32_t options)
346 {
347  if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
349  } else {
351  }
352 
355  } else {
357  }
358 
359  add_op_attr(op, options);
360 
361  crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
363  pcmk__xe_add_node(op, node, 0);
364  crm_xml_add(op, PCMK__XA_ATTR_SET, set);
369 
370  if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
372  } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
374  }
375 }
376 
377 int
378 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
379  const char *value, const char *dampen, const char *set,
380  const char *user_name, uint32_t options)
381 {
382  int rc = pcmk_rc_ok;
383  xmlNode *request = NULL;
384  const char *target = NULL;
385 
386  if (name == NULL) {
387  return EINVAL;
388  }
389 
391 
392  if (target != NULL) {
393  node = target;
394  }
395 
396  crm_debug("Asking %s to update '%s' to '%s' for %s",
397  pcmk_ipc_name(api, true), name, pcmk__s(value, "(null)"),
398  pcmk__s(node, "local node"));
399 
400  request = create_attrd_op(user_name);
401  populate_update_op(request, node, name, value, dampen, set, options);
402 
403  rc = connect_and_send_attrd_request(api, request);
404 
405  free_xml(request);
406  return rc;
407 }
408 
409 int
410 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
411  const char *set, const char *user_name,
412  uint32_t options)
413 {
414  int rc = pcmk_rc_ok;
415  xmlNode *request = NULL;
416 
417  if (attrs == NULL) {
418  return EINVAL;
419  }
420 
421  /* There are two different ways of handling a list of attributes:
422  *
423  * (1) For messages originating from some command line tool, we have to send
424  * them one at a time. In this loop, we just call pcmk__attrd_api_update
425  * for each, letting it deal with creating the API object if it doesn't
426  * already exist.
427  *
428  * The reason we can't use a single message in this case is that we can't
429  * trust that the server supports it. Remote nodes could be involved
430  * here, and there's no guarantee that a newer client running on a remote
431  * node is talking to (or proxied through) a cluster node with a newer
432  * attrd. We also can't just try sending a single message and then falling
433  * back on multiple. There's no handshake with the attrd server to
434  * determine its version. And then we would need to do that fallback in the
435  * dispatch function for this to work for all connection types (mainloop in
436  * particular), and at that point we won't know what the original message
437  * was in order to break it apart and resend as individual messages.
438  *
439  * (2) For messages between daemons, we can be assured that the local attrd
440  * will support the new message and that it can send to the other attrds
441  * as one request or split up according to the minimum supported version.
442  */
443  for (GList *iter = attrs; iter != NULL; iter = iter->next) {
444  pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
445 
446  if (pcmk__is_daemon) {
447  const char *target = NULL;
448  xmlNode *child = NULL;
449 
450  /* First time through this loop - create the basic request. */
451  if (request == NULL) {
452  request = create_attrd_op(user_name);
453  add_op_attr(request, options);
454  }
455 
456  /* Add a child node for this operation. We add the task to the top
457  * level XML node so attrd_ipc_dispatch doesn't need changes. And
458  * then we also add the task to each child node in populate_update_op
459  * so attrd_client_update knows what form of update is taking place.
460  */
461  child = pcmk__xe_create(request, PCMK_XE_OP);
463 
464  if (target != NULL) {
465  pair->node = target;
466  }
467 
468  populate_update_op(child, pair->node, pair->name, pair->value, dampen,
469  set, options);
470  } else {
471  rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
472  dampen, set, user_name, options);
473  }
474  }
475 
476  /* If we were doing multiple attributes at once, we still need to send the
477  * request. Do that now, creating and destroying the API object if needed.
478  */
479  if (pcmk__is_daemon) {
480  rc = connect_and_send_attrd_request(api, request);
481  free_xml(request);
482  }
483 
484  return rc;
485 }
#define PCMK__ATTRD_CMD_UPDATE_DELAY
Definition: crm_internal.h:78
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:270
A dumping ground.
#define PCMK__ATTRD_CMD_UPDATE_BOTH
Definition: crm_internal.h:77
char data[0]
Definition: cpg.c:58
#define PCMK__XA_ATTR_CLEAR_INTERVAL
#define PCMK__ATTRD_CMD_PEER_REMOVE
Definition: crm_internal.h:75
#define PCMK__VALUE_ATTRD
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:410
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:307
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
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:360
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:313
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: nvpair.c:987
char * crm_system_name
Definition: utils.c:48
enum crm_exit_e crm_exit_t
#define PCMK__XA_SUBT
#define PCMK__ATTRD_CMD_CLEAR_FAILURE
Definition: crm_internal.h:83
#define PCMK__XA_ATTR_REGEX
const char * pcmk__node_attr_target(const char *name)
Definition: attrs.c:42
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:178
#define PCMK__XA_ATTR_NAME
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:378
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
Definition: ipc_attrd.c:242
#define PCMK__XA_ATTR_CLEAR_OPERATION
#define PCMK__XA_ATTR_SYNC_POINT
Protocol violated.
Definition: results.h:277
union pcmk__attrd_api_reply_t::@0 data
#define crm_debug(fmt, args...)
Definition: logging.h:402
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
#define PCMK__ATTRD_CMD_REFRESH
Definition: crm_internal.h:80
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#define PCMK_XE_UTILIZATION
Definition: xml_names.h:217
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:94
#define PCMK_XA_TASK
Definition: xml_names.h:424
bool pcmk__is_daemon
Definition: logging.c:47
Wrappers for and extensions to libxml2.
#define PCMK__XA_ATTR_IS_PRIVATE
Success.
Definition: results.h:251
#define PCMK__ATTRD_CMD_QUERY
Definition: crm_internal.h:79
#define PCMK__XA_ATTR_DAMPENING
#define PCMK__XA_ATTR_RESOURCE
#define PCMK__ATTRD_CMD_UPDATE
Definition: crm_internal.h:76
#define PCMK__XA_ATTR_SET_TYPE
void free_xml(xmlNode *child)
Definition: xml.c:958
int(* post_connect)(pcmk_ipc_api_t *api)
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
#define PCMK__XA_ATTR_SET
enum pcmk__attrd_api_reply reply_type
const xmlChar * pcmkXmlStr
Definition: xml.h:41
const char * target
Definition: pcmk_fence.c:29
#define PCMK__XA_ATTR_HOST
int(* new_data)(pcmk_ipc_api_t *api)
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:219
#define PCMK_XE_OP
Definition: xml_names.h:146
#define PCMK_XE_NODE
Definition: xml_names.h:136
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
bool(* reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request)
Sending a command will wait for any reply.
Definition: ipc.h:105
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__XA_ATTR_VALUE
#define PCMK__XE_ACK
#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
IPC interface to Pacemaker daemons.
pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition: ipc_attrd.c:118
#define PCMK__XA_ATTR_USER
#define PCMK__XA_ATTR_IS_REMOTE
#define PCMK__XA_T
void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
Definition: nodes.c:127
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:96
#define PCMK__XA_REAP
Attribute manager.
Definition: ipc.h:78
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:122
#define PCMK__VALUE_LOCAL
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
#define crm_info(fmt, args...)
Definition: logging.h:399
#define PCMK__XA_SRC