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