pacemaker 2.1.6-6fdc9deea29
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
rules.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-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#include <crm_internal.h>
11#include <crm/crm.h>
12#include <crm/msg_xml.h>
13#include <crm/common/xml.h>
15
16#include <glib.h>
17
18#include <crm/pengine/rules.h>
21
22#include <sys/types.h>
23#include <regex.h>
24#include <ctype.h>
25
27
38gboolean
39pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
40 crm_time_t *next_change)
41{
42 pe_rule_eval_data_t rule_data = {
43 .node_hash = node_hash,
44 .role = RSC_ROLE_UNKNOWN,
45 .now = now,
46 .match_data = NULL,
47 .rsc_data = NULL,
48 .op_data = NULL
49 };
50
51 return pe_eval_rules(ruleset, &rule_data, next_change);
52}
53
54gboolean
55pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
56 crm_time_t *now, crm_time_t *next_change,
57 pe_match_data_t *match_data)
58{
59 pe_rule_eval_data_t rule_data = {
60 .node_hash = node_hash,
61 .role = role,
62 .now = now,
63 .match_data = match_data,
64 .rsc_data = NULL,
65 .op_data = NULL
66 };
67
68 return pe_eval_expr(rule, &rule_data, next_change);
69}
70
87gboolean
88pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
89 crm_time_t *now, crm_time_t *next_change,
90 pe_match_data_t *match_data)
91{
92 pe_rule_eval_data_t rule_data = {
93 .node_hash = node_hash,
94 .role = role,
95 .now = now,
96 .match_data = match_data,
97 .rsc_data = NULL,
98 .op_data = NULL
99 };
100
101 return pe_eval_subexpr(expr, &rule_data, next_change);
102}
103
105find_expression_type(xmlNode * expr)
106{
107 const char *tag = NULL;
108 const char *attr = NULL;
109
111 tag = crm_element_name(expr);
112
113 if (pcmk__str_eq(tag, PCMK_XE_DATE_EXPRESSION, pcmk__str_none)) {
114 return time_expr;
115
116 } else if (pcmk__str_eq(tag, PCMK_XE_RSC_EXPRESSION, pcmk__str_none)) {
117 return rsc_expr;
118
119 } else if (pcmk__str_eq(tag, PCMK_XE_OP_EXPRESSION, pcmk__str_none)) {
120 return op_expr;
121
122 } else if (pcmk__str_eq(tag, XML_TAG_RULE, pcmk__str_none)) {
123 return nested_rule;
124
125 } else if (!pcmk__str_eq(tag, XML_TAG_EXPRESSION, pcmk__str_none)) {
126 return not_expr;
127
128 } else if (pcmk__str_any_of(attr, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, NULL)) {
129 return loc_expr;
130
131 } else if (pcmk__str_eq(attr, CRM_ATTR_ROLE, pcmk__str_none)) {
132 return role_expr;
133 }
134
135 return attr_expr;
136}
137
138/* As per the nethack rules:
139 *
140 * moon period = 29.53058 days ~= 30, year = 365.2422 days
141 * days moon phase advances on first day of year compared to preceding year
142 * = 365.2422 - 12*29.53058 ~= 11
143 * years in Metonic cycle (time until same phases fall on the same days of
144 * the month) = 18.6 ~= 19
145 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
146 * (29 as initial condition)
147 * current phase in days = first day phase + days elapsed in year
148 * 6 moons ~= 177 days
149 * 177 ~= 8 reported phases * 22
150 * + 11/22 for rounding
151 *
152 * 0-7, with 0: new, 4: full
153 */
154
155static int
156phase_of_the_moon(const crm_time_t *now)
157{
158 uint32_t epact, diy, goldn;
159 uint32_t y;
160
161 crm_time_get_ordinal(now, &y, &diy);
162
163 goldn = (y % 19) + 1;
164 epact = (11 * goldn + 18) % 30;
165 if ((epact == 25 && goldn > 11) || epact == 24)
166 epact++;
167
168 return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
169}
170
171static int
172check_one(const xmlNode *cron_spec, const char *xml_field, uint32_t time_field)
173{
174 int rc = pcmk_rc_undetermined;
175 const char *value = crm_element_value(cron_spec, xml_field);
176 long long low, high;
177
178 if (value == NULL) {
179 /* Return pe_date_result_undetermined if the field is missing. */
180 goto bail;
181 }
182
183 if (pcmk__parse_ll_range(value, &low, &high) != pcmk_rc_ok) {
184 goto bail;
185 } else if (low == high) {
186 /* A single number was given, not a range. */
187 if (time_field < low) {
189 } else if (time_field > high) {
191 } else {
193 }
194 } else if (low != -1 && high != -1) {
195 /* This is a range with both bounds. */
196 if (time_field < low) {
198 } else if (time_field > high) {
200 } else {
202 }
203 } else if (low == -1) {
204 /* This is a range with no starting value. */
205 rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
206 } else if (high == -1) {
207 /* This is a range with no ending value. */
208 rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
209 }
210
211bail:
212 if (rc == pcmk_rc_within_range) {
213 crm_debug("Condition '%s' in %s: passed", value, xml_field);
214 } else {
215 crm_debug("Condition '%s' in %s: failed", value, xml_field);
216 }
217
218 return rc;
219}
220
221static gboolean
222check_passes(int rc) {
223 /* _within_range is obvious. _undetermined is a pass because
224 * this is the return value if a field is not given. In this
225 * case, we just want to ignore it and check other fields to
226 * see if they place some restriction on what can pass.
227 */
228 return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined;
229}
230
231#define CHECK_ONE(spec, name, var) do { \
232 int subpart_rc = check_one(spec, name, var); \
233 if (check_passes(subpart_rc) == FALSE) { \
234 return subpart_rc; \
235 } \
236} while (0)
237
238int
239pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec)
240{
241 uint32_t h, m, s, y, d, w;
242
243 CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied);
244
245 crm_time_get_gregorian(now, &y, &m, &d);
246 CHECK_ONE(cron_spec, "years", y);
247 CHECK_ONE(cron_spec, "months", m);
248 CHECK_ONE(cron_spec, "monthdays", d);
249
250 crm_time_get_timeofday(now, &h, &m, &s);
251 CHECK_ONE(cron_spec, "hours", h);
252 CHECK_ONE(cron_spec, "minutes", m);
253 CHECK_ONE(cron_spec, "seconds", s);
254
255 crm_time_get_ordinal(now, &y, &d);
256 CHECK_ONE(cron_spec, "yeardays", d);
257
258 crm_time_get_isoweek(now, &y, &w, &d);
259 CHECK_ONE(cron_spec, "weekyears", y);
260 CHECK_ONE(cron_spec, "weeks", w);
261 CHECK_ONE(cron_spec, "weekdays", d);
262
263 CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now));
264 if (crm_element_value(cron_spec, "moon") != NULL) {
265 pcmk__config_warn("Support for 'moon' in date_spec elements "
266 "(such as %s) is deprecated and will be removed "
267 "in a future release of Pacemaker", ID(cron_spec));
268 }
269
270 /* If we get here, either no fields were specified (which is success), or all
271 * the fields that were specified had their conditions met (which is also a
272 * success). Thus, the result is success.
273 */
274 return pcmk_rc_ok;
275}
276
277static void
278update_field(crm_time_t *t, const xmlNode *xml, const char *attr,
279 void (*time_fn)(crm_time_t *, int))
280{
281 long long value;
282
283 if ((pcmk__scan_ll(crm_element_value(xml, attr), &value, 0LL) == pcmk_rc_ok)
284 && (value != 0LL) && (value >= INT_MIN) && (value <= INT_MAX)) {
285 time_fn(t, (int) value);
286 }
287}
288
289static crm_time_t *
290parse_xml_duration(const crm_time_t *start, const xmlNode *duration_spec)
291{
292 crm_time_t *end = pcmk_copy_time(start);
293
294 update_field(end, duration_spec, "years", crm_time_add_years);
295 update_field(end, duration_spec, "months", crm_time_add_months);
296 update_field(end, duration_spec, "weeks", crm_time_add_weeks);
297 update_field(end, duration_spec, "days", crm_time_add_days);
298 update_field(end, duration_spec, "hours", crm_time_add_hours);
299 update_field(end, duration_spec, "minutes", crm_time_add_minutes);
300 update_field(end, duration_spec, "seconds", crm_time_add_seconds);
301
302 return end;
303}
304
305// Set next_change to t if t is earlier
306static void
307crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t)
308{
309 if ((next_change != NULL) && (t != NULL)) {
310 if (!crm_time_is_defined(next_change)
311 || (crm_time_compare(t, next_change) < 0)) {
312 crm_time_set(next_change, t);
313 }
314 }
315}
316
317// Information about a block of nvpair elements
318typedef struct sorted_set_s {
319 int score; // This block's score for sorting
320 const char *name; // This block's ID
321 const char *special_name; // ID that should sort first
322 xmlNode *attr_set; // This block
324
325static gint
326sort_pairs(gconstpointer a, gconstpointer b)
327{
328 const sorted_set_t *pair_a = a;
329 const sorted_set_t *pair_b = b;
330
331 if (a == NULL && b == NULL) {
332 return 0;
333 } else if (a == NULL) {
334 return 1;
335 } else if (b == NULL) {
336 return -1;
337 }
338
339 if (pcmk__str_eq(pair_a->name, pair_a->special_name, pcmk__str_casei)) {
340 return -1;
341
342 } else if (pcmk__str_eq(pair_b->name, pair_a->special_name, pcmk__str_casei)) {
343 return 1;
344 }
345
346 if (pair_a->score < pair_b->score) {
347 return 1;
348 } else if (pair_a->score > pair_b->score) {
349 return -1;
350 }
351 return 0;
352}
353
354static void
355populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
356{
357 const char *name = NULL;
358 const char *value = NULL;
359 const char *old_value = NULL;
360 xmlNode *list = nvpair_list;
361 xmlNode *an_attr = NULL;
362
363 name = crm_element_name(list->children);
364 if (pcmk__str_eq(XML_TAG_ATTRS, name, pcmk__str_casei)) {
365 list = list->children;
366 }
367
368 for (an_attr = pcmk__xe_first_child(list); an_attr != NULL;
369 an_attr = pcmk__xe_next(an_attr)) {
370
371 if (pcmk__str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, pcmk__str_none)) {
372 xmlNode *ref_nvpair = expand_idref(an_attr, top);
373
375 if (name == NULL) {
377 }
378
380 if (value == NULL) {
381 value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
382 }
383
384 if (name == NULL || value == NULL) {
385 continue;
386 }
387
388 old_value = g_hash_table_lookup(hash, name);
389
390 if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
391 if (old_value) {
392 crm_trace("Letting %s default (removing explicit value \"%s\")",
393 name, value);
394 g_hash_table_remove(hash, name);
395 }
396 continue;
397
398 } else if (old_value == NULL) {
399 crm_trace("Setting %s=\"%s\"", name, value);
400 g_hash_table_insert(hash, strdup(name), strdup(value));
401
402 } else if (overwrite) {
403 crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
404 name, value, old_value);
405 g_hash_table_replace(hash, strdup(name), strdup(value));
406 }
407 }
408 }
409}
410
411typedef struct unpack_data_s {
412 gboolean overwrite;
413 void *hash;
414 crm_time_t *next_change;
415 const pe_rule_eval_data_t *rule_data;
416 xmlNode *top;
418
419static void
420unpack_attr_set(gpointer data, gpointer user_data)
421{
422 sorted_set_t *pair = data;
423 unpack_data_t *unpack_data = user_data;
424
425 if (!pe_eval_rules(pair->attr_set, unpack_data->rule_data,
426 unpack_data->next_change)) {
427 return;
428 }
429
430 crm_trace("Adding attributes from %s (score %d) %s overwrite",
431 pair->name, pair->score,
432 (unpack_data->overwrite? "with" : "without"));
433 populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
434}
435
447static GList *
448make_pairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
449 const char *always_first)
450{
451 GList *unsorted = NULL;
452
453 if (xml_obj == NULL) {
454 return NULL;
455 }
456 for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL;
457 attr_set = pcmk__xe_next(attr_set)) {
458
459 if (pcmk__str_eq(set_name, (const char *) attr_set->name,
461 const char *score = NULL;
462 sorted_set_t *pair = NULL;
463 xmlNode *expanded_attr_set = expand_idref(attr_set, top);
464
465 if (expanded_attr_set == NULL) {
466 // Schema (if not "none") prevents this
467 continue;
468 }
469
470 pair = calloc(1, sizeof(sorted_set_t));
471 pair->name = ID(expanded_attr_set);
472 pair->special_name = always_first;
473 pair->attr_set = expanded_attr_set;
474
475 score = crm_element_value(expanded_attr_set, XML_RULE_ATTR_SCORE);
476 pair->score = char2score(score);
477
478 unsorted = g_list_prepend(unsorted, pair);
479 }
480 }
481 return g_list_sort(unsorted, sort_pairs);
482}
483
496void
497pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
498 const pe_rule_eval_data_t *rule_data, GHashTable *hash,
499 const char *always_first, gboolean overwrite,
500 crm_time_t *next_change)
501{
502 GList *pairs = make_pairs(top, xml_obj, set_name, always_first);
503
504 if (pairs) {
506 .hash = hash,
507 .overwrite = overwrite,
508 .next_change = next_change,
509 .top = top,
510 .rule_data = rule_data
511 };
512
513 g_list_foreach(pairs, unpack_attr_set, &data);
514 g_list_free_full(pairs, free);
515 }
516}
517
531void
532pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
533 GHashTable *node_hash, GHashTable *hash,
534 const char *always_first, gboolean overwrite,
535 crm_time_t *now, crm_time_t *next_change)
536{
537 pe_rule_eval_data_t rule_data = {
538 .node_hash = node_hash,
539 .role = RSC_ROLE_UNKNOWN,
540 .now = now,
541 .match_data = NULL,
542 .rsc_data = NULL,
543 .op_data = NULL
544 };
545
546 pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash,
547 always_first, overwrite, next_change);
548}
549
559char *
560pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
561{
562 size_t len = 0;
563 int i;
564 const char *p, *last_match_index;
565 char *p_dst, *result = NULL;
566
567 if (pcmk__str_empty(string) || !match_data) {
568 return NULL;
569 }
570
571 p = last_match_index = string;
572
573 while (*p) {
574 if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
575 i = *(p + 1) - '0';
576 if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
577 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
578 len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
579 last_match_index = p + 2;
580 }
581 p++;
582 }
583 p++;
584 }
585 len += p - last_match_index + 1;
586
587 /* FIXME: Excessive? */
588 if (len - 1 <= 0) {
589 return NULL;
590 }
591
592 p_dst = result = calloc(1, len);
593 p = string;
594
595 while (*p) {
596 if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
597 i = *(p + 1) - '0';
598 if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
599 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
600 /* rm_eo can be equal to rm_so, but then there is nothing to do */
601 int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
602 memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
603 p_dst += match_len;
604 }
605 p++;
606 } else {
607 *(p_dst) = *(p);
608 p_dst++;
609 }
610 p++;
611 }
612
613 return result;
614}
615
625gboolean
626pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
627 crm_time_t *next_change)
628{
629 // If there are no rules, pass by default
630 gboolean ruleset_default = TRUE;
631
632 for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE);
633 rule != NULL; rule = crm_next_same_xml(rule)) {
634
635 ruleset_default = FALSE;
636 if (pe_eval_expr(rule, rule_data, next_change)) {
637 /* Only the deprecated "lifetime" element of location constraints
638 * may contain more than one rule at the top level -- the schema
639 * limits a block of nvpairs to a single top-level rule. So, this
640 * effectively means that a lifetime is active if any rule it
641 * contains is active.
642 */
643 return TRUE;
644 }
645 }
646
647 return ruleset_default;
648}
649
659gboolean
660pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
661 crm_time_t *next_change)
662{
663 xmlNode *expr = NULL;
664 gboolean test = TRUE;
665 gboolean empty = TRUE;
666 gboolean passed = TRUE;
667 gboolean do_and = TRUE;
668 const char *value = NULL;
669
670 rule = expand_idref(rule, NULL);
672 if (pcmk__str_eq(value, "or", pcmk__str_casei)) {
673 do_and = FALSE;
674 passed = FALSE;
675 }
676
677 crm_trace("Testing rule %s", ID(rule));
678 for (expr = pcmk__xe_first_child(rule); expr != NULL;
679 expr = pcmk__xe_next(expr)) {
680
681 test = pe_eval_subexpr(expr, rule_data, next_change);
682 empty = FALSE;
683
684 if (test && do_and == FALSE) {
685 crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
686 return TRUE;
687
688 } else if (test == FALSE && do_and) {
689 crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
690 return FALSE;
691 }
692 }
693
694 if (empty) {
695 crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
696 }
697
698 crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
699 return passed;
700}
701
711gboolean
712pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
713 crm_time_t *next_change)
714{
715 gboolean accept = FALSE;
716 const char *uname = NULL;
717
718 switch (find_expression_type(expr)) {
719 case nested_rule:
720 accept = pe_eval_expr(expr, rule_data, next_change);
721 break;
722 case attr_expr:
723 case loc_expr:
724 /* these expressions can never succeed if there is
725 * no node to compare with
726 */
727 if (rule_data->node_hash != NULL) {
728 accept = pe__eval_attr_expr(expr, rule_data);
729 }
730 break;
731
732 case time_expr:
733 switch (pe__eval_date_expr(expr, rule_data, next_change)) {
735 case pcmk_rc_ok:
736 accept = TRUE;
737 break;
738
739 default:
740 accept = FALSE;
741 break;
742 }
743 break;
744
745 case role_expr:
746 accept = pe__eval_role_expr(expr, rule_data);
747 break;
748
749 case rsc_expr:
750 accept = pe__eval_rsc_expr(expr, rule_data);
751 break;
752
753 case op_expr:
754 accept = pe__eval_op_expr(expr, rule_data);
755 break;
756
757 default:
758 CRM_CHECK(FALSE /* bad type */ , return FALSE);
759 accept = FALSE;
760 }
761 if (rule_data->node_hash) {
762 uname = g_hash_table_lookup(rule_data->node_hash, CRM_ATTR_UNAME);
763 }
764
765 crm_trace("Expression %s %s on %s",
766 ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
767 return accept;
768}
769
785static int
786compare_attr_expr_vals(const char *l_val, const char *r_val, const char *type,
787 const char *op)
788{
789 int cmp = 0;
790
791 if (l_val != NULL && r_val != NULL) {
792 if (type == NULL) {
793 if (pcmk__strcase_any_of(op, "lt", "lte", "gt", "gte", NULL)) {
794 if (pcmk__char_in_any_str('.', l_val, r_val, NULL)) {
795 type = "number";
796 } else {
797 type = "integer";
798 }
799
800 } else {
801 type = "string";
802 }
803 crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
804 }
805
806 if (pcmk__str_eq(type, "string", pcmk__str_casei)) {
807 cmp = strcasecmp(l_val, r_val);
808
809 } else if (pcmk__str_eq(type, "integer", pcmk__str_casei)) {
810 long long l_val_num;
811 int rc1 = pcmk__scan_ll(l_val, &l_val_num, 0LL);
812
813 long long r_val_num;
814 int rc2 = pcmk__scan_ll(r_val, &r_val_num, 0LL);
815
816 if ((rc1 == pcmk_rc_ok) && (rc2 == pcmk_rc_ok)) {
817 if (l_val_num < r_val_num) {
818 cmp = -1;
819 } else if (l_val_num > r_val_num) {
820 cmp = 1;
821 } else {
822 cmp = 0;
823 }
824
825 } else {
826 crm_debug("Integer parse error. Comparing %s and %s as strings",
827 l_val, r_val);
828 cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
829 }
830
831 } else if (pcmk__str_eq(type, "number", pcmk__str_casei)) {
832 double l_val_num;
833 double r_val_num;
834
835 int rc1 = pcmk__scan_double(l_val, &l_val_num, NULL, NULL);
836 int rc2 = pcmk__scan_double(r_val, &r_val_num, NULL, NULL);
837
838 if (rc1 == pcmk_rc_ok && rc2 == pcmk_rc_ok) {
839 if (l_val_num < r_val_num) {
840 cmp = -1;
841 } else if (l_val_num > r_val_num) {
842 cmp = 1;
843 } else {
844 cmp = 0;
845 }
846
847 } else {
848 crm_debug("Floating-point parse error. Comparing %s and %s as "
849 "strings", l_val, r_val);
850 cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
851 }
852
853 } else if (pcmk__str_eq(type, "version", pcmk__str_casei)) {
854 cmp = compare_version(l_val, r_val);
855
856 }
857
858 } else if (l_val == NULL && r_val == NULL) {
859 cmp = 0;
860 } else if (r_val == NULL) {
861 cmp = 1;
862 } else { // l_val == NULL && r_val != NULL
863 cmp = -1;
864 }
865
866 return cmp;
867}
868
883static bool
884accept_attr_expr(const char *l_val, const char *r_val, const char *type,
885 const char *op)
886{
887 int cmp;
888
889 if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
890 return (l_val != NULL);
891
892 } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
893 return (l_val == NULL);
894
895 }
896
897 cmp = compare_attr_expr_vals(l_val, r_val, type, op);
898
899 if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
900 return (cmp == 0);
901
902 } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
903 return (cmp != 0);
904
905 } else if (l_val == NULL || r_val == NULL) {
906 // The comparison is meaningless from this point on
907 return false;
908
909 } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
910 return (cmp < 0);
911
912 } else if (pcmk__str_eq(op, "lte", pcmk__str_casei)) {
913 return (cmp <= 0);
914
915 } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
916 return (cmp > 0);
917
918 } else if (pcmk__str_eq(op, "gte", pcmk__str_casei)) {
919 return (cmp >= 0);
920 }
921
922 return false; // Should never reach this point
923}
924
933static const char *
934expand_value_source(const char *value, const char *value_source,
935 const pe_match_data_t *match_data)
936{
937 GHashTable *table = NULL;
938
939 if (pcmk__str_empty(value)) {
940 return NULL; // value_source is irrelevant
941
942 } else if (pcmk__str_eq(value_source, "param", pcmk__str_casei)) {
943 table = match_data->params;
944
945 } else if (pcmk__str_eq(value_source, "meta", pcmk__str_casei)) {
946 table = match_data->meta;
947
948 } else { // literal
949 return value;
950 }
951
952 if (table == NULL) {
953 return NULL;
954 }
955 return (const char *) g_hash_table_lookup(table, value);
956}
957
968gboolean
969pe__eval_attr_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
970{
971 gboolean attr_allocated = FALSE;
972 const char *h_val = NULL;
973
974 const char *op = NULL;
975 const char *type = NULL;
976 const char *attr = NULL;
977 const char *value = NULL;
978 const char *value_source = NULL;
979
984 value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
985
986 if (attr == NULL) {
987 pe_err("Expression %s invalid: " XML_EXPR_ATTR_ATTRIBUTE
988 " not specified", pcmk__s(ID(expr), "without ID"));
989 return FALSE;
990 } else if (op == NULL) {
991 pe_err("Expression %s invalid: " XML_EXPR_ATTR_OPERATION
992 " not specified", pcmk__s(ID(expr), "without ID"));
993 }
994
995 if (rule_data->match_data != NULL) {
996 // Expand any regular expression submatches (%0-%9) in attribute name
997 if (rule_data->match_data->re != NULL) {
998 char *resolved_attr = pe_expand_re_matches(attr, rule_data->match_data->re);
999
1000 if (resolved_attr != NULL) {
1001 attr = (const char *) resolved_attr;
1002 attr_allocated = TRUE;
1003 }
1004 }
1005
1006 // Get value appropriate to value-source
1007 value = expand_value_source(value, value_source, rule_data->match_data);
1008 }
1009
1010 if (rule_data->node_hash != NULL) {
1011 h_val = (const char *)g_hash_table_lookup(rule_data->node_hash, attr);
1012 }
1013
1014 if (attr_allocated) {
1015 free((char *)attr);
1016 attr = NULL;
1017 }
1018
1019 return accept_attr_expr(h_val, value, type, op);
1020}
1021
1032int
1033pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data,
1034 crm_time_t *next_change)
1035{
1036 crm_time_t *start = NULL;
1037 crm_time_t *end = NULL;
1038 const char *value = NULL;
1039 const char *op = crm_element_value(expr, "operation");
1040
1041 xmlNode *duration_spec = NULL;
1042 xmlNode *date_spec = NULL;
1043
1044 // "undetermined" will also be returned for parsing errors
1045 int rc = pcmk_rc_undetermined;
1046
1047 crm_trace("Testing expression: %s", ID(expr));
1048
1049 duration_spec = first_named_child(expr, "duration");
1050 date_spec = first_named_child(expr, "date_spec");
1051
1052 value = crm_element_value(expr, "start");
1053 if (value != NULL) {
1054 start = crm_time_new(value);
1055 }
1056 value = crm_element_value(expr, "end");
1057 if (value != NULL) {
1058 end = crm_time_new(value);
1059 }
1060
1061 if (start != NULL && end == NULL && duration_spec != NULL) {
1062 end = parse_xml_duration(start, duration_spec);
1063 }
1064
1065 if (pcmk__str_eq(op, "in_range", pcmk__str_null_matches | pcmk__str_casei)) {
1066 if ((start == NULL) && (end == NULL)) {
1067 // in_range requires at least one of start or end
1068 } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) {
1070 crm_time_set_if_earlier(next_change, start);
1071 } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) {
1073 } else {
1075 if (end && next_change) {
1076 // Evaluation doesn't change until second after end
1077 crm_time_add_seconds(end, 1);
1078 crm_time_set_if_earlier(next_change, end);
1079 }
1080 }
1081
1082 } else if (pcmk__str_eq(op, "date_spec", pcmk__str_casei)) {
1083 rc = pe_cron_range_satisfied(rule_data->now, date_spec);
1084 // @TODO set next_change appropriately
1085
1086 } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1087 if (start == NULL) {
1088 // gt requires start
1089 } else if (crm_time_compare(rule_data->now, start) > 0) {
1091 } else {
1093
1094 // Evaluation doesn't change until second after start
1095 crm_time_add_seconds(start, 1);
1096 crm_time_set_if_earlier(next_change, start);
1097 }
1098
1099 } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1100 if (end == NULL) {
1101 // lt requires end
1102 } else if (crm_time_compare(rule_data->now, end) < 0) {
1104 crm_time_set_if_earlier(next_change, end);
1105 } else {
1107 }
1108 }
1109
1110 crm_time_free(start);
1111 crm_time_free(end);
1112 return rc;
1113}
1114
1115gboolean
1116pe__eval_op_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1117{
1118 const char *name = crm_element_value(expr, XML_NVPAIR_ATTR_NAME);
1119 const char *interval_s = crm_element_value(expr, XML_LRM_ATTR_INTERVAL);
1120 guint interval;
1121
1122 crm_trace("Testing op_defaults expression: %s", ID(expr));
1123
1124 if (rule_data->op_data == NULL) {
1125 crm_trace("No operations data provided");
1126 return FALSE;
1127 }
1128
1129 interval = crm_parse_interval_spec(interval_s);
1130 if (interval == 0 && errno != 0) {
1131 crm_trace("Could not parse interval: %s", interval_s);
1132 return FALSE;
1133 }
1134
1135 if (interval_s != NULL && interval != rule_data->op_data->interval) {
1136 crm_trace("Interval doesn't match: %d != %d", interval, rule_data->op_data->interval);
1137 return FALSE;
1138 }
1139
1140 if (!pcmk__str_eq(name, rule_data->op_data->op_name, pcmk__str_none)) {
1141 crm_trace("Name doesn't match: %s != %s", name, rule_data->op_data->op_name);
1142 return FALSE;
1143 }
1144
1145 return TRUE;
1146}
1147
1157gboolean
1158pe__eval_role_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1159{
1160 gboolean accept = FALSE;
1161 const char *op = NULL;
1162 const char *value = NULL;
1163
1164 if (rule_data->role == RSC_ROLE_UNKNOWN) {
1165 return accept;
1166 }
1167
1170
1171 if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
1172 if (rule_data->role > RSC_ROLE_STARTED) {
1173 accept = TRUE;
1174 }
1175
1176 } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
1177 if ((rule_data->role > RSC_ROLE_UNKNOWN)
1178 && (rule_data->role < RSC_ROLE_UNPROMOTED)) {
1179 accept = TRUE;
1180 }
1181
1182 } else if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1183 if (text2role(value) == rule_data->role) {
1184 accept = TRUE;
1185 }
1186
1187 } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1188 // Test "ne" only with promotable clone roles
1189 if ((rule_data->role > RSC_ROLE_UNKNOWN)
1190 && (rule_data->role < RSC_ROLE_UNPROMOTED)) {
1191 accept = FALSE;
1192
1193 } else if (text2role(value) != rule_data->role) {
1194 accept = TRUE;
1195 }
1196 }
1197 return accept;
1198}
1199
1200gboolean
1201pe__eval_rsc_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1202{
1203 const char *class = crm_element_value(expr, XML_AGENT_ATTR_CLASS);
1204 const char *provider = crm_element_value(expr, XML_AGENT_ATTR_PROVIDER);
1205 const char *type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
1206
1207 crm_trace("Testing rsc_defaults expression: %s", ID(expr));
1208
1209 if (rule_data->rsc_data == NULL) {
1210 crm_trace("No resource data provided");
1211 return FALSE;
1212 }
1213
1214 if (class != NULL &&
1215 !pcmk__str_eq(class, rule_data->rsc_data->standard, pcmk__str_none)) {
1216 crm_trace("Class doesn't match: %s != %s", class, rule_data->rsc_data->standard);
1217 return FALSE;
1218 }
1219
1220 if ((provider == NULL && rule_data->rsc_data->provider != NULL) ||
1221 (provider != NULL && rule_data->rsc_data->provider == NULL) ||
1222 !pcmk__str_eq(provider, rule_data->rsc_data->provider, pcmk__str_none)) {
1223 crm_trace("Provider doesn't match: %s != %s", provider, rule_data->rsc_data->provider);
1224 return FALSE;
1225 }
1226
1227 if (type != NULL &&
1228 !pcmk__str_eq(type, rule_data->rsc_data->agent, pcmk__str_none)) {
1229 crm_trace("Agent doesn't match: %s != %s", type, rule_data->rsc_data->agent);
1230 return FALSE;
1231 }
1232
1233 return TRUE;
1234}
1235
1236// Deprecated functions kept only for backward API compatibility
1237// LCOV_EXCL_START
1238
1240
1241gboolean
1242test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1243{
1244 return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1245}
1246
1247gboolean
1248test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1249{
1250 return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
1251}
1252
1253gboolean
1254pe_test_rule_re(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1255{
1256 pe_match_data_t match_data = {
1257 .re = re_match_data,
1258 .params = NULL,
1259 .meta = NULL,
1260 };
1261 return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
1262}
1263
1264gboolean
1265pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1266 crm_time_t *now, pe_match_data_t *match_data)
1267{
1268 return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
1269}
1270
1271gboolean
1272test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1273{
1274 return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
1275}
1276
1277gboolean
1278pe_test_expression_re(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1279{
1280 pe_match_data_t match_data = {
1281 .re = re_match_data,
1282 .params = NULL,
1283 .meta = NULL,
1284 };
1285 return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
1286}
1287
1288gboolean
1289pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1290 enum rsc_role_e role, crm_time_t *now,
1291 pe_match_data_t *match_data)
1292{
1293 return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
1294}
1295
1296void
1297unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1298 GHashTable *node_hash, GHashTable *hash,
1299 const char *always_first, gboolean overwrite,
1300 crm_time_t *now)
1301{
1302 pe_rule_eval_data_t rule_data = {
1303 .node_hash = node_hash,
1304 .role = RSC_ROLE_UNKNOWN,
1305 .now = now,
1306 .match_data = NULL,
1307 .rsc_data = NULL,
1308 .op_data = NULL
1309 };
1310
1311 pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash, always_first,
1312 overwrite, NULL);
1313}
1314
1315// LCOV_EXCL_STOP
1316// End deprecated API
const char * name
Definition cib.c:24
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition utils.c:271
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
int compare_version(const char *version1, const char *version2)
Definition utils.c:189
rsc_role_e
Possible roles that a resource can be in.
Definition common.h:92
@ RSC_ROLE_STARTED
Definition common.h:95
@ RSC_ROLE_UNKNOWN
Definition common.h:93
@ RSC_ROLE_UNPROMOTED
Definition common.h:96
enum rsc_role_e text2role(const char *role)
Definition common.c:479
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
enum crm_ais_msg_types type
Definition cpg.c:3
char uname[MAX_NAME]
Definition cpg.c:5
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_ATTR_ROLE
Definition crm.h:116
#define CRM_ATTR_KIND
Definition crm.h:115
#define CRM_ATTR_UNAME
Definition crm.h:113
#define CRM_ATTR_ID
Definition crm.h:114
void crm_time_add_hours(crm_time_t *dt, int value)
Definition iso8601.c:1642
void crm_time_add_seconds(crm_time_t *dt, int value)
Add a given number of seconds to a date/time or duration.
Definition iso8601.c:1548
void crm_time_set(crm_time_t *target, const crm_time_t *source)
Definition iso8601.c:1305
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition iso8601.c:1648
void crm_time_add_days(crm_time_t *dt, int value)
Definition iso8601.c:1568
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition iso8601.c:398
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition iso8601.c:1374
void crm_time_add_years(crm_time_t *dt, int value)
Definition iso8601.c:1654
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition iso8601.c:142
void crm_time_add_months(crm_time_t *dt, int value)
Definition iso8601.c:1593
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:109
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition iso8601.c:299
struct crm_time_s crm_time_t
Definition iso8601.h:32
int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
Definition iso8601.c:365
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition iso8601.c:1518
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition iso8601.c:406
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition iso8601.c:1636
#define CRM_TRACE_INIT_DATA(name)
Definition logging.h:134
#define CRM_CHECK(expr, failure_action)
Definition logging.h:235
#define crm_debug(fmt, args...)
Definition logging.h:380
#define crm_err(fmt, args...)
Definition logging.h:375
#define crm_trace(fmt, args...)
Definition logging.h:381
#define pcmk__config_warn(fmt...)
#define XML_RULE_ATTR_BOOLEAN_OP
Definition msg_xml.h:354
#define ID(x)
Definition msg_xml.h:480
#define XML_EXPR_ATTR_TYPE
Definition msg_xml.h:360
#define XML_NVPAIR_ATTR_VALUE
Definition msg_xml.h:404
#define XML_TAG_EXPRESSION
Definition msg_xml.h:356
#define XML_RULE_ATTR_SCORE
Definition msg_xml.h:351
#define XML_EXPR_ATTR_OPERATION
Definition msg_xml.h:358
#define PCMK_XE_OP_EXPRESSION
Definition msg_xml.h:37
#define XML_TAG_RULE
Definition msg_xml.h:350
#define XML_LRM_ATTR_INTERVAL
Definition msg_xml.h:309
#define XML_AGENT_ATTR_PROVIDER
Definition msg_xml.h:283
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:282
#define XML_TAG_ATTRS
Definition msg_xml.h:224
#define PCMK_XE_RSC_EXPRESSION
Definition msg_xml.h:44
#define XML_NVPAIR_ATTR_NAME
Definition msg_xml.h:403
#define XML_EXPR_ATTR_VALUE
Definition msg_xml.h:359
#define XML_CIB_TAG_NVPAIR
Definition msg_xml.h:219
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition msg_xml.h:361
#define PCMK_XE_DATE_EXPRESSION
Definition msg_xml.h:36
#define XML_EXPR_ATTR_ATTRIBUTE
Definition msg_xml.h:357
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:496
pcmk__action_result_t result
Definition pcmk_fence.c:35
#define pe_err(fmt...)
Definition internal.h:52
@ pcmk_rc_before_range
Definition results.h:124
@ pcmk_rc_op_unsatisfied
Definition results.h:126
@ pcmk_rc_ok
Definition results.h:151
@ pcmk_rc_undetermined
Definition results.h:125
@ pcmk_rc_within_range
Definition results.h:123
@ pcmk_rc_after_range
Definition results.h:122
gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_re_match_data_t *re_match_data)
Definition rules.c:1254
struct sorted_set_s sorted_set_t
enum expression_type find_expression_type(xmlNode *expr)
Definition rules.c:105
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition rules.c:1248
gboolean pe__eval_op_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition rules.c:1116
char * pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
Expand any regular expression submatches (%0-%9) in a string.
Definition rules.c:560
gboolean pe__eval_rsc_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition rules.c:1201
gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition rules.c:1289
gboolean pe__eval_role_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition rules.c:1158
int pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec)
Definition rules.c:239
int pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition rules.c:1033
gboolean pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Definition rules.c:55
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Evaluate any rules contained by given XML element.
Definition rules.c:39
struct unpack_data_s unpack_data_t
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition rules.c:1272
gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate a single rule expression, including any subexpressions.
Definition rules.c:712
gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition rules.c:1265
gboolean pe__eval_attr_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition rules.c:969
gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate rules.
Definition rules.c:626
void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition rules.c:532
void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now)
Definition rules.c:1297
gboolean pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Evaluate one rule subelement (pass/fail)
Definition rules.c:88
#define CHECK_ONE(spec, name, var)
Definition rules.c:231
gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_re_match_data_t *re_match_data)
Definition rules.c:1278
gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate all of a rule's expressions.
Definition rules.c:660
void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, const pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition rules.c:497
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition rules.c:1242
expression_type
Definition rules.h:22
@ not_expr
Definition rules.h:23
@ rsc_expr
Definition rules.h:33
@ op_expr
Definition rules.h:34
@ role_expr
Definition rules.h:27
@ attr_expr
Definition rules.h:25
@ nested_rule
Definition rules.h:24
@ time_expr
Definition rules.h:28
@ loc_expr
Definition rules.h:26
Deprecated Pacemaker rule API.
bool pcmk__char_in_any_str(int ch,...) G_GNUC_NULL_TERMINATED
Definition strings.c:980
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition strings.c:199
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:933
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:957
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition strings.c:810
GHashTable * meta
Definition common.h:178
pe_re_match_data_t * re
Definition common.h:176
GHashTable * params
Definition common.h:177
const char * op_name
Definition common.h:188
guint interval
Definition common.h:189
regmatch_t * pmatch
Definition common.h:172
char * string
Definition common.h:170
const char * agent
Definition common.h:184
const char * provider
Definition common.h:183
const char * standard
Definition common.h:182
pe_rsc_eval_data_t * rsc_data
Definition common.h:197
GHashTable * node_hash
Definition common.h:193
enum rsc_role_e role
Definition common.h:194
crm_time_t * now
Definition common.h:195
pe_match_data_t * match_data
Definition common.h:196
pe_op_eval_data_t * op_data
Definition common.h:198
Wrappers for and extensions to libxml2.
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2593
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2521
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition xml.c:2547