root/lib/cib/cib_utils.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_compare_generation
  2. get_cib_copy
  3. cib_get_generation
  4. cib_version_details
  5. cib_diff_version_details
  6. get_object_path
  7. get_object_parent
  8. get_object_root
  9. createEmptyCib
  10. cib_acl_enabled
  11. cib_perform_op
  12. cib_create_op
  13. cib_native_callback
  14. cib_native_notify
  15. cib_metadata
  16. verify_cib_options
  17. cib_pref
  18. cib_read_config
  19. cib_apply_patch_event
  20. cib_internal_config_changed
  21. cib_internal_op

   1 /*
   2  * Copyright (c) 2004 International Business Machines
   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 <unistd.h>
  21 #include <stdlib.h>
  22 #include <stdio.h>
  23 #include <stdarg.h>
  24 #include <string.h>
  25 #include <sys/utsname.h>
  26 
  27 #include <glib.h>
  28 
  29 #include <crm/crm.h>
  30 #include <crm/cib/internal.h>
  31 #include <crm/msg_xml.h>
  32 #include <crm/common/xml.h>
  33 #include <crm/pengine/rules.h>
  34 
  35 struct config_root_s {
  36     const char *name;
  37     const char *parent;
  38     const char *path;
  39 };
  40 
  41  /*
  42   * "//crm_config" will also work in place of "/cib/configuration/crm_config"
  43   * The / prefix means find starting from the root, whereas the // prefix means
  44   * find anywhere and risks multiple matches
  45   */
  46 /* *INDENT-OFF* */
  47 struct config_root_s known_paths[] = {
  48     { NULL,                     NULL,                 "//cib" },
  49     { XML_TAG_CIB,              NULL,                 "//cib" },
  50     { XML_CIB_TAG_STATUS,       "/cib",               "//cib/status" },
  51     { XML_CIB_TAG_CONFIGURATION,"/cib",               "//cib/configuration" },
  52     { XML_CIB_TAG_CRMCONFIG,    "/cib/configuration", "//cib/configuration/crm_config" },
  53     { XML_CIB_TAG_NODES,        "/cib/configuration", "//cib/configuration/nodes" },
  54     { XML_CIB_TAG_DOMAINS,      "/cib/configuration", "//cib/configuration/domains" },
  55     { XML_CIB_TAG_RESOURCES,    "/cib/configuration", "//cib/configuration/resources" },
  56     { XML_CIB_TAG_CONSTRAINTS,  "/cib/configuration", "//cib/configuration/constraints" },
  57     { XML_CIB_TAG_OPCONFIG,     "/cib/configuration", "//cib/configuration/op_defaults" },
  58     { XML_CIB_TAG_RSCCONFIG,    "/cib/configuration", "//cib/configuration/rsc_defaults" },
  59     { XML_CIB_TAG_ACLS,         "/cib/configuration", "//cib/configuration/acls" },
  60     { XML_TAG_FENCING_TOPOLOGY, "/cib/configuration", "//cib/configuration/fencing-topology" },
  61     { XML_CIB_TAG_SECTION_ALL,  NULL,                 "//cib" },
  62 };
  63 /* *INDENT-ON* */
  64 
  65 int
  66 cib_compare_generation(xmlNode * left, xmlNode * right)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68     int lpc = 0;
  69 
  70     const char *attributes[] = {
  71         XML_ATTR_GENERATION_ADMIN,
  72         XML_ATTR_GENERATION,
  73         XML_ATTR_NUMUPDATES,
  74     };
  75 
  76     crm_log_xml_trace(left, "left");
  77     crm_log_xml_trace(right, "right");
  78 
  79     for (lpc = 0; lpc < DIMOF(attributes); lpc++) {
  80         int int_elem_l = -1;
  81         int int_elem_r = -1;
  82         const char *elem_r = NULL;
  83         const char *elem_l = crm_element_value(left, attributes[lpc]);
  84 
  85         if (right != NULL) {
  86             elem_r = crm_element_value(right, attributes[lpc]);
  87         }
  88 
  89         if (elem_l != NULL) {
  90             int_elem_l = crm_parse_int(elem_l, NULL);
  91         }
  92         if (elem_r != NULL) {
  93             int_elem_r = crm_parse_int(elem_r, NULL);
  94         }
  95 
  96         if (int_elem_l < int_elem_r) {
  97             crm_trace("%s (%s < %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
  98             return -1;
  99 
 100         } else if (int_elem_l > int_elem_r) {
 101             crm_trace("%s (%s > %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
 102             return 1;
 103         }
 104     }
 105 
 106     return 0;
 107 }
 108 
 109 /* Deprecated - doesn't expose -EACCES */
 110 xmlNode *
 111 get_cib_copy(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113     xmlNode *xml_cib;
 114     int options = cib_scope_local | cib_sync_call;
 115     int rc = pcmk_ok;
 116 
 117     if (cib->state == cib_disconnected) {
 118         return NULL;
 119     }
 120 
 121     rc = cib->cmds->query(cib, NULL, &xml_cib, options);
 122     if (rc == -EACCES) {
 123         return NULL;
 124 
 125     } else if (rc != pcmk_ok) {
 126         crm_err("Couldn't retrieve the CIB");
 127         free_xml(xml_cib);
 128         return NULL;
 129 
 130     } else if (xml_cib == NULL) {
 131         crm_err("The CIB result was empty");
 132         free_xml(xml_cib);
 133         return NULL;
 134     }
 135 
 136     if (safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) {
 137         return xml_cib;
 138     }
 139     free_xml(xml_cib);
 140     return NULL;
 141 }
 142 
 143 xmlNode *
 144 cib_get_generation(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146     xmlNode *the_cib = NULL;
 147     xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
 148 
 149     cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
 150     if (the_cib != NULL) {
 151         copy_in_properties(generation, the_cib);
 152         free_xml(the_cib);
 153     }
 154 
 155     return generation;
 156 }
 157 
 158 gboolean
 159 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161     *epoch = -1;
 162     *updates = -1;
 163     *admin_epoch = -1;
 164 
 165     if (cib == NULL) {
 166         return FALSE;
 167 
 168     } else {
 169         crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
 170         crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
 171         crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
 172     }
 173     return TRUE;
 174 }
 175 
 176 gboolean
 177 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
     /* [previous][next][first][last][top][bottom][index][help] */
 178                          int *_admin_epoch, int *_epoch, int *_updates)
 179 {
 180     int add[] = { 0, 0, 0 };
 181     int del[] = { 0, 0, 0 };
 182 
 183     xml_patch_versions(diff, add, del);
 184 
 185     *admin_epoch = add[0];
 186     *epoch = add[1];
 187     *updates = add[2];
 188 
 189     *_admin_epoch = del[0];
 190     *_epoch = del[1];
 191     *_updates = del[2];
 192 
 193     return TRUE;
 194 }
 195 
 196 /*
 197  * The caller should never free the return value
 198  */
 199 
 200 const char *
 201 get_object_path(const char *object_type)
     /* [previous][next][first][last][top][bottom][index][help] */
 202 {
 203     int lpc = 0;
 204     int max = DIMOF(known_paths);
 205 
 206     for (; lpc < max; lpc++) {
 207         if ((object_type == NULL && known_paths[lpc].name == NULL)
 208             || safe_str_eq(object_type, known_paths[lpc].name)) {
 209             return known_paths[lpc].path;
 210         }
 211     }
 212     return NULL;
 213 }
 214 
 215 const char *
 216 get_object_parent(const char *object_type)
     /* [previous][next][first][last][top][bottom][index][help] */
 217 {
 218     int lpc = 0;
 219     int max = DIMOF(known_paths);
 220 
 221     for (; lpc < max; lpc++) {
 222         if (safe_str_eq(object_type, known_paths[lpc].name)) {
 223             return known_paths[lpc].parent;
 224         }
 225     }
 226     return NULL;
 227 }
 228 
 229 xmlNode *
 230 get_object_root(const char *object_type, xmlNode * the_root)
     /* [previous][next][first][last][top][bottom][index][help] */
 231 {
 232     const char *xpath = get_object_path(object_type);
 233 
 234     if (xpath == NULL) {
 235         return the_root;        /* or return NULL? */
 236     }
 237 
 238     return get_xpath_object(xpath, the_root, LOG_DEBUG_4);
 239 }
 240 
 241 /*
 242  * It is the callers responsibility to free both the new CIB (output)
 243  *     and the new CIB (input)
 244  */
 245 xmlNode *
 246 createEmptyCib(int admin_epoch)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     xmlNode *cib_root = NULL, *config = NULL;
 249 
 250     cib_root = create_xml_node(NULL, XML_TAG_CIB);
 251     crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 252     crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema());
 253 
 254     crm_xml_add_int(cib_root, XML_ATTR_GENERATION, admin_epoch);
 255     crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0);
 256     crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0);
 257 
 258     config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
 259     create_xml_node(cib_root, XML_CIB_TAG_STATUS);
 260 
 261     create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
 262     create_xml_node(config, XML_CIB_TAG_NODES);
 263     create_xml_node(config, XML_CIB_TAG_RESOURCES);
 264     create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
 265 
 266     return cib_root;
 267 }
 268 
 269 static bool
 270 cib_acl_enabled(xmlNode *xml, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272     bool rc = FALSE;
 273 
 274 #if ENABLE_ACL
 275     if(pcmk_acl_required(user)) {
 276         const char *value = NULL;
 277         GHashTable *options = crm_str_table_new();
 278 
 279         cib_read_config(options, xml);
 280         value = cib_pref(options, "enable-acl");
 281         rc = crm_is_true(value);
 282         g_hash_table_destroy(options);
 283     }
 284 
 285     crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
 286 #endif
 287     return rc;
 288 }
 289 
 290 int
 291 cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
     /* [previous][next][first][last][top][bottom][index][help] */
 292                const char *section, xmlNode * req, xmlNode * input,
 293                gboolean manage_counters, gboolean * config_changed,
 294                xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
 295 {
 296     int rc = pcmk_ok;
 297     gboolean check_dtd = TRUE;
 298     xmlNode *top = NULL;
 299     xmlNode *scratch = NULL;
 300     xmlNode *local_diff = NULL;
 301 
 302     const char *new_version = NULL;
 303     static struct qb_log_callsite *diff_cs = NULL;
 304     const char *user = crm_element_value(req, F_CIB_USER);
 305     bool with_digest = FALSE;
 306 
 307     crm_trace("Begin %s%s%s op", is_set(call_options, cib_dryrun)?"dry-run of ":"", is_query ? "read-only " : "", op);
 308 
 309     CRM_CHECK(output != NULL, return -ENOMSG);
 310     CRM_CHECK(result_cib != NULL, return -ENOMSG);
 311     CRM_CHECK(config_changed != NULL, return -ENOMSG);
 312 
 313     if(output) {
 314         *output = NULL;
 315     }
 316 
 317     *result_cib = NULL;
 318     *config_changed = FALSE;
 319 
 320     if (fn == NULL) {
 321         return -EINVAL;
 322     }
 323 
 324     if (is_query) {
 325         xmlNode *cib_ro = current_cib;
 326         xmlNode *cib_filtered = NULL;
 327 
 328         if(cib_acl_enabled(cib_ro, user)) {
 329             if(xml_acl_filtered_copy(user, current_cib, current_cib, &cib_filtered)) {
 330                 if (cib_filtered == NULL) {
 331                     crm_debug("Pre-filtered the entire cib");
 332                     return -EACCES;
 333                 }
 334                 cib_ro = cib_filtered;
 335                 crm_log_xml_trace(cib_ro, "filtered");
 336             }
 337         }
 338 
 339         rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
 340 
 341         if(output == NULL || *output == NULL) {
 342             /* nothing */
 343 
 344         } else if(cib_filtered == *output) {
 345             cib_filtered = NULL; /* Let them have this copy */
 346 
 347         } else if(*output == current_cib) {
 348             /* They already know not to free it */
 349 
 350         } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
 351             /* We're about to free the document of which *output is a part */
 352             *output = copy_xml(*output);
 353 
 354         } else if((*output)->doc == current_cib->doc) {
 355             /* Give them a copy they can free */
 356             *output = copy_xml(*output);
 357         }
 358 
 359         free_xml(cib_filtered);
 360         return rc;
 361     }
 362 
 363 
 364     if (is_set(call_options, cib_zero_copy)) {
 365         /* Conditional on v2 patch style */
 366 
 367         scratch = current_cib;
 368 
 369         /* Create a shallow copy of current_cib for the version details */
 370         current_cib = create_xml_node(NULL, (const char *)scratch->name);
 371         copy_in_properties(current_cib, scratch);
 372         top = current_cib;
 373 
 374         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
 375         rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
 376 
 377     } else {
 378         scratch = copy_xml(current_cib);
 379         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
 380         rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
 381 
 382         if(scratch && xml_tracking_changes(scratch) == FALSE) {
 383             crm_trace("Inferring changes after %s op", op);
 384             xml_track_changes(scratch, user, current_cib, cib_acl_enabled(current_cib, user));
 385             xml_calculate_changes(current_cib, scratch);
 386         }
 387         CRM_CHECK(current_cib != scratch, return -EINVAL);
 388     }
 389 
 390     xml_acl_disable(scratch); /* Allow the system to make any additional changes */
 391 
 392     if (rc == pcmk_ok && scratch == NULL) {
 393         rc = -EINVAL;
 394         goto done;
 395 
 396     } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
 397         crm_trace("ACL rejected part or all of the proposed changes");
 398         rc = -EACCES;
 399         goto done;
 400 
 401     } else if (rc != pcmk_ok) {
 402         goto done;
 403     }
 404 
 405     if (scratch) {
 406         new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
 407 
 408         if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
 409             crm_err("Discarding update with feature set '%s' greater than our own '%s'",
 410                     new_version, CRM_FEATURE_SET);
 411             rc = -EPROTONOSUPPORT;
 412             goto done;
 413         }
 414     }
 415 
 416     if (current_cib) {
 417         int old = 0;
 418         int new = 0;
 419 
 420         crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
 421         crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
 422 
 423         if (old > new) {
 424             crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
 425                     XML_ATTR_GENERATION_ADMIN, old, new, call_options);
 426             crm_log_xml_warn(req, "Bad Op");
 427             crm_log_xml_warn(input, "Bad Data");
 428             rc = -pcmk_err_old_data;
 429 
 430         } else if (old == new) {
 431             crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
 432             crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
 433             if (old > new) {
 434                 crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
 435                         XML_ATTR_GENERATION, old, new, call_options);
 436                 crm_log_xml_warn(req, "Bad Op");
 437                 crm_log_xml_warn(input, "Bad Data");
 438                 rc = -pcmk_err_old_data;
 439             }
 440         }
 441     }
 442 
 443     crm_trace("Massaging CIB contents");
 444     strip_text_nodes(scratch);
 445     fix_plus_plus_recursive(scratch);
 446 
 447     if (is_set(call_options, cib_zero_copy)) {
 448         /* At this point, current_cib is just the 'cib' tag and its properties,
 449          *
 450          * The v1 format would barf on this, but we know the v2 patch
 451          * format only needs it for the top-level version fields
 452          */
 453         local_diff = xml_create_patchset(2, current_cib, scratch, (bool*)config_changed, manage_counters);
 454 
 455     } else {
 456         static time_t expires = 0;
 457         time_t tm_now = time(NULL);
 458 
 459         if (expires < tm_now) {
 460             expires = tm_now + 60;  /* Validate clients are correctly applying v2-style diffs at most once a minute */
 461             with_digest = TRUE;
 462         }
 463 
 464         local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)config_changed, manage_counters);
 465     }
 466 
 467     xml_log_changes(LOG_TRACE, __FUNCTION__, scratch);
 468     xml_accept_changes(scratch);
 469 
 470     if (diff_cs == NULL) {
 471         diff_cs = qb_log_callsite_get(__PRETTY_FUNCTION__, __FILE__, "diff-validation", LOG_DEBUG, __LINE__, crm_trace_nonlog);
 472     }
 473 
 474     if(local_diff) {
 475         patchset_process_digest(local_diff, current_cib, scratch, with_digest);
 476 
 477         xml_log_patchset(LOG_INFO, __FUNCTION__, local_diff);
 478         crm_log_xml_trace(local_diff, "raw patch");
 479     }
 480 
 481     if (is_not_set(call_options, cib_zero_copy) /* The original to compare against doesn't exist */
 482         && local_diff
 483         && crm_is_callsite_active(diff_cs, LOG_TRACE, 0)) {
 484 
 485         /* Validate the calculated patch set */
 486         int test_rc, format = 1;
 487         xmlNode * c = copy_xml(current_cib);
 488 
 489         crm_element_value_int(local_diff, "format", &format);
 490         test_rc = xml_apply_patchset(c, local_diff, manage_counters);
 491 
 492         if(test_rc != pcmk_ok) {
 493             save_xml_to_file(c,           "PatchApply:calculated", NULL);
 494             save_xml_to_file(current_cib, "PatchApply:input", NULL);
 495             save_xml_to_file(scratch,     "PatchApply:actual", NULL);
 496             save_xml_to_file(local_diff,  "PatchApply:diff", NULL);
 497             crm_err("v%d patchset error, patch failed to apply: %s (%d)", format, pcmk_strerror(test_rc), test_rc);
 498         }
 499         free_xml(c);
 500     }
 501 
 502     if (safe_str_eq(section, XML_CIB_TAG_STATUS)) {
 503         /* Throttle the amount of costly validation we perform due to status updates
 504          * a) we don't really care whats in the status section
 505          * b) we don't validate any of its contents at the moment anyway
 506          */
 507         check_dtd = FALSE;
 508     }
 509 
 510     /* === scratch must not be modified after this point ===
 511      * Exceptions, anything in:
 512 
 513      static filter_t filter[] = {
 514      { 0, XML_ATTR_ORIGIN },
 515      { 0, XML_CIB_ATTR_WRITTEN },
 516      { 0, XML_ATTR_UPDATE_ORIG },
 517      { 0, XML_ATTR_UPDATE_CLIENT },
 518      { 0, XML_ATTR_UPDATE_USER },
 519      };
 520      */
 521 
 522     if (*config_changed && is_not_set(call_options, cib_no_mtime)) {
 523         char *now_str = NULL;
 524         time_t now = time(NULL);
 525         const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
 526 
 527         now_str = ctime(&now);
 528         now_str[24] = EOS;      /* replace the newline */
 529         crm_xml_replace(scratch, XML_CIB_ATTR_WRITTEN, now_str);
 530 
 531         if (schema) {
 532             static int minimum_schema = 0;
 533             int current_schema = get_schema_version(schema);
 534 
 535             if (minimum_schema == 0) {
 536                 minimum_schema = get_schema_version("pacemaker-1.2");
 537             }
 538 
 539             /* Does the CIB support the "update-*" attributes... */
 540             if (current_schema >= minimum_schema) {
 541                 const char *origin = crm_element_value(req, F_ORIG);
 542 
 543                 CRM_LOG_ASSERT(origin != NULL);
 544                 crm_xml_replace(scratch, XML_ATTR_UPDATE_ORIG, origin);
 545                 crm_xml_replace(scratch, XML_ATTR_UPDATE_CLIENT,
 546                                 crm_element_value(req, F_CIB_CLIENTNAME));
 547 #if ENABLE_ACL
 548                 crm_xml_replace(scratch, XML_ATTR_UPDATE_USER, crm_element_value(req, F_CIB_USER));
 549 #endif
 550             }
 551         }
 552     }
 553 
 554     crm_trace("Perform validation: %s", check_dtd ? "true" : "false");
 555     if (rc == pcmk_ok && check_dtd && validate_xml(scratch, NULL, TRUE) == FALSE) {
 556         const char *current_dtd = crm_element_value(scratch, XML_ATTR_VALIDATION);
 557 
 558         crm_warn("Updated CIB does not validate against %s schema/dtd", crm_str(current_dtd));
 559         rc = -pcmk_err_schema_validation;
 560     }
 561 
 562   done:
 563 
 564     *result_cib = scratch;
 565 #if ENABLE_ACL
 566     if(rc != pcmk_ok && cib_acl_enabled(current_cib, user)) {
 567         if(xml_acl_filtered_copy(user, current_cib, scratch, result_cib)) {
 568             if (*result_cib == NULL) {
 569                 crm_debug("Pre-filtered the entire cib result");
 570             }
 571             free_xml(scratch);
 572         }
 573     }
 574 #endif
 575 
 576     if(diff) {
 577         *diff = local_diff;
 578     } else {
 579         free_xml(local_diff);
 580     }
 581 
 582     free_xml(top);
 583     crm_trace("Done");
 584     return rc;
 585 }
 586 
 587 xmlNode *
 588 cib_create_op(int call_id, const char *token, const char *op, const char *host, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 589               xmlNode * data, int call_options, const char *user_name)
 590 {
 591     xmlNode *op_msg = create_xml_node(NULL, "cib_command");
 592 
 593     CRM_CHECK(op_msg != NULL, return NULL);
 594     CRM_CHECK(token != NULL, return NULL);
 595 
 596     crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
 597 
 598     crm_xml_add(op_msg, F_TYPE, T_CIB);
 599     crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
 600     crm_xml_add(op_msg, F_CIB_OPERATION, op);
 601     crm_xml_add(op_msg, F_CIB_HOST, host);
 602     crm_xml_add(op_msg, F_CIB_SECTION, section);
 603     crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
 604 #if ENABLE_ACL
 605     if (user_name) {
 606         crm_xml_add(op_msg, F_CIB_USER, user_name);
 607     }
 608 #endif
 609     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
 610     crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
 611 
 612     if (data != NULL) {
 613         add_message_xml(op_msg, F_CIB_CALLDATA, data);
 614     }
 615 
 616     if (call_options & cib_inhibit_bcast) {
 617         CRM_CHECK((call_options & cib_scope_local), return NULL);
 618     }
 619     return op_msg;
 620 }
 621 
 622 void
 623 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 624 {
 625     xmlNode *output = NULL;
 626     cib_callback_client_t *blob = NULL;
 627 
 628     if (msg != NULL) {
 629         crm_element_value_int(msg, F_CIB_RC, &rc);
 630         crm_element_value_int(msg, F_CIB_CALLID, &call_id);
 631         output = get_message_xml(msg, F_CIB_CALLDATA);
 632     }
 633 
 634     blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id));
 635     if (blob == NULL) {
 636         crm_trace("No callback found for call %d", call_id);
 637     }
 638 
 639     if (cib == NULL) {
 640         crm_debug("No cib object supplied");
 641     }
 642 
 643     if (rc == -pcmk_err_diff_resync) {
 644         /* This is an internal value that clients do not and should not care about */
 645         rc = pcmk_ok;
 646     }
 647 
 648     if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
 649         crm_trace("Invoking callback %s for call %d", crm_str(blob->id), call_id);
 650         blob->callback(msg, call_id, rc, output, blob->user_data);
 651 
 652     } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
 653         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
 654         crm_log_xml_debug(msg, "Failed CIB Update");
 655     }
 656 
 657     /* This may free user_data, so do it after the callback */
 658     if (blob) {
 659         remove_cib_op_callback(call_id, FALSE);
 660     }
 661 
 662     if (cib && cib->op_callback != NULL) {
 663         crm_trace("Invoking global callback for call %d", call_id);
 664         cib->op_callback(msg, call_id, rc, output);
 665     }
 666     crm_trace("OP callback activated for %d", call_id);
 667 }
 668 
 669 void
 670 cib_native_notify(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 671 {
 672     xmlNode *msg = user_data;
 673     cib_notify_client_t *entry = data;
 674     const char *event = NULL;
 675 
 676     if (msg == NULL) {
 677         crm_warn("Skipping callback - NULL message");
 678         return;
 679     }
 680 
 681     event = crm_element_value(msg, F_SUBTYPE);
 682 
 683     if (entry == NULL) {
 684         crm_warn("Skipping callback - NULL callback client");
 685         return;
 686 
 687     } else if (entry->callback == NULL) {
 688         crm_warn("Skipping callback - NULL callback");
 689         return;
 690 
 691     } else if (safe_str_neq(entry->event, event)) {
 692         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
 693         return;
 694     }
 695 
 696     crm_trace("Invoking callback for %p/%s event...", entry, event);
 697     entry->callback(event, msg);
 698     crm_trace("Callback invoked...");
 699 }
 700 
 701 pe_cluster_option cib_opts[] = {
 702     /*
 703      * name, legacy name,
 704      * type, allowed values, default, validator,
 705      * short description,
 706      * long description
 707      */
 708     {
 709         "enable-acl", NULL,
 710         "boolean", NULL, "false", &check_boolean,
 711         "Enable CIB ACL",
 712         NULL
 713     },
 714     {
 715         "cluster-ipc-limit", NULL,
 716         "integer", NULL, "500", &check_positive_number,
 717         "Maximum IPC message backlog before disconnecting a cluster daemon",
 718         "Raise this if log has \"Evicting client\" messages for cluster daemon"
 719             " PIDs (a good value is the number of resources in the cluster"
 720             " multiplied by the number of nodes)"
 721     },
 722 };
 723 
 724 void
 725 cib_metadata(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 726 {
 727     config_metadata("Cluster Information Base", "1.0",
 728                     "Cluster Information Base Options",
 729                     "This is a fake resource that details the options that can be configured for the Cluster Information Base.",
 730                     cib_opts, DIMOF(cib_opts));
 731 }
 732 
 733 void
 734 verify_cib_options(GHashTable * options)
     /* [previous][next][first][last][top][bottom][index][help] */
 735 {
 736     verify_all_options(options, cib_opts, DIMOF(cib_opts));
 737 }
 738 
 739 const char *
 740 cib_pref(GHashTable * options, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 741 {
 742     return get_cluster_pref(options, cib_opts, DIMOF(cib_opts), name);
 743 }
 744 
 745 gboolean
 746 cib_read_config(GHashTable * options, xmlNode * current_cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 747 {
 748     xmlNode *config = NULL;
 749     crm_time_t *now = NULL;
 750 
 751     if (options == NULL || current_cib == NULL) {
 752         return FALSE;
 753     }
 754 
 755     now = crm_time_new(NULL);
 756 
 757     g_hash_table_remove_all(options);
 758 
 759     config = get_object_root(XML_CIB_TAG_CRMCONFIG, current_cib);
 760     if (config) {
 761         unpack_instance_attributes(current_cib, config, XML_CIB_TAG_PROPSET, NULL, options,
 762                                    CIB_OPTIONS_FIRST, TRUE, now);
 763     }
 764 
 765     verify_cib_options(options);
 766 
 767     crm_time_free(now);
 768 
 769     return TRUE;
 770 }
 771 
 772 int
 773 cib_apply_patch_event(xmlNode * event, xmlNode * input, xmlNode ** output, int level)
     /* [previous][next][first][last][top][bottom][index][help] */
 774 {
 775     int rc = pcmk_err_generic;
 776 
 777     xmlNode *diff = NULL;
 778 
 779     CRM_ASSERT(event);
 780     CRM_ASSERT(input);
 781     CRM_ASSERT(output);
 782 
 783     crm_element_value_int(event, F_CIB_RC, &rc);
 784     diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
 785 
 786     if (rc < pcmk_ok || diff == NULL) {
 787         return rc;
 788     }
 789 
 790     if (level > LOG_CRIT) {
 791         xml_log_patchset(level, "Config update", diff);
 792     }
 793 
 794     if (input != NULL) {
 795         rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output, NULL);
 796 
 797         if (rc != pcmk_ok) {
 798             crm_debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), rc, *output);
 799 
 800             if (rc == -pcmk_err_old_data) {
 801                 crm_trace("Masking error, we already have the supplied update");
 802                 return pcmk_ok;
 803             }
 804             free_xml(*output); *output = NULL;
 805 
 806             return rc;
 807         }
 808     }
 809 
 810     return rc;
 811 }
 812 
 813 /* v2 and v2 patch formats */
 814 #define XPATH_CONFIG_CHANGE \
 815     "//" XML_CIB_TAG_CRMCONFIG " | " \
 816     "//" XML_DIFF_CHANGE "[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
 817 
 818 gboolean
 819 cib_internal_config_changed(xmlNode *diff)
     /* [previous][next][first][last][top][bottom][index][help] */
 820 {
 821     gboolean changed = FALSE;
 822 
 823     if (diff) {
 824         xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
 825 
 826         if (numXpathResults(xpathObj) > 0) {
 827             changed = TRUE;
 828         }
 829         freeXpathObject(xpathObj);
 830     }
 831     return changed;
 832 }
 833 
 834 int
 835 cib_internal_op(cib_t * cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 836                 const char *section, xmlNode * data,
 837                 xmlNode ** output_data, int call_options, const char *user_name)
 838 {
 839     int (*delegate) (cib_t * cib, const char *op, const char *host,
 840                      const char *section, xmlNode * data,
 841                      xmlNode ** output_data, int call_options, const char *user_name) =
 842         cib->delegate_fn;
 843 
 844 #if ENABLE_ACL
 845     if(user_name == NULL) {
 846         user_name = getenv("CIB_user");
 847     }
 848 #endif
 849 
 850     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
 851 }

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