root/lib/pengine/rules.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. test_ruleset
  2. test_rule
  3. pe_test_rule_re
  4. pe_test_rule_full
  5. test_expression
  6. pe_test_expression_re
  7. pe_test_expression_full
  8. find_expression_type
  9. test_role_expression
  10. test_attr_expression
  11. pe_test_attr_expression_full
  12. phase_of_the_moon
  13. decodeNVpair
  14. cron_range_satisfied
  15. parse_xml_duration
  16. test_date_expression
  17. sort_pairs
  18. populate_hash
  19. get_versioned_rule
  20. add_versioned_attributes
  21. unpack_attr_set
  22. unpack_versioned_attr_set
  23. make_pairs_and_populate_data
  24. unpack_instance_attributes
  25. pe_unpack_versioned_attributes
  26. pe_expand_re_matches
  27. pe_unpack_versioned_parameters

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

/* [previous][next][first][last][top][bottom][index][help] */