pacemaker  1.1.24-3850484742
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 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>
14 
15 #include <glib.h>
16 
17 #include <crm/pengine/rules.h>
18 #include <crm/pengine/internal.h>
19 
20 #include <sys/types.h>
21 #include <regex.h>
22 #include <ctype.h>
23 
24 CRM_TRACE_INIT_DATA(pe_rules);
25 
26 crm_time_t *parse_xml_duration(crm_time_t * start, xmlNode * duration_spec);
27 
28 gboolean test_date_expression(xmlNode * time_expr, crm_time_t * now);
29 gboolean cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec);
30 gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now);
31 gboolean pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data);
32 gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now);
33 
34 gboolean
35 test_ruleset(xmlNode * ruleset, GHashTable * node_hash, crm_time_t * now)
36 {
37  gboolean ruleset_default = TRUE;
38  xmlNode *rule = NULL;
39 
40  for (rule = __xml_first_child_element(ruleset); rule != NULL;
41  rule = __xml_next_element(rule)) {
42 
43  if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
44  ruleset_default = FALSE;
45  if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) {
46  return TRUE;
47  }
48  }
49  }
50 
51  return ruleset_default;
52 }
53 
54 gboolean
55 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
56 {
57  return pe_test_rule_full(rule, node_hash, role, now, NULL);
58 }
59 
60 gboolean
61 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)
62 {
63  pe_match_data_t match_data = {
64  .re = re_match_data,
65  .params = NULL,
66  .meta = NULL,
67  };
68  return pe_test_rule_full(rule, node_hash, role, now, &match_data);
69 }
70 
71 gboolean
72 pe_test_rule_full(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
73 {
74  xmlNode *expr = NULL;
75  gboolean test = TRUE;
76  gboolean empty = TRUE;
77  gboolean passed = TRUE;
78  gboolean do_and = TRUE;
79  const char *value = NULL;
80 
81  rule = expand_idref(rule, NULL);
83  if (safe_str_eq(value, "or")) {
84  do_and = FALSE;
85  passed = FALSE;
86  }
87 
88  crm_trace("Testing rule %s", ID(rule));
89  for (expr = __xml_first_child_element(rule); expr != NULL;
90  expr = __xml_next_element(expr)) {
91 
92  test = pe_test_expression_full(expr, node_hash, role, now, match_data);
93  empty = FALSE;
94 
95  if (test && do_and == FALSE) {
96  crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
97  return TRUE;
98 
99  } else if (test == FALSE && do_and) {
100  crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
101  return FALSE;
102  }
103  }
104 
105  if (empty) {
106  crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
107  }
108 
109  crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
110  return passed;
111 }
112 
113 gboolean
114 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
115 {
116  return pe_test_expression_full(expr, node_hash, role, now, NULL);
117 }
118 
119 gboolean
120 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)
121 {
122  pe_match_data_t match_data = {
123  .re = re_match_data,
124  .params = NULL,
125  .meta = NULL,
126  };
127  return pe_test_expression_full(expr, node_hash, role, now, &match_data);
128 }
129 
130 gboolean
131 pe_test_expression_full(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
132 {
133  gboolean accept = FALSE;
134  const char *uname = NULL;
135 
136  switch (find_expression_type(expr)) {
137  case nested_rule:
138  accept = pe_test_rule_full(expr, node_hash, role, now, match_data);
139  break;
140  case attr_expr:
141  case loc_expr:
142  /* these expressions can never succeed if there is
143  * no node to compare with
144  */
145  if (node_hash != NULL) {
146  accept = pe_test_attr_expression_full(expr, node_hash, now, match_data);
147  }
148  break;
149 
150  case time_expr:
151  accept = test_date_expression(expr, now);
152  break;
153 
154  case role_expr:
155  accept = test_role_expression(expr, role, now);
156  break;
157 
158 #ifdef ENABLE_VERSIONED_ATTRS
159  case version_expr:
160  if (node_hash && g_hash_table_lookup_extended(node_hash,
162  NULL, NULL)) {
163  accept = test_attr_expression(expr, node_hash, now);
164  } else {
165  // we are going to test it when we have ra-version
166  accept = TRUE;
167  }
168  break;
169 #endif
170 
171  default:
172  CRM_CHECK(FALSE /* bad type */ , return FALSE);
173  accept = FALSE;
174  }
175  if (node_hash) {
176  uname = g_hash_table_lookup(node_hash, CRM_ATTR_UNAME);
177  }
178 
179  crm_trace("Expression %s %s on %s",
180  ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
181  return accept;
182 }
183 
184 enum expression_type
185 find_expression_type(xmlNode * expr)
186 {
187  const char *tag = NULL;
188  const char *attr = NULL;
189 
191  tag = crm_element_name(expr);
192 
193  if (safe_str_eq(tag, "date_expression")) {
194  return time_expr;
195 
196  } else if (safe_str_eq(tag, XML_TAG_RULE)) {
197  return nested_rule;
198 
199  } else if (safe_str_neq(tag, "expression")) {
200  return not_expr;
201 
202  } else if (safe_str_eq(attr, CRM_ATTR_UNAME)
203  || safe_str_eq(attr, CRM_ATTR_KIND)
204  || safe_str_eq(attr, CRM_ATTR_ID)) {
205  return loc_expr;
206 
207  } else if (safe_str_eq(attr, CRM_ATTR_ROLE)) {
208  return role_expr;
209 
210 #ifdef ENABLE_VERSIONED_ATTRS
211  } else if (safe_str_eq(attr, CRM_ATTR_RA_VERSION)) {
212  return version_expr;
213 #endif
214  }
215 
216  return attr_expr;
217 }
218 
219 gboolean
220 test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now)
221 {
222  gboolean accept = FALSE;
223  const char *op = NULL;
224  const char *value = NULL;
225 
226  if (role == RSC_ROLE_UNKNOWN) {
227  return accept;
228  }
229 
230  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
232 
233  if (safe_str_eq(op, "defined")) {
234  if (role > RSC_ROLE_STARTED) {
235  accept = TRUE;
236  }
237 
238  } else if (safe_str_eq(op, "not_defined")) {
239  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
240  accept = TRUE;
241  }
242 
243  } else if (safe_str_eq(op, "eq")) {
244  if (text2role(value) == role) {
245  accept = TRUE;
246  }
247 
248  } else if (safe_str_eq(op, "ne")) {
249  /* we will only test "ne" wtih master/slave roles style */
250  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
251  accept = FALSE;
252 
253  } else if (text2role(value) != role) {
254  accept = TRUE;
255  }
256  }
257  return accept;
258 }
259 
260 gboolean
261 test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now)
262 {
263  return pe_test_attr_expression_full(expr, hash, now, NULL);
264 }
265 
266 gboolean
267 pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data)
268 {
269  gboolean accept = FALSE;
270  gboolean attr_allocated = FALSE;
271  int cmp = 0;
272  const char *h_val = NULL;
273  GHashTable *table = NULL;
274 
275  const char *op = NULL;
276  const char *type = NULL;
277  const char *attr = NULL;
278  const char *value = NULL;
279  const char *value_source = NULL;
280 
283  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
285  value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
286 
287  if (attr == NULL || op == NULL) {
288  pe_err("Invalid attribute or operation in expression"
289  " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
290  return FALSE;
291  }
292 
293  if (match_data) {
294  if (match_data->re) {
295  char *resolved_attr = pe_expand_re_matches(attr, match_data->re);
296 
297  if (resolved_attr) {
298  attr = (const char *) resolved_attr;
299  attr_allocated = TRUE;
300  }
301  }
302 
303  if (safe_str_eq(value_source, "param")) {
304  table = match_data->params;
305  } else if (safe_str_eq(value_source, "meta")) {
306  table = match_data->meta;
307  }
308  }
309 
310  if (table) {
311  const char *param_name = value;
312  const char *param_value = NULL;
313 
314  if (param_name && param_name[0]) {
315  if ((param_value = (const char *)g_hash_table_lookup(table, param_name))) {
316  value = param_value;
317  }
318  }
319  }
320 
321  if (hash != NULL) {
322  h_val = (const char *)g_hash_table_lookup(hash, attr);
323  }
324 
325  if (attr_allocated) {
326  free((char *)attr);
327  attr = NULL;
328  }
329 
330  if (value != NULL && h_val != NULL) {
331  if (type == NULL) {
332  if (safe_str_eq(op, "lt")
333  || safe_str_eq(op, "lte")
334  || safe_str_eq(op, "gt")
335  || safe_str_eq(op, "gte")) {
336  type = "number";
337 
338  } else {
339  type = "string";
340  }
341  crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
342  }
343 
344  if (safe_str_eq(type, "string")) {
345  cmp = strcasecmp(h_val, value);
346 
347  } else if (safe_str_eq(type, "number")) {
348  int h_val_f = crm_parse_int(h_val, NULL);
349  int value_f = crm_parse_int(value, NULL);
350 
351  if (h_val_f < value_f) {
352  cmp = -1;
353  } else if (h_val_f > value_f) {
354  cmp = 1;
355  } else {
356  cmp = 0;
357  }
358 
359  } else if (safe_str_eq(type, "version")) {
360  cmp = compare_version(h_val, value);
361 
362  }
363 
364  } else if (value == NULL && h_val == NULL) {
365  cmp = 0;
366  } else if (value == NULL) {
367  cmp = 1;
368  } else {
369  cmp = -1;
370  }
371 
372  if (safe_str_eq(op, "defined")) {
373  if (h_val != NULL) {
374  accept = TRUE;
375  }
376 
377  } else if (safe_str_eq(op, "not_defined")) {
378  if (h_val == NULL) {
379  accept = TRUE;
380  }
381 
382  } else if (safe_str_eq(op, "eq")) {
383  if ((h_val == value) || cmp == 0) {
384  accept = TRUE;
385  }
386 
387  } else if (safe_str_eq(op, "ne")) {
388  if ((h_val == NULL && value != NULL)
389  || (h_val != NULL && value == NULL)
390  || cmp != 0) {
391  accept = TRUE;
392  }
393 
394  } else if (value == NULL || h_val == NULL) {
395  // The comparison is meaningless from this point on
396  accept = FALSE;
397 
398  } else if (safe_str_eq(op, "lt")) {
399  if (cmp < 0) {
400  accept = TRUE;
401  }
402 
403  } else if (safe_str_eq(op, "lte")) {
404  if (cmp <= 0) {
405  accept = TRUE;
406  }
407 
408  } else if (safe_str_eq(op, "gt")) {
409  if (cmp > 0) {
410  accept = TRUE;
411  }
412 
413  } else if (safe_str_eq(op, "gte")) {
414  if (cmp >= 0) {
415  accept = TRUE;
416  }
417  }
418 
419  return accept;
420 }
421 
422 /* As per the nethack rules:
423  *
424  * moon period = 29.53058 days ~= 30, year = 365.2422 days
425  * days moon phase advances on first day of year compared to preceding year
426  * = 365.2422 - 12*29.53058 ~= 11
427  * years in Metonic cycle (time until same phases fall on the same days of
428  * the month) = 18.6 ~= 19
429  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
430  * (29 as initial condition)
431  * current phase in days = first day phase + days elapsed in year
432  * 6 moons ~= 177 days
433  * 177 ~= 8 reported phases * 22
434  * + 11/22 for rounding
435  *
436  * 0-7, with 0: new, 4: full
437  */
438 
439 static int
440 phase_of_the_moon(crm_time_t * now)
441 {
442  uint32_t epact, diy, goldn;
443  uint32_t y;
444 
445  crm_time_get_ordinal(now, &y, &diy);
446 
447  goldn = (y % 19) + 1;
448  epact = (11 * goldn + 18) % 30;
449  if ((epact == 25 && goldn > 11) || epact == 24)
450  epact++;
451 
452  return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
453 }
454 
455 static gboolean
456 decodeNVpair(const char *srcstring, char separator, char **name, char **value)
457 {
458  int lpc = 0;
459  int len = 0;
460  const char *temp = NULL;
461 
462  CRM_ASSERT(name != NULL && value != NULL);
463  *name = NULL;
464  *value = NULL;
465 
466  crm_trace("Attempting to decode: [%s]", srcstring);
467  if (srcstring != NULL) {
468  len = strlen(srcstring);
469  while (lpc <= len) {
470  if (srcstring[lpc] == separator) {
471  *name = calloc(1, lpc + 1);
472  if (*name == NULL) {
473  break; /* and return FALSE */
474  }
475  memcpy(*name, srcstring, lpc);
476  (*name)[lpc] = '\0';
477 
478 /* this sucks but as the strtok manpage says..
479  * it *is* a bug
480  */
481  len = len - lpc;
482  len--;
483  if (len <= 0) {
484  *value = NULL;
485  } else {
486 
487  *value = calloc(1, len + 1);
488  if (*value == NULL) {
489  break; /* and return FALSE */
490  }
491  temp = srcstring + lpc + 1;
492  memcpy(*value, temp, len);
493  (*value)[len] = '\0';
494  }
495  return TRUE;
496  }
497  lpc++;
498  }
499  }
500 
501  if (*name != NULL) {
502  free(*name);
503  *name = NULL;
504  }
505  *name = NULL;
506  *value = NULL;
507 
508  return FALSE;
509 }
510 
511 #define cron_check(xml_field, time_field) \
512  value = crm_element_value(cron_spec, xml_field); \
513  if(value != NULL) { \
514  gboolean pass = TRUE; \
515  decodeNVpair(value, '-', &value_low, &value_high); \
516  if(value_low == NULL) { \
517  value_low = strdup(value); \
518  } \
519  value_low_i = crm_parse_int(value_low, "0"); \
520  value_high_i = crm_parse_int(value_high, "-1"); \
521  if(value_high_i < 0) { \
522  if(value_low_i != time_field) { \
523  pass = FALSE; \
524  } \
525  } else if(value_low_i > time_field) { \
526  pass = FALSE; \
527  } else if(value_high_i < time_field) { \
528  pass = FALSE; \
529  } \
530  free(value_low); \
531  free(value_high); \
532  if(pass == FALSE) { \
533  crm_debug("Condition '%s' in %s: failed", value, xml_field); \
534  return pass; \
535  } \
536  crm_debug("Condition '%s' in %s: passed", value, xml_field); \
537  }
538 
539 gboolean
540 cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
541 {
542  const char *value = NULL;
543  char *value_low = NULL;
544  char *value_high = NULL;
545 
546  int value_low_i = 0;
547  int value_high_i = 0;
548 
549  uint32_t h, m, s, y, d, w;
550 
551  CRM_CHECK(now != NULL, return FALSE);
552 
553  crm_time_get_timeofday(now, &h, &m, &s);
554 
555  cron_check("seconds", s);
556  cron_check("minutes", m);
557  cron_check("hours", h);
558 
559  crm_time_get_gregorian(now, &y, &m, &d);
560 
561  cron_check("monthdays", d);
562  cron_check("months", m);
563  cron_check("years", y);
564 
565  crm_time_get_ordinal(now, &y, &d);
566 
567  cron_check("yeardays", d);
568 
569  crm_time_get_isoweek(now, &y, &w, &d);
570 
571  cron_check("weekyears", y);
572  cron_check("weeks", w);
573  cron_check("weekdays", d);
574 
575  cron_check("moon", phase_of_the_moon(now));
576 
577  return TRUE;
578 }
579 
580 #define update_field(xml_field, time_fn) \
581  value = crm_element_value(duration_spec, xml_field); \
582  if(value != NULL) { \
583  int value_i = crm_parse_int(value, "0"); \
584  time_fn(end, value_i); \
585  }
586 
587 crm_time_t *
588 parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
589 {
590  crm_time_t *end = NULL;
591  const char *value = NULL;
592 
593  end = crm_time_new(NULL);
594  crm_time_set(end, start);
595 
603 
604  return end;
605 }
606 
607 gboolean
609 {
610  crm_time_t *start = NULL;
611  crm_time_t *end = NULL;
612  const char *value = NULL;
613  const char *op = crm_element_value(time_expr, "operation");
614 
615  xmlNode *duration_spec = NULL;
616  xmlNode *date_spec = NULL;
617 
618  gboolean passed = FALSE;
619 
620  crm_trace("Testing expression: %s", ID(time_expr));
621 
622  duration_spec = first_named_child(time_expr, "duration");
623  date_spec = first_named_child(time_expr, "date_spec");
624 
625  value = crm_element_value(time_expr, "start");
626  if (value != NULL) {
627  start = crm_time_new(value);
628  }
629  value = crm_element_value(time_expr, "end");
630  if (value != NULL) {
631  end = crm_time_new(value);
632  }
633 
634  if (start != NULL && end == NULL && duration_spec != NULL) {
635  end = parse_xml_duration(start, duration_spec);
636  }
637  if (op == NULL) {
638  op = "in_range";
639  }
640 
641  if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) {
642  if (start != NULL && crm_time_compare(start, now) > 0) {
643  passed = FALSE;
644  } else if (end != NULL && crm_time_compare(end, now) < 0) {
645  passed = FALSE;
646  } else if (safe_str_eq(op, "in_range")) {
647  passed = TRUE;
648  } else {
649  passed = cron_range_satisfied(now, date_spec);
650  }
651 
652  } else if (safe_str_eq(op, "gt") && crm_time_compare(start, now) < 0) {
653  passed = TRUE;
654 
655  } else if (safe_str_eq(op, "lt") && crm_time_compare(end, now) > 0) {
656  passed = TRUE;
657 
658  } else if (safe_str_eq(op, "eq") && crm_time_compare(start, now) == 0) {
659  passed = TRUE;
660 
661  } else if (safe_str_eq(op, "neq") && crm_time_compare(start, now) != 0) {
662  passed = TRUE;
663  }
664 
665  crm_time_free(start);
666  crm_time_free(end);
667  return passed;
668 }
669 
670 typedef struct sorted_set_s {
671  int score;
672  const char *name;
673  const char *special_name;
674  xmlNode *attr_set;
675 } sorted_set_t;
676 
677 static gint
678 sort_pairs(gconstpointer a, gconstpointer b)
679 {
680  const sorted_set_t *pair_a = a;
681  const sorted_set_t *pair_b = b;
682 
683  if (a == NULL && b == NULL) {
684  return 0;
685  } else if (a == NULL) {
686  return 1;
687  } else if (b == NULL) {
688  return -1;
689  }
690 
691  if (safe_str_eq(pair_a->name, pair_a->special_name)) {
692  return -1;
693 
694  } else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
695  return 1;
696  }
697 
698  if (pair_a->score < pair_b->score) {
699  return 1;
700  } else if (pair_a->score > pair_b->score) {
701  return -1;
702  }
703  return 0;
704 }
705 
706 static void
707 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
708 {
709  const char *name = NULL;
710  const char *value = NULL;
711  const char *old_value = NULL;
712  xmlNode *list = nvpair_list;
713  xmlNode *an_attr = NULL;
714 
715  name = crm_element_name(list->children);
716  if (safe_str_eq(XML_TAG_ATTRS, name)) {
717  list = list->children;
718  }
719 
720  for (an_attr = __xml_first_child_element(list); an_attr != NULL;
721  an_attr = __xml_next_element(an_attr)) {
722 
723  if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
724  xmlNode *ref_nvpair = expand_idref(an_attr, top);
725 
726  name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
727  if (name == NULL) {
728  name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME);
729  }
730 
731  crm_trace("Setting attribute: %s", name);
732  value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
733  if (value == NULL) {
734  value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
735  }
736 
737  if (name == NULL || value == NULL) {
738  continue;
739 
740  }
741 
742  old_value = g_hash_table_lookup(hash, name);
743 
744  if (safe_str_eq(value, "#default")) {
745  if (old_value) {
746  crm_trace("Removing value for %s (%s)", name, value);
747  g_hash_table_remove(hash, name);
748  }
749  continue;
750 
751  } else if (old_value == NULL) {
752  g_hash_table_insert(hash, strdup(name), strdup(value));
753 
754  } else if (overwrite) {
755  crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
756  g_hash_table_replace(hash, strdup(name), strdup(value));
757  }
758  }
759  }
760 }
761 
762 #ifdef ENABLE_VERSIONED_ATTRS
763 static xmlNode*
764 get_versioned_rule(xmlNode * attr_set)
765 {
766  xmlNode * rule = NULL;
767  xmlNode * expr = NULL;
768 
769  for (rule = __xml_first_child_element(attr_set); rule != NULL;
770  rule = __xml_next_element(rule)) {
771 
772  if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
773  for (expr = __xml_first_child_element(rule); expr != NULL;
774  expr = __xml_next_element(expr)) {
775 
776  if (find_expression_type(expr) == version_expr) {
777  return rule;
778  }
779  }
780  }
781  }
782 
783  return NULL;
784 }
785 
786 static void
787 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
788 {
789  xmlNode *attr_set_copy = NULL;
790  xmlNode *rule = NULL;
791  xmlNode *expr = NULL;
792 
793  if (!attr_set || !versioned_attrs) {
794  return;
795  }
796 
797  attr_set_copy = copy_xml(attr_set);
798 
799  rule = get_versioned_rule(attr_set_copy);
800  if (!rule) {
801  free_xml(attr_set_copy);
802  return;
803  }
804 
805  expr = __xml_first_child_element(rule);
806  while (expr != NULL) {
807  if (find_expression_type(expr) != version_expr) {
808  xmlNode *node = expr;
809 
810  expr = __xml_next_element(expr);
811  free_xml(node);
812  } else {
813  expr = __xml_next_element(expr);
814  }
815  }
816 
817  add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
818 }
819 #endif
820 
821 typedef struct unpack_data_s {
822  gboolean overwrite;
823  GHashTable *node_hash;
824  void *hash;
825  crm_time_t *now;
826  xmlNode *top;
827 } unpack_data_t;
828 
829 static void
830 unpack_attr_set(gpointer data, gpointer user_data)
831 {
832  sorted_set_t *pair = data;
833  unpack_data_t *unpack_data = user_data;
834 
835  if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
836  return;
837  }
838 
839 #ifdef ENABLE_VERSIONED_ATTRS
840  if (get_versioned_rule(pair->attr_set) && !(unpack_data->node_hash &&
841  g_hash_table_lookup_extended(unpack_data->node_hash,
842  CRM_ATTR_RA_VERSION, NULL, NULL))) {
843  // we haven't actually tested versioned expressions yet
844  return;
845  }
846 #endif
847 
848  crm_trace("Adding attributes from %s", pair->name);
849  populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
850 }
851 
852 #ifdef ENABLE_VERSIONED_ATTRS
853 static void
854 unpack_versioned_attr_set(gpointer data, gpointer user_data)
855 {
856  sorted_set_t *pair = data;
857  unpack_data_t *unpack_data = user_data;
858 
859  if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
860  return;
861  }
862 
863  add_versioned_attributes(pair->attr_set, unpack_data->hash);
864 }
865 #endif
866 
867 static GListPtr
868 make_pairs_and_populate_data(xmlNode * top, xmlNode * xml_obj, const char *set_name,
869  GHashTable * node_hash, void * hash, const char *always_first,
870  gboolean overwrite, crm_time_t * now, unpack_data_t * data)
871 {
872  GListPtr unsorted = NULL;
873  const char *score = NULL;
874  sorted_set_t *pair = NULL;
875  xmlNode *attr_set = NULL;
876 
877  if (xml_obj == NULL) {
878  crm_trace("No instance attributes");
879  return NULL;
880  }
881 
882  crm_trace("Checking for attributes");
883  for (attr_set = __xml_first_child_element(xml_obj); attr_set != NULL;
884  attr_set = __xml_next_element(attr_set)) {
885 
886  /* Uncertain if set_name == NULL check is strictly necessary here */
887  if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
888  pair = NULL;
889  attr_set = expand_idref(attr_set, top);
890  if (attr_set == NULL) {
891  continue;
892  }
893 
894  pair = calloc(1, sizeof(sorted_set_t));
895  pair->name = ID(attr_set);
896  pair->special_name = always_first;
897  pair->attr_set = attr_set;
898 
899  score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
900  pair->score = char2score(score);
901 
902  unsorted = g_list_prepend(unsorted, pair);
903  }
904  }
905 
906  if (pair != NULL) {
907  data->hash = hash;
908  data->node_hash = node_hash;
909  data->now = now;
910  data->overwrite = overwrite;
911  data->top = top;
912  }
913 
914  if (unsorted) {
915  return g_list_sort(unsorted, sort_pairs);
916  }
917 
918  return NULL;
919 }
920 
921 void
922 unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
923  GHashTable * node_hash, GHashTable * hash, const char *always_first,
924  gboolean overwrite, crm_time_t * now)
925 {
927  GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
928  always_first, overwrite, now, &data);
929 
930  if (pairs) {
931  g_list_foreach(pairs, unpack_attr_set, &data);
932  g_list_free_full(pairs, free);
933  }
934 }
935 
936 #ifdef ENABLE_VERSIONED_ATTRS
937 void
938 pe_unpack_versioned_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
939  GHashTable * node_hash, xmlNode * hash, crm_time_t * now)
940 {
942  GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
943  NULL, FALSE, now, &data);
944 
945  if (pairs) {
946  g_list_foreach(pairs, unpack_versioned_attr_set, &data);
947  g_list_free_full(pairs, free);
948  }
949 }
950 #endif
951 
952 char *
953 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
954 {
955  size_t len = 0;
956  int i;
957  const char *p, *last_match_index;
958  char *p_dst, *result = NULL;
959 
960  if (!string || string[0] == '\0' || !match_data) {
961  return NULL;
962  }
963 
964  p = last_match_index = string;
965 
966  while (*p) {
967  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
968  i = *(p + 1) - '0';
969  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
970  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
971  len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
972  last_match_index = p + 2;
973  }
974  p++;
975  }
976  p++;
977  }
978  len += p - last_match_index + 1;
979 
980  /* FIXME: Excessive? */
981  if (len - 1 <= 0) {
982  return NULL;
983  }
984 
985  p_dst = result = calloc(1, len);
986  p = string;
987 
988  while (*p) {
989  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
990  i = *(p + 1) - '0';
991  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
992  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
993  /* rm_eo can be equal to rm_so, but then there is nothing to do */
994  int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
995  memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
996  p_dst += match_len;
997  }
998  p++;
999  } else {
1000  *(p_dst) = *(p);
1001  p_dst++;
1002  }
1003  p++;
1004  }
1005 
1006  return result;
1007 }
1008 
1009 #ifdef ENABLE_VERSIONED_ATTRS
1010 GHashTable*
1011 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
1012 {
1013  GHashTable *hash = crm_str_table_new();
1014 
1015  if (versioned_params && ra_version) {
1016  GHashTable *node_hash = crm_str_table_new();
1017  xmlNode *attr_set = __xml_first_child_element(versioned_params);
1018 
1019  if (attr_set) {
1020  g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
1021  strdup(ra_version));
1022  unpack_instance_attributes(NULL, versioned_params, crm_element_name(attr_set),
1023  node_hash, hash, NULL, FALSE, NULL);
1024  }
1025 
1026  g_hash_table_destroy(node_hash);
1027  }
1028 
1029  return hash;
1030 }
1031 #endif
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:190
A dumping ground.
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1285
#define cron_check(xml_field, time_field)
Definition: rules.c:511
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:61
void crm_time_add_seconds(crm_time_t *dt, int value)
Definition: iso8601.c:1179
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:182
#define CRM_ATTR_KIND
Definition: crm.h:90
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:336
struct crm_time_s crm_time_t
Definition: iso8601.h:37
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:114
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:324
int char2score(const char *score)
Definition: utils.c:221
pe_re_match_data_t * re
Definition: rules.h:45
#define XML_TAG_ATTRS
Definition: msg_xml.h:187
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:120
#define CRM_ATTR_ROLE
Definition: crm.h:91
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:337
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:157
int crm_time_get_ordinal(crm_time_t *dt, uint32_t *y, uint32_t *d)
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:370
gboolean test_attr_expression(xmlNode *expr, GHashTable *hash, crm_time_t *now)
Definition: rules.c:261
#define CRM_ATTR_RA_VERSION
Definition: crm.h:98
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:182
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1273
gboolean cron_range_satisfied(crm_time_t *now, xmlNode *cron_spec)
Definition: rules.c:540
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:131
gboolean pe_test_attr_expression_full(xmlNode *expr, GHashTable *hash, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:267
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2114
char uname[MAX_NAME]
Definition: internal.h:81
Definition: rules.h:29
#define crm_debug(fmt, args...)
Definition: logging.h:279
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition: iso8601.c:1279
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:393
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:335
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1225
#define CRM_ATTR_UNAME
Definition: crm.h:88
#define crm_trace(fmt, args...)
Definition: logging.h:280
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:4314
Wrappers for and extensions to libxml2.
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:953
gboolean test_date_expression(xmlNode *time_expr, crm_time_t *now)
Definition: rules.c:608
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1267
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:334
crm_time_t * parse_xml_duration(crm_time_t *start, xmlNode *duration_spec)
Definition: rules.c:588
GHashTable * meta
Definition: rules.h:47
void free_xml(xmlNode *child)
Definition: xml.c:2108
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:72
enum rsc_role_e text2role(const char *role)
Definition: common.c:386
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:245
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:330
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:55
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
struct unpack_data_s unpack_data_t
CRM_TRACE_INIT_DATA(pe_rules)
GHashTable * params
Definition: rules.h:46
struct sorted_set_s sorted_set_t
int crm_time_get_gregorian(crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
#define crm_err(fmt, args...)
Definition: logging.h:274
int crm_time_get_timeofday(crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:35
void crm_time_set(crm_time_t *target, crm_time_t *source)
Definition: iso8601.c:985
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:99
regmatch_t * pmatch
Definition: rules.h:41
int compare_version(const char *version1, const char *version2)
Definition: utils.c:477
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:371
#define uint32_t
Definition: stdint.in.h:158
#define CRM_ASSERT(expr)
Definition: error.h:20
char data[0]
Definition: internal.h:86
#define crm_str(x)
Definition: logging.h:300
int crm_time_compare(crm_time_t *dt, crm_time_t *rhs)
Definition: iso8601.c:1152
rsc_role_e
Definition: common.h:81
gboolean test_role_expression(xmlNode *expr, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:220
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:1969
Definition: rules.h:32
xmlNode * first_named_child(xmlNode *parent, const char *name)
Definition: xml.c:4241
expression_type
Definition: rules.h:28
#define ID(x)
Definition: msg_xml.h:452
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:922
#define pe_err(fmt...)
Definition: internal.h:18
#define safe_str_eq(a, b)
Definition: util.h:74
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:185
#define XML_TAG_RULE
Definition: msg_xml.h:323
#define CRM_ATTR_ID
Definition: crm.h:89
GList * GListPtr
Definition: crm.h:210
int crm_time_get_isoweek(crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
#define XML_EXPR_ATTR_ATTRIBUTE
Definition: msg_xml.h:333
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1200
char * string
Definition: rules.h:39
#define update_field(xml_field, time_fn)
Definition: rules.c:580
enum crm_ais_msg_types type
Definition: internal.h:79
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:116