pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_ticket.c
Go to the documentation of this file.
1 /*
2  * Copyright 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 General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <crm/cib/internal.h>
13 #include <crm/pengine/internal.h>
14 
15 #include <pacemaker.h>
16 #include <pacemaker-internal.h>
17 
18 #include "libpacemaker_private.h"
19 
20 static int
21 build_ticket_modify_xml(cib_t *cib, const char *ticket_id, xmlNode **ticket_state_xml,
22  xmlNode **xml_top)
23 {
24  int rc = pcmk__get_ticket_state(cib, ticket_id, ticket_state_xml);
25 
26  if (rc == pcmk_rc_ok || rc == pcmk_rc_duplicate_id) {
27  /* Ticket(s) found - return their state */
28  *xml_top = *ticket_state_xml;
29 
30  } else if (rc == ENXIO) {
31  /* No ticket found - build the XML needed to create it */
32  xmlNode *xml_obj = NULL;
33 
34  *xml_top = pcmk__xe_create(NULL, PCMK_XE_STATUS);
35  xml_obj = pcmk__xe_create(*xml_top, PCMK_XE_TICKETS);
36  *ticket_state_xml = pcmk__xe_create(xml_obj, PCMK__XE_TICKET_STATE);
37  crm_xml_add(*ticket_state_xml, PCMK_XA_ID, ticket_id);
38 
39  rc = pcmk_rc_ok;
40 
41  } else {
42  /* Some other error occurred - clean up and return */
43  pcmk__xml_free(*ticket_state_xml);
44  }
45 
46  return rc;
47 }
48 
49 static void
50 add_attribute_xml(pcmk_scheduler_t *scheduler, const char *ticket_id,
51  GHashTable *attr_set, xmlNode **ticket_state_xml)
52 {
53  GHashTableIter hash_iter;
54  char *key = NULL;
55  char *value = NULL;
56  pcmk__ticket_t *ticket = NULL;
57 
58  ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
59  ticket_id);
60  g_hash_table_iter_init(&hash_iter, attr_set);
61  while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
62  crm_xml_add(*ticket_state_xml, key, value);
63 
64  if (pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)
65  && ((ticket == NULL)
67  && crm_is_true(value)) {
68 
69  char *now = pcmk__ttoa(time(NULL));
70 
71  crm_xml_add(*ticket_state_xml, PCMK_XA_LAST_GRANTED, now);
72  free(now);
73  }
74  }
75 }
76 
77 int
78 pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
79 {
80  int rc = pcmk_rc_ok;
81  xmlNode *xml_search = NULL;
82  char *xpath = NULL;
83 
84  pcmk__assert((cib != NULL) && (state != NULL));
85  *state = NULL;
86 
87  if (ticket_id != NULL) {
89  "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
90  ticket_id);
91  } else {
93  }
94 
95  rc = cib->cmds->query(cib, xpath, &xml_search, cib_sync_call|cib_xpath);
96  rc = pcmk_legacy2rc(rc);
97 
98  if (rc == pcmk_rc_ok) {
99  crm_log_xml_debug(xml_search, "Match");
100 
101  if (xml_search->children != NULL && ticket_id != NULL) {
103  }
104  }
105 
106  free(xpath);
107 
108  *state = xml_search;
109  return rc;
110 }
111 
112 int
113 pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
114 {
115  int rc = pcmk_rc_ok;
116  xmlNode *result = NULL;
117  const char *xpath_base = NULL;
118  char *xpath = NULL;
119 
120  pcmk__assert((out != NULL) && (cib != NULL));
121 
123  pcmk__assert(xpath_base != NULL);
124 
125  if (ticket_id != NULL) {
126  xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET "[@" PCMK_XA_TICKET "=\"%s\"]",
127  xpath_base, ticket_id);
128  } else {
129  xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET, xpath_base);
130  }
131 
132  rc = cib->cmds->query(cib, xpath, &result, cib_sync_call|cib_xpath);
133  rc = pcmk_legacy2rc(rc);
134 
135  if (result != NULL) {
136  out->message(out, "ticket-constraints", result);
138  }
139 
140  free(xpath);
141  return rc;
142 }
143 
144 int
145 pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
146 {
147  pcmk__output_t *out = NULL;
148  int rc = pcmk_rc_ok;
149  cib_t *cib = NULL;
150 
151  rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
152  if (rc != pcmk_rc_ok) {
153  goto done;
154  }
155 
156  rc = pcmk__ticket_constraints(out, cib, ticket_id);
157 
158 done:
159  if (cib != NULL) {
161  }
162 
163  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
164  return rc;
165 }
166 
167 static int
168 delete_single_ticket(xmlNode *child, void *userdata)
169 {
170  int rc = pcmk_rc_ok;
171  cib_t *cib = (cib_t *) userdata;
172 
173  rc = cib->cmds->remove(cib, PCMK_XE_STATUS, child, cib_sync_call);
174  rc = pcmk_legacy2rc(rc);
175 
176  return rc;
177 }
178 
179 int
181  const char *ticket_id, bool force)
182 {
183  int rc = pcmk_rc_ok;
184  xmlNode *state = NULL;
185 
186  pcmk__assert((cib != NULL) && (scheduler != NULL));
187 
188  if (ticket_id == NULL) {
189  return EINVAL;
190  }
191 
192  if (!force) {
193  pcmk__ticket_t *ticket = NULL;
194 
195  ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
196  ticket_id);
197  if (ticket == NULL) {
198  return ENXIO;
199  }
200 
201  if (pcmk_is_set(ticket->flags, pcmk__ticket_granted)) {
202  return EACCES;
203  }
204  }
205 
206  rc = pcmk__get_ticket_state(cib, ticket_id, &state);
207 
208  if (rc == pcmk_rc_duplicate_id) {
209  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
210  ticket_id);
211 
212  } else if (rc == ENXIO) {
213  return pcmk_rc_ok;
214 
215  } else if (rc != pcmk_rc_ok) {
216  return rc;
217  }
218 
219  crm_log_xml_debug(state, "Delete");
220 
221  if (rc == pcmk_rc_duplicate_id) {
222  rc = pcmk__xe_foreach_child(state, NULL, delete_single_ticket, cib);
223  } else {
224  rc = delete_single_ticket(state, cib);
225  }
226 
227  if (rc == pcmk_rc_ok) {
228  out->info(out, "Cleaned up %s", ticket_id);
229  }
230 
231  pcmk__xml_free(state);
232  return rc;
233 }
234 
235 int
236 pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
237 {
238  pcmk_scheduler_t *scheduler = NULL;
239  pcmk__output_t *out = NULL;
240  cib_t *cib = NULL;
241  int rc = pcmk_rc_ok;
242 
243  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
244  if (rc != pcmk_rc_ok) {
245  goto done;
246  }
247 
248  rc = pcmk__ticket_delete(out, cib, scheduler, ticket_id, force);
249 
250 done:
251  if (cib != NULL) {
253  }
254 
255  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
257  return rc;
258 }
259 
260 int
262  const char *ticket_id, const char *attr_name,
263  const char *attr_default)
264 {
265  int rc = pcmk_rc_ok;
266  const char *attr_value = NULL;
267  pcmk__ticket_t *ticket = NULL;
268 
269  pcmk__assert((out != NULL) && (scheduler != NULL));
270 
271  if (ticket_id == NULL || attr_name == NULL) {
272  return EINVAL;
273  }
274 
275  ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
276  ticket_id);
277 
278  if (ticket != NULL) {
279  attr_value = g_hash_table_lookup(ticket->state, attr_name);
280  }
281 
282  if (attr_value != NULL) {
283  out->message(out, "ticket-attribute", ticket_id, attr_name, attr_value);
284  } else if (attr_default != NULL) {
285  out->message(out, "ticket-attribute", ticket_id, attr_name, attr_default);
286  } else {
287  rc = ENXIO;
288  }
289 
290  return rc;
291 }
292 
293 int
294 pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
295  const char *attr_name, const char *attr_default)
296 {
297  pcmk_scheduler_t *scheduler = NULL;
298  pcmk__output_t *out = NULL;
299  int rc = pcmk_rc_ok;
300 
301  rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
302  if (rc != pcmk_rc_ok) {
303  goto done;
304  }
305 
306  rc = pcmk__ticket_get_attr(out, scheduler, ticket_id, attr_name, attr_default);
307 
308 done:
309  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
311  return rc;
312 }
313 
314 int
316  const char *ticket_id, bool details, bool raw)
317 {
318  int rc = pcmk_rc_ok;
319 
320  pcmk__assert((out != NULL) && (scheduler != NULL));
321 
322  if (ticket_id != NULL) {
323  GHashTable *tickets = NULL;
324  pcmk__ticket_t *ticket = NULL;
325 
326  ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
327  ticket_id);
328  if (ticket == NULL) {
329  return ENXIO;
330  }
331 
332  /* The ticket-list message expects a GHashTable, so we'll construct
333  * one with just this single item.
334  */
335  tickets = pcmk__strkey_table(free, NULL);
336  g_hash_table_insert(tickets, strdup(ticket->id), ticket);
337  out->message(out, "ticket-list", tickets, false, raw, details);
338  g_hash_table_destroy(tickets);
339 
340  } else {
341  out->message(out, "ticket-list", scheduler->priv->ticket_constraints,
342  false, raw, details);
343  }
344 
345  return rc;
346 }
347 
348 int
349 pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
350 {
351  pcmk_scheduler_t *scheduler = NULL;
352  pcmk__output_t *out = NULL;
353  int rc = pcmk_rc_ok;
354 
355  rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
356  if (rc != pcmk_rc_ok) {
357  goto done;
358  }
359 
361 
362  /* XML output (which is the only format supported by public API functions
363  * due to the use of pcmk__xml_output_new above) always prints all details,
364  * so just pass false for the last two arguments.
365  */
366  rc = pcmk__ticket_info(out, scheduler, ticket_id, false, false);
367 
368 done:
369  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
371  return rc;
372 }
373 
374 int
376  const char *ticket_id, GList *attr_delete, bool force)
377 {
378  xmlNode *ticket_state_xml = NULL;
379  xmlNode *xml_top = NULL;
380  int rc = pcmk_rc_ok;
381 
382  pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
383 
384  if (ticket_id == NULL) {
385  return EINVAL;
386  }
387 
388  /* Nothing to do */
389  if (attr_delete == NULL) {
390  return pcmk_rc_ok;
391  }
392 
393  rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
394 
395  if (rc == pcmk_rc_duplicate_id) {
396  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
397  } else if (rc != pcmk_rc_ok) {
398  pcmk__xml_free(ticket_state_xml);
399  return rc;
400  }
401 
402  for (GList *list_iter = attr_delete; list_iter != NULL; list_iter = list_iter->next) {
403  const char *key = list_iter->data;
404 
405  if (!force && pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)) {
406  pcmk__xml_free(ticket_state_xml);
407  return EACCES;
408  }
409 
410  pcmk__xe_remove_attr(ticket_state_xml, key);
411  }
412 
413  crm_log_xml_debug(xml_top, "Replace");
414  rc = cib->cmds->replace(cib, PCMK_XE_STATUS, ticket_state_xml, cib_sync_call);
415  rc = pcmk_legacy2rc(rc);
416 
417  pcmk__xml_free(xml_top);
418  return rc;
419 }
420 
421 int
422 pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
423 {
424  pcmk_scheduler_t *scheduler = NULL;
425  pcmk__output_t *out = NULL;
426  int rc = pcmk_rc_ok;
427  cib_t *cib = NULL;
428 
429  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
430  if (rc != pcmk_rc_ok) {
431  goto done;
432  }
433 
434  rc = pcmk__ticket_remove_attr(out, cib, scheduler, ticket_id, attr_delete, force);
435 
436 done:
437  if (cib != NULL) {
439  }
440 
441  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
443  return rc;
444 }
445 
446 int
448  const char *ticket_id, GHashTable *attr_set, bool force)
449 {
450  xmlNode *ticket_state_xml = NULL;
451  xmlNode *xml_top = NULL;
452  int rc = pcmk_rc_ok;
453 
454  pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
455 
456  if (ticket_id == NULL) {
457  return EINVAL;
458  }
459 
460  /* Nothing to do */
461  if (attr_set == NULL || g_hash_table_size(attr_set) == 0) {
462  return pcmk_rc_ok;
463  }
464 
465  rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
466 
467  if (rc == pcmk_rc_duplicate_id) {
468  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
469  } else if (rc != pcmk_rc_ok) {
470  pcmk__xml_free(ticket_state_xml);
471  return rc;
472  }
473 
474  if (!force && g_hash_table_lookup(attr_set, PCMK__XA_GRANTED)) {
475  pcmk__xml_free(ticket_state_xml);
476  return EACCES;
477  }
478 
479  add_attribute_xml(scheduler, ticket_id, attr_set, &ticket_state_xml);
480 
481  crm_log_xml_debug(xml_top, "Update");
482  rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call);
483  rc = pcmk_legacy2rc(rc);
484 
485  pcmk__xml_free(xml_top);
486  return rc;
487 }
488 
489 int
490 pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
491  bool force)
492 {
493  pcmk_scheduler_t *scheduler = NULL;
494  pcmk__output_t *out = NULL;
495  int rc = pcmk_rc_ok;
496  cib_t *cib = NULL;
497 
498  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
499  if (rc != pcmk_rc_ok) {
500  goto done;
501  }
502 
503  rc = pcmk__ticket_set_attr(out, cib, scheduler, ticket_id, attr_set, force);
504 
505 done:
506  if (cib != NULL) {
508  }
509 
510  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
512  return rc;
513 }
514 
515 int
516 pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
517 {
518  xmlNode *state_xml = NULL;
519  int rc = pcmk_rc_ok;
520 
521  pcmk__assert((out != NULL) && (cib != NULL));
522 
523  rc = pcmk__get_ticket_state(cib, ticket_id, &state_xml);
524 
525  if (rc == pcmk_rc_duplicate_id) {
526  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
527  ticket_id);
528  }
529 
530  if (state_xml != NULL) {
531  out->message(out, "ticket-state", state_xml);
532  pcmk__xml_free(state_xml);
533  }
534 
535  return rc;
536 }
537 
538 int
539 pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
540 {
541  pcmk__output_t *out = NULL;
542  int rc = pcmk_rc_ok;
543  cib_t *cib = NULL;
544 
545  rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
546  if (rc != pcmk_rc_ok) {
547  goto done;
548  }
549 
550  rc = pcmk__ticket_state(out, cib, ticket_id);
551 
552 done:
553  if (cib != NULL) {
555  }
556 
557  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
558  return rc;
559 }
#define PCMK__XE_TICKET_STATE
#define PCMK_XE_CIB
Definition: xml_names.h:79
#define PCMK_XE_STATUS
Definition: xml_names.h:204
int(* message)(pcmk__output_t *out, const char *message_id,...)
int pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
Definition: pcmk_ticket.c:78
#define PCMK_XE_CONSTRAINTS
Definition: xml_names.h:89
G_GNUC_INTERNAL int pcmk__setup_output_cib_sched(pcmk__output_t **out, cib_t **cib, pcmk_scheduler_t **scheduler, xmlNode **xml)
Definition: pcmk_setup.c:42
int pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set, bool force)
Set the given attribute(s) on a ticket.
Definition: pcmk_ticket.c:490
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition: results.c:810
int pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler, const char *ticket_id, bool details, bool raw)
Return information about the given ticket.
Definition: pcmk_ticket.c:315
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml_element.c:979
High Level API.
int pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, bool force)
Definition: pcmk_ticket.c:180
int pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
Return a ticket&#39;s state XML.
Definition: pcmk_ticket.c:539
int pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
Delete a ticket&#39;s state from the local cluster site.
Definition: pcmk_ticket.c:236
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition: output.c:271
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml_element.c:339
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
int pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
Remove the given attribute(s) from a ticket.
Definition: pcmk_ticket.c:422
cib_api_operations_t * cmds
Definition: cib_types.h:325
int pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
Return information about the given ticket.
Definition: pcmk_ticket.c:349
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
int pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
Return constraints that apply to the given ticket.
Definition: pcmk_ticket.c:145
int pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id, const char *attr_name, const char *attr_default)
Return the value of a ticket&#39;s attribute.
Definition: pcmk_ticket.c:294
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:169
#define crm_log_xml_debug(xml, text)
Definition: logging.h:379
int pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, GHashTable *attr_set, bool force)
Set the given attribute(s) on a ticket.
Definition: pcmk_ticket.c:447
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:151
#define PCMK_XA_ID
Definition: xml_names.h:301
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:667
int pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
Definition: pcmk_ticket.c:516
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:172
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3470
#define PCMK_XE_TICKETS
Definition: xml_names.h:213
int cib__clean_up_connection(cib_t **cib)
Definition: cib_utils.c:920
const char * pcmk_cib_xpath_for(const char *element_name)
Get the relative XPath needed to find a specified CIB element name.
Definition: cib.c:112
#define pcmk__assert(expr)
void pe_free_working_set(pcmk_scheduler_t *scheduler)
Free scheduler data.
Definition: status.c:56
GHashTable * state
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:685
pcmk__action_result_t result
Definition: pcmk_fence.c:37
#define PCMK_XE_RSC_TICKET
Definition: xml_names.h:190
pcmk_scheduler_t * scheduler
#define PCMK_XA_TICKET
Definition: xml_names.h:426
int pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, GList *attr_delete, bool force)
Remove the given attribute(s) from a ticket.
Definition: pcmk_ticket.c:375
#define PCMK__XA_GRANTED
This structure contains everything that makes up a single output formatter.
gboolean crm_is_true(const char *s)
Definition: strings.c:490
#define PCMK_XA_LAST_GRANTED
Definition: xml_names.h:315
int pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler, const char *ticket_id, const char *attr_name, const char *attr_default)
Definition: pcmk_ticket.c:261
int pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
Definition: pcmk_ticket.c:113
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:174
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1