root/lib/cib/cib_utils.c

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

DEFINITIONS

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

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

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