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. cib__get_notify_patchset
  5. element_in_patchset_v1
  6. element_in_patchset_v2
  7. cib__element_in_patchset
  8. createEmptyCib
  9. cib_acl_enabled
  10. should_copy_cib
  11. cib_perform_op
  12. cib__create_op
  13. validate_transaction_request
  14. cib__extend_transaction
  15. cib_native_callback
  16. cib_native_notify
  17. cib_metadata
  18. verify_cib_options
  19. cib_pref
  20. cib_read_config
  21. cib_internal_op
  22. cib_apply_patch_event
  23. cib__signon_query
  24. cib__clean_up_connection
  25. get_object_path
  26. get_object_parent
  27. 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/cib_internal.h>
  24 #include <crm/common/xml.h>
  25 #include <crm/common/xml_internal.h>
  26 #include <crm/pengine/rules.h>
  27 
  28 xmlNode *
  29 cib_get_generation(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31     xmlNode *the_cib = NULL;
  32     xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
  33 
  34     cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
  35     if (the_cib != NULL) {
  36         copy_in_properties(generation, the_cib);
  37         free_xml(the_cib);
  38     }
  39 
  40     return generation;
  41 }
  42 
  43 gboolean
  44 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46     *epoch = -1;
  47     *updates = -1;
  48     *admin_epoch = -1;
  49 
  50     if (cib == NULL) {
  51         return FALSE;
  52 
  53     } else {
  54         crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
  55         crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
  56         crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
  57     }
  58     return TRUE;
  59 }
  60 
  61 gboolean
  62 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
     /* [previous][next][first][last][top][bottom][index][help] */
  63                          int *_admin_epoch, int *_epoch, int *_updates)
  64 {
  65     int add[] = { 0, 0, 0 };
  66     int del[] = { 0, 0, 0 };
  67 
  68     xml_patch_versions(diff, add, del);
  69 
  70     *admin_epoch = add[0];
  71     *epoch = add[1];
  72     *updates = add[2];
  73 
  74     *_admin_epoch = del[0];
  75     *_epoch = del[1];
  76     *_updates = del[2];
  77 
  78     return TRUE;
  79 }
  80 
  81 /*!
  82  * \internal
  83  * \brief Get the XML patchset from a CIB diff notification
  84  *
  85  * \param[in]  msg       CIB diff notification
  86  * \param[out] patchset  Where to store XML patchset
  87  *
  88  * \return Standard Pacemaker return code
  89  */
  90 int
  91 cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     int rc = pcmk_err_generic;
  94 
  95     CRM_ASSERT(patchset != NULL);
  96     *patchset = NULL;
  97 
  98     if (msg == NULL) {
  99         crm_err("CIB diff notification received with no XML");
 100         return ENOMSG;
 101     }
 102 
 103     if ((crm_element_value_int(msg, F_CIB_RC, &rc) != 0) || (rc != pcmk_ok)) {
 104         crm_warn("Ignore failed CIB update: %s " CRM_XS " rc=%d",
 105                  pcmk_strerror(rc), rc);
 106         crm_log_xml_debug(msg, "failed");
 107         return pcmk_legacy2rc(rc);
 108     }
 109 
 110     *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 111 
 112     if (*patchset == NULL) {
 113         crm_err("CIB diff notification received with no patchset");
 114         return ENOMSG;
 115     }
 116     return pcmk_rc_ok;
 117 }
 118 
 119 #define XPATH_DIFF_V1 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
 120 
 121 /*!
 122  * \internal
 123  * \brief Check whether a given CIB element was modified in a CIB patchset (v1)
 124  *
 125  * \param[in] patchset  CIB XML patchset
 126  * \param[in] element   XML tag of CIB element to check (\c NULL is equivalent
 127  *                      to \c XML_TAG_CIB)
 128  *
 129  * \return \c true if \p element was modified, or \c false otherwise
 130  */
 131 static bool
 132 element_in_patchset_v1(const xmlNode *patchset, const char *element)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s",
 135                                     pcmk__s(element, XML_TAG_CIB));
 136     xmlXPathObject *xpath_obj = xpath_search(patchset, xpath);
 137 
 138     free(xpath);
 139 
 140     if (xpath_obj == NULL) {
 141         return false;
 142     }
 143     freeXpathObject(xpath_obj);
 144     return true;
 145 }
 146 
 147 /*!
 148  * \internal
 149  * \brief Check whether a given CIB element was modified in a CIB patchset (v2)
 150  *
 151  * \param[in] patchset  CIB XML patchset
 152  * \param[in] element   XML tag of CIB element to check (\c NULL is equivalent
 153  *                      to \c XML_TAG_CIB). Supported values include any CIB
 154  *                      element supported by \c pcmk__cib_abs_xpath_for().
 155  *
 156  * \return \c true if \p element was modified, or \c false otherwise
 157  */
 158 static bool
 159 element_in_patchset_v2(const xmlNode *patchset, const char *element)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161     const char *element_xpath = pcmk__cib_abs_xpath_for(element);
 162     const char *parent_xpath = pcmk_cib_parent_name_for(element);
 163     char *element_regex = NULL;
 164     bool rc = false;
 165 
 166     CRM_CHECK(element_xpath != NULL, return false); // Unsupported element
 167 
 168     // Matches if and only if element_xpath is part of a changed path
 169     element_regex = crm_strdup_printf("^%s(/|$)", element_xpath);
 170 
 171     for (const xmlNode *change = first_named_child(patchset, XML_DIFF_CHANGE);
 172          change != NULL; change = crm_next_same_xml(change)) {
 173 
 174         const char *op = crm_element_value(change, F_CIB_OPERATION);
 175         const char *diff_xpath = crm_element_value(change, XML_DIFF_PATH);
 176 
 177         if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) {
 178             // Change to an existing element
 179             rc = true;
 180             break;
 181         }
 182 
 183         if (pcmk__str_eq(op, "create", pcmk__str_none)
 184             && pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none)
 185             && pcmk__xe_is(pcmk__xml_first_child(change), element)) {
 186 
 187             // Newly added element
 188             rc = true;
 189             break;
 190         }
 191     }
 192 
 193     free(element_regex);
 194     return rc;
 195 }
 196 
 197 /*!
 198  * \internal
 199  * \brief Check whether a given CIB element was modified in a CIB patchset
 200  *
 201  * \param[in] patchset  CIB XML patchset
 202  * \param[in] element   XML tag of CIB element to check (\c NULL is equivalent
 203  *                      to \c XML_TAG_CIB). Supported values include any CIB
 204  *                      element supported by \c pcmk__cib_abs_xpath_for().
 205  *
 206  * \return \c true if \p element was modified, or \c false otherwise
 207  */
 208 bool
 209 cib__element_in_patchset(const xmlNode *patchset, const char *element)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211     int format = 1;
 212 
 213     CRM_ASSERT(patchset != NULL);
 214 
 215     crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
 216     switch (format) {
 217         case 1:
 218             return element_in_patchset_v1(patchset, element);
 219 
 220         case 2:
 221             return element_in_patchset_v2(patchset, element);
 222 
 223         default:
 224             crm_warn("Unknown patch format: %d", format);
 225             return false;
 226     }
 227 }
 228 
 229 /*!
 230  * \brief Create XML for a new (empty) CIB
 231  *
 232  * \param[in] cib_epoch   What to use as "epoch" CIB property
 233  *
 234  * \return Newly created XML for empty CIB
 235  * \note It is the caller's responsibility to free the result with free_xml().
 236  */
 237 xmlNode *
 238 createEmptyCib(int cib_epoch)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240     xmlNode *cib_root = NULL, *config = NULL;
 241 
 242     cib_root = create_xml_node(NULL, XML_TAG_CIB);
 243     crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 244     crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema());
 245 
 246     crm_xml_add_int(cib_root, XML_ATTR_GENERATION, cib_epoch);
 247     crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0);
 248     crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0);
 249 
 250     config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
 251     create_xml_node(cib_root, XML_CIB_TAG_STATUS);
 252 
 253     create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
 254     create_xml_node(config, XML_CIB_TAG_NODES);
 255     create_xml_node(config, XML_CIB_TAG_RESOURCES);
 256     create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
 257 
 258 #if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
 259     {
 260         xmlNode *rsc_defaults = create_xml_node(config, XML_CIB_TAG_RSCCONFIG);
 261         xmlNode *meta = create_xml_node(rsc_defaults, XML_TAG_META_SETS);
 262         xmlNode *nvpair = create_xml_node(meta, XML_CIB_TAG_NVPAIR);
 263 
 264         crm_xml_add(meta, XML_ATTR_ID, "build-resource-defaults");
 265         crm_xml_add(nvpair, XML_ATTR_ID, "build-" XML_RSC_ATTR_STICKINESS);
 266         crm_xml_add(nvpair, XML_NVPAIR_ATTR_NAME, XML_RSC_ATTR_STICKINESS);
 267         crm_xml_add_int(nvpair, XML_NVPAIR_ATTR_VALUE,
 268                         PCMK__RESOURCE_STICKINESS_DEFAULT);
 269     }
 270 #endif
 271     return cib_root;
 272 }
 273 
 274 static bool
 275 cib_acl_enabled(xmlNode *xml, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277     bool rc = FALSE;
 278 
 279     if(pcmk_acl_required(user)) {
 280         const char *value = NULL;
 281         GHashTable *options = pcmk__strkey_table(free, free);
 282 
 283         cib_read_config(options, xml);
 284         value = cib_pref(options, "enable-acl");
 285         rc = crm_is_true(value);
 286         g_hash_table_destroy(options);
 287     }
 288 
 289     crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
 290     return rc;
 291 }
 292 
 293 /*!
 294  * \internal
 295  * \brief Determine whether to perform operations on a scratch copy of the CIB
 296  *
 297  * \param[in] op            CIB operation
 298  * \param[in] section       CIB section
 299  * \param[in] call_options  CIB call options
 300  *
 301  * \return \p true if we should make a copy of the CIB, or \p false otherwise
 302  */
 303 static bool
 304 should_copy_cib(const char *op, const char *section, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306     if (pcmk_is_set(call_options, cib_dryrun)) {
 307         // cib_dryrun implies a scratch copy by definition; no side effects
 308         return true;
 309     }
 310 
 311     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
 312         /* Commit-transaction must make a copy for atomicity. We must revert to
 313          * the original CIB if the entire transaction cannot be applied
 314          * successfully.
 315          */
 316         return true;
 317     }
 318 
 319     if (pcmk_is_set(call_options, cib_transaction)) {
 320         /* If cib_transaction is set, then we're in the process of committing a
 321          * transaction. The commit-transaction request already made a scratch
 322          * copy, and we're accumulating changes in that copy.
 323          */
 324         return false;
 325     }
 326 
 327     if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_none)) {
 328         /* Copying large CIBs accounts for a huge percentage of our CIB usage,
 329          * and this avoids some of it.
 330          *
 331          * @TODO: Is this safe? See discussion at
 332          * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
 333          */
 334         return false;
 335     }
 336 
 337     // Default behavior is to operate on a scratch copy
 338     return true;
 339 }
 340 
 341 int
 342 cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query,
     /* [previous][next][first][last][top][bottom][index][help] */
 343                const char *section, xmlNode *req, xmlNode *input,
 344                bool manage_counters, bool *config_changed,
 345                xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff,
 346                xmlNode **output)
 347 {
 348     int rc = pcmk_ok;
 349     bool check_schema = true;
 350     bool make_copy = true;
 351     xmlNode *top = NULL;
 352     xmlNode *scratch = NULL;
 353     xmlNode *patchset_cib = NULL;
 354     xmlNode *local_diff = NULL;
 355 
 356     const char *new_version = NULL;
 357     const char *user = crm_element_value(req, F_CIB_USER);
 358     bool with_digest = false;
 359 
 360     crm_trace("Begin %s%s%s op",
 361               (pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
 362               (is_query? "read-only " : ""), op);
 363 
 364     CRM_CHECK(output != NULL, return -ENOMSG);
 365     CRM_CHECK(current_cib != NULL, return -ENOMSG);
 366     CRM_CHECK(result_cib != NULL, return -ENOMSG);
 367     CRM_CHECK(config_changed != NULL, return -ENOMSG);
 368 
 369     if(output) {
 370         *output = NULL;
 371     }
 372 
 373     *result_cib = NULL;
 374     *config_changed = false;
 375 
 376     if (fn == NULL) {
 377         return -EINVAL;
 378     }
 379 
 380     if (is_query) {
 381         xmlNode *cib_ro = *current_cib;
 382         xmlNode *cib_filtered = NULL;
 383 
 384         if (cib_acl_enabled(cib_ro, user)
 385             && xml_acl_filtered_copy(user, *current_cib, *current_cib,
 386                                      &cib_filtered)) {
 387 
 388             if (cib_filtered == NULL) {
 389                 crm_debug("Pre-filtered the entire cib");
 390                 return -EACCES;
 391             }
 392             cib_ro = cib_filtered;
 393             crm_log_xml_trace(cib_ro, "filtered");
 394         }
 395 
 396         rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
 397 
 398         if(output == NULL || *output == NULL) {
 399             /* nothing */
 400 
 401         } else if(cib_filtered == *output) {
 402             cib_filtered = NULL; /* Let them have this copy */
 403 
 404         } else if (*output == *current_cib) {
 405             /* They already know not to free it */
 406 
 407         } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
 408             /* We're about to free the document of which *output is a part */
 409             *output = copy_xml(*output);
 410 
 411         } else if ((*output)->doc == (*current_cib)->doc) {
 412             /* Give them a copy they can free */
 413             *output = copy_xml(*output);
 414         }
 415 
 416         free_xml(cib_filtered);
 417         return rc;
 418     }
 419 
 420     make_copy = should_copy_cib(op, section, call_options);
 421 
 422     if (!make_copy) {
 423         /* Conditional on v2 patch style */
 424 
 425         scratch = *current_cib;
 426 
 427         // Make a copy of the top-level element to store version details
 428         top = create_xml_node(NULL, (const char *) scratch->name);
 429         copy_in_properties(top, scratch);
 430         patchset_cib = top;
 431 
 432         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
 433         rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
 434 
 435         /* If scratch points to a new object now (for example, after an erase
 436          * operation), then *current_cib should point to the same object.
 437          */
 438         *current_cib = scratch;
 439 
 440     } else {
 441         scratch = copy_xml(*current_cib);
 442         patchset_cib = *current_cib;
 443 
 444         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
 445         rc = (*fn) (op, call_options, section, req, input, *current_cib,
 446                     &scratch, output);
 447 
 448         if ((scratch != NULL) && !xml_tracking_changes(scratch)) {
 449             crm_trace("Inferring changes after %s op", op);
 450             xml_track_changes(scratch, user, *current_cib,
 451                               cib_acl_enabled(*current_cib, user));
 452             xml_calculate_changes(*current_cib, scratch);
 453         }
 454         CRM_CHECK(*current_cib != scratch, return -EINVAL);
 455     }
 456 
 457     xml_acl_disable(scratch); /* Allow the system to make any additional changes */
 458 
 459     if (rc == pcmk_ok && scratch == NULL) {
 460         rc = -EINVAL;
 461         goto done;
 462 
 463     } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
 464         crm_trace("ACL rejected part or all of the proposed changes");
 465         rc = -EACCES;
 466         goto done;
 467 
 468     } else if (rc != pcmk_ok) {
 469         goto done;
 470     }
 471 
 472     if (scratch) {
 473         new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
 474 
 475         if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
 476             crm_err("Discarding update with feature set '%s' greater than our own '%s'",
 477                     new_version, CRM_FEATURE_SET);
 478             rc = -EPROTONOSUPPORT;
 479             goto done;
 480         }
 481     }
 482 
 483     if (patchset_cib != NULL) {
 484         int old = 0;
 485         int new = 0;
 486 
 487         crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
 488         crm_element_value_int(patchset_cib, XML_ATTR_GENERATION_ADMIN, &old);
 489 
 490         if (old > new) {
 491             crm_err("%s went backwards: %d -> %d (Opts: %#x)",
 492                     XML_ATTR_GENERATION_ADMIN, old, new, call_options);
 493             crm_log_xml_warn(req, "Bad Op");
 494             crm_log_xml_warn(input, "Bad Data");
 495             rc = -pcmk_err_old_data;
 496 
 497         } else if (old == new) {
 498             crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
 499             crm_element_value_int(patchset_cib, XML_ATTR_GENERATION, &old);
 500             if (old > new) {
 501                 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
 502                         XML_ATTR_GENERATION, old, new, call_options);
 503                 crm_log_xml_warn(req, "Bad Op");
 504                 crm_log_xml_warn(input, "Bad Data");
 505                 rc = -pcmk_err_old_data;
 506             }
 507         }
 508     }
 509 
 510     crm_trace("Massaging CIB contents");
 511     pcmk__strip_xml_text(scratch);
 512     fix_plus_plus_recursive(scratch);
 513 
 514     if (!make_copy) {
 515         /* At this point, patchset_cib is just the "cib" tag and its properties.
 516          *
 517          * The v1 format would barf on this, but we know the v2 patch
 518          * format only needs it for the top-level version fields
 519          */
 520         local_diff = xml_create_patchset(2, patchset_cib, scratch,
 521                                          config_changed, manage_counters);
 522 
 523     } else {
 524         static time_t expires = 0;
 525         time_t tm_now = time(NULL);
 526 
 527         if (expires < tm_now) {
 528             expires = tm_now + 60;  /* Validate clients are correctly applying v2-style diffs at most once a minute */
 529             with_digest = true;
 530         }
 531 
 532         local_diff = xml_create_patchset(0, patchset_cib, scratch,
 533                                          config_changed, manage_counters);
 534     }
 535 
 536     pcmk__log_xml_changes(LOG_TRACE, scratch);
 537     xml_accept_changes(scratch);
 538 
 539     if(local_diff) {
 540         patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
 541         pcmk__log_xml_patchset(LOG_INFO, local_diff);
 542         crm_log_xml_trace(local_diff, "raw patch");
 543     }
 544 
 545     if (make_copy && (local_diff != NULL)) {
 546         // Original to compare against doesn't exist
 547         pcmk__if_tracing(
 548             {
 549                 // Validate the calculated patch set
 550                 int test_rc = pcmk_ok;
 551                 int format = 1;
 552                 xmlNode *cib_copy = copy_xml(patchset_cib);
 553 
 554                 crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
 555                 test_rc = xml_apply_patchset(cib_copy, local_diff,
 556                                              manage_counters);
 557 
 558                 if (test_rc != pcmk_ok) {
 559                     save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
 560                     save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
 561                     save_xml_to_file(scratch, "PatchApply:actual", NULL);
 562                     save_xml_to_file(local_diff, "PatchApply:diff", NULL);
 563                     crm_err("v%d patchset error, patch failed to apply: %s "
 564                             "(%d)",
 565                             format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
 566                             test_rc);
 567                 }
 568                 free_xml(cib_copy);
 569             },
 570             {}
 571         );
 572     }
 573 
 574     if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
 575         /* Throttle the amount of costly validation we perform due to status updates
 576          * a) we don't really care whats in the status section
 577          * b) we don't validate any of its contents at the moment anyway
 578          */
 579         check_schema = false;
 580     }
 581 
 582     /* === scratch must not be modified after this point ===
 583      * Exceptions, anything in:
 584 
 585      static filter_t filter[] = {
 586      { 0, XML_ATTR_ORIGIN },
 587      { 0, XML_CIB_ATTR_WRITTEN },
 588      { 0, XML_ATTR_UPDATE_ORIG },
 589      { 0, XML_ATTR_UPDATE_CLIENT },
 590      { 0, XML_ATTR_UPDATE_USER },
 591      };
 592      */
 593 
 594     if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
 595         const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
 596 
 597         pcmk__xe_add_last_written(scratch);
 598         if (schema) {
 599             static int minimum_schema = 0;
 600             int current_schema = get_schema_version(schema);
 601 
 602             if (minimum_schema == 0) {
 603                 minimum_schema = get_schema_version("pacemaker-1.2");
 604             }
 605 
 606             /* Does the CIB support the "update-*" attributes... */
 607             if (current_schema >= minimum_schema) {
 608                 /* Ensure values of origin, client, and user in scratch match
 609                  * the values in req
 610                  */
 611                 const char *origin = crm_element_value(req, F_ORIG);
 612                 const char *client = crm_element_value(req, F_CIB_CLIENTNAME);
 613 
 614                 if (origin != NULL) {
 615                     crm_xml_add(scratch, XML_ATTR_UPDATE_ORIG, origin);
 616                 } else {
 617                     xml_remove_prop(scratch, XML_ATTR_UPDATE_ORIG);
 618                 }
 619 
 620                 if (client != NULL) {
 621                     crm_xml_add(scratch, XML_ATTR_UPDATE_CLIENT, user);
 622                 } else {
 623                     xml_remove_prop(scratch, XML_ATTR_UPDATE_CLIENT);
 624                 }
 625 
 626                 if (user != NULL) {
 627                     crm_xml_add(scratch, XML_ATTR_UPDATE_USER, user);
 628                 } else {
 629                     xml_remove_prop(scratch, XML_ATTR_UPDATE_USER);
 630                 }
 631             }
 632         }
 633     }
 634 
 635     crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
 636     if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, true)) {
 637         const char *current_schema = crm_element_value(scratch,
 638                                                        XML_ATTR_VALIDATION);
 639 
 640         crm_warn("Updated CIB does not validate against %s schema",
 641                  pcmk__s(current_schema, "unspecified"));
 642         rc = -pcmk_err_schema_validation;
 643     }
 644 
 645   done:
 646 
 647     *result_cib = scratch;
 648 
 649     /* @TODO: This may not work correctly with !make_copy, since we don't
 650      * keep the original CIB.
 651      */
 652     if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
 653         && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
 654 
 655         if (*result_cib == NULL) {
 656             crm_debug("Pre-filtered the entire cib result");
 657         }
 658         free_xml(scratch);
 659     }
 660 
 661     if(diff) {
 662         *diff = local_diff;
 663     } else {
 664         free_xml(local_diff);
 665     }
 666 
 667     free_xml(top);
 668     crm_trace("Done");
 669     return rc;
 670 }
 671 
 672 int
 673 cib__create_op(cib_t *cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 674                const char *section, xmlNode *data, int call_options,
 675                const char *user_name, const char *client_name,
 676                xmlNode **op_msg)
 677 {
 678     CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
 679 
 680     *op_msg = create_xml_node(NULL, T_CIB_COMMAND);
 681     if (*op_msg == NULL) {
 682         return -EPROTO;
 683     }
 684 
 685     cib->call_id++;
 686     if (cib->call_id < 1) {
 687         cib->call_id = 1;
 688     }
 689 
 690     crm_xml_add(*op_msg, F_XML_TAGNAME, T_CIB_COMMAND);
 691     crm_xml_add(*op_msg, F_TYPE, T_CIB);
 692     crm_xml_add(*op_msg, F_CIB_OPERATION, op);
 693     crm_xml_add(*op_msg, F_CIB_HOST, host);
 694     crm_xml_add(*op_msg, F_CIB_SECTION, section);
 695     crm_xml_add(*op_msg, F_CIB_USER, user_name);
 696     crm_xml_add(*op_msg, F_CIB_CLIENTNAME, client_name);
 697     crm_xml_add_int(*op_msg, F_CIB_CALLID, cib->call_id);
 698 
 699     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
 700     crm_xml_add_int(*op_msg, F_CIB_CALLOPTS, call_options);
 701 
 702     if (data != NULL) {
 703         add_message_xml(*op_msg, F_CIB_CALLDATA, data);
 704     }
 705 
 706     if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
 707         CRM_CHECK(pcmk_is_set(call_options, cib_scope_local),
 708                   free_xml(*op_msg); return -EPROTO);
 709     }
 710     return pcmk_ok;
 711 }
 712 
 713 /*!
 714  * \internal
 715  * \brief Check whether a CIB request is supported in a transaction
 716  *
 717  * \param[in] request  CIB request
 718  *
 719  * \return Standard Pacemaker return code
 720  */
 721 static int
 722 validate_transaction_request(const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 723 {
 724     const char *op = crm_element_value(request, F_CIB_OPERATION);
 725     const char *host = crm_element_value(request, F_CIB_HOST);
 726     const cib__operation_t *operation = NULL;
 727     int rc = cib__get_operation(op, &operation);
 728 
 729     if (rc != pcmk_rc_ok) {
 730         // cib__get_operation() logs error
 731         return rc;
 732     }
 733 
 734     if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
 735         crm_err("Operation %s is not supported in CIB transactions", op);
 736         return EOPNOTSUPP;
 737     }
 738 
 739     if (host != NULL) {
 740         crm_err("Operation targeting a specific node (%s) is not supported in "
 741                 "a CIB transaction",
 742                 host);
 743         return EOPNOTSUPP;
 744     }
 745     return pcmk_rc_ok;
 746 }
 747 
 748 /*!
 749  * \internal
 750  * \brief Append a CIB request to a CIB transaction
 751  *
 752  * \param[in,out] cib      CIB client whose transaction to extend
 753  * \param[in,out] request  Request to add to transaction
 754  *
 755  * \return Legacy Pacemaker return code
 756  */
 757 int
 758 cib__extend_transaction(cib_t *cib, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 759 {
 760     int rc = pcmk_rc_ok;
 761 
 762     CRM_ASSERT((cib != NULL) && (request != NULL));
 763 
 764     rc = validate_transaction_request(request);
 765 
 766     if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
 767         rc = pcmk_rc_no_transaction;
 768     }
 769 
 770     if (rc == pcmk_rc_ok) {
 771         add_node_copy(cib->transaction, request);
 772 
 773     } else {
 774         const char *op = crm_element_value(request, F_CIB_OPERATION);
 775         const char *client_id = NULL;
 776 
 777         cib->cmds->client_id(cib, NULL, &client_id);
 778         crm_err("Failed to add '%s' operation to transaction for client %s: %s",
 779                 op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
 780         crm_log_xml_info(request, "failed");
 781     }
 782     return pcmk_rc2legacy(rc);
 783 }
 784 
 785 void
 786 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 787 {
 788     xmlNode *output = NULL;
 789     cib_callback_client_t *blob = NULL;
 790 
 791     if (msg != NULL) {
 792         crm_element_value_int(msg, F_CIB_RC, &rc);
 793         crm_element_value_int(msg, F_CIB_CALLID, &call_id);
 794         output = get_message_xml(msg, F_CIB_CALLDATA);
 795     }
 796 
 797     blob = cib__lookup_id(call_id);
 798 
 799     if (blob == NULL) {
 800         crm_trace("No callback found for call %d", call_id);
 801     }
 802 
 803     if (cib == NULL) {
 804         crm_debug("No cib object supplied");
 805     }
 806 
 807     if (rc == -pcmk_err_diff_resync) {
 808         /* This is an internal value that clients do not and should not care about */
 809         rc = pcmk_ok;
 810     }
 811 
 812     if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
 813         crm_trace("Invoking callback %s for call %d",
 814                   pcmk__s(blob->id, "without ID"), call_id);
 815         blob->callback(msg, call_id, rc, output, blob->user_data);
 816 
 817     } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
 818         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
 819         crm_log_xml_debug(msg, "Failed CIB Update");
 820     }
 821 
 822     /* This may free user_data, so do it after the callback */
 823     if (blob) {
 824         remove_cib_op_callback(call_id, FALSE);
 825     }
 826 
 827     if (cib && cib->op_callback != NULL) {
 828         crm_trace("Invoking global callback for call %d", call_id);
 829         cib->op_callback(msg, call_id, rc, output);
 830     }
 831     crm_trace("OP callback activated for %d", call_id);
 832 }
 833 
 834 void
 835 cib_native_notify(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 836 {
 837     xmlNode *msg = user_data;
 838     cib_notify_client_t *entry = data;
 839     const char *event = NULL;
 840 
 841     if (msg == NULL) {
 842         crm_warn("Skipping callback - NULL message");
 843         return;
 844     }
 845 
 846     event = crm_element_value(msg, F_SUBTYPE);
 847 
 848     if (entry == NULL) {
 849         crm_warn("Skipping callback - NULL callback client");
 850         return;
 851 
 852     } else if (entry->callback == NULL) {
 853         crm_warn("Skipping callback - NULL callback");
 854         return;
 855 
 856     } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
 857         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
 858         return;
 859     }
 860 
 861     crm_trace("Invoking callback for %p/%s event...", entry, event);
 862     entry->callback(event, msg);
 863     crm_trace("Callback invoked...");
 864 }
 865 
 866 static pcmk__cluster_option_t cib_opts[] = {
 867     /* name, legacy name, type, allowed values,
 868      * default value, validator,
 869      * short description,
 870      * long description
 871      */
 872     {
 873         "enable-acl", NULL, "boolean", NULL,
 874         "false", pcmk__valid_boolean,
 875         N_("Enable Access Control Lists (ACLs) for the CIB"),
 876         NULL
 877     },
 878     {
 879         "cluster-ipc-limit", NULL, "integer", NULL,
 880         "500", pcmk__valid_positive_number,
 881         N_("Maximum IPC message backlog before disconnecting a cluster daemon"),
 882         N_("Raise this if log has \"Evicting client\" messages for cluster daemon"
 883             " PIDs (a good value is the number of resources in the cluster"
 884             " multiplied by the number of nodes).")
 885     },
 886 };
 887 
 888 void
 889 cib_metadata(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 890 {
 891     const char *desc_short = "Cluster Information Base manager options";
 892     const char *desc_long = "Cluster options used by Pacemaker's Cluster "
 893                             "Information Base manager";
 894 
 895     gchar *s = pcmk__format_option_metadata("pacemaker-based", desc_short,
 896                                             desc_long, cib_opts,
 897                                             PCMK__NELEM(cib_opts));
 898     printf("%s", s);
 899     g_free(s);
 900 }
 901 
 902 static void
 903 verify_cib_options(GHashTable *options)
     /* [previous][next][first][last][top][bottom][index][help] */
 904 {
 905     pcmk__validate_cluster_options(options, cib_opts, PCMK__NELEM(cib_opts));
 906 }
 907 
 908 const char *
 909 cib_pref(GHashTable * options, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 910 {
 911     return pcmk__cluster_option(options, cib_opts, PCMK__NELEM(cib_opts),
 912                                 name);
 913 }
 914 
 915 gboolean
 916 cib_read_config(GHashTable * options, xmlNode * current_cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 917 {
 918     xmlNode *config = NULL;
 919     crm_time_t *now = NULL;
 920 
 921     if (options == NULL || current_cib == NULL) {
 922         return FALSE;
 923     }
 924 
 925     now = crm_time_new(NULL);
 926 
 927     g_hash_table_remove_all(options);
 928 
 929     config = pcmk_find_cib_element(current_cib, XML_CIB_TAG_CRMCONFIG);
 930     if (config) {
 931         pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL,
 932                           options, CIB_OPTIONS_FIRST, TRUE, now, NULL);
 933     }
 934 
 935     verify_cib_options(options);
 936 
 937     crm_time_free(now);
 938 
 939     return TRUE;
 940 }
 941 
 942 int
 943 cib_internal_op(cib_t * cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 944                 const char *section, xmlNode * data,
 945                 xmlNode ** output_data, int call_options, const char *user_name)
 946 {
 947     int (*delegate) (cib_t * cib, const char *op, const char *host,
 948                      const char *section, xmlNode * data,
 949                      xmlNode ** output_data, int call_options, const char *user_name) =
 950         cib->delegate_fn;
 951 
 952     if(user_name == NULL) {
 953         user_name = getenv("CIB_user");
 954     }
 955 
 956     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
 957 }
 958 
 959 /*!
 960  * \brief Apply a CIB update patch to a given CIB
 961  *
 962  * \param[in]  event   CIB update patch
 963  * \param[in]  input   CIB to patch
 964  * \param[out] output  Resulting CIB after patch
 965  * \param[in]  level   Log the patch at this log level (unless LOG_CRIT)
 966  *
 967  * \return Legacy Pacemaker return code
 968  * \note sbd calls this function
 969  */
 970 int
 971 cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
     /* [previous][next][first][last][top][bottom][index][help] */
 972                       int level)
 973 {
 974     int rc = pcmk_err_generic;
 975 
 976     xmlNode *diff = NULL;
 977 
 978     CRM_ASSERT(event);
 979     CRM_ASSERT(input);
 980     CRM_ASSERT(output);
 981 
 982     crm_element_value_int(event, F_CIB_RC, &rc);
 983     diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
 984 
 985     if (rc < pcmk_ok || diff == NULL) {
 986         return rc;
 987     }
 988 
 989     if (level > LOG_CRIT) {
 990         pcmk__log_xml_patchset(level, diff);
 991     }
 992 
 993     if (input != NULL) {
 994         rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
 995                               NULL);
 996 
 997         if (rc != pcmk_ok) {
 998             crm_debug("Update didn't apply: %s (%d) %p",
 999                       pcmk_strerror(rc), rc, *output);
1000 
1001             if (rc == -pcmk_err_old_data) {
1002                 crm_trace("Masking error, we already have the supplied update");
1003                 return pcmk_ok;
1004             }
1005             free_xml(*output);
1006             *output = NULL;
1007             return rc;
1008         }
1009     }
1010     return rc;
1011 }
1012 
1013 #define log_signon_query_err(out, fmt, args...) do {    \
1014         if (out != NULL) {                              \
1015             out->err(out, fmt, ##args);                 \
1016         } else {                                        \
1017             crm_err(fmt, ##args);                       \
1018         }                                               \
1019     } while (0)
1020 
1021 int
1022 cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
     /* [previous][next][first][last][top][bottom][index][help] */
1023 {
1024     int rc = pcmk_rc_ok;
1025     cib_t *cib_conn = NULL;
1026 
1027     CRM_ASSERT(cib_object != NULL);
1028 
1029     if (cib == NULL) {
1030         cib_conn = cib_new();
1031     } else {
1032         if (*cib == NULL) {
1033             *cib = cib_new();
1034         }
1035         cib_conn = *cib;
1036     }
1037 
1038     if (cib_conn == NULL) {
1039         return ENOMEM;
1040     }
1041 
1042     if (cib_conn->state == cib_disconnected) {
1043         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
1044         rc = pcmk_legacy2rc(rc);
1045     }
1046 
1047     if (rc != pcmk_rc_ok) {
1048         log_signon_query_err(out, "Could not connect to the CIB: %s",
1049                              pcmk_rc_str(rc));
1050         goto done;
1051     }
1052 
1053     if (out != NULL) {
1054         out->transient(out, "Querying CIB...");
1055     }
1056     rc = cib_conn->cmds->query(cib_conn, NULL, cib_object,
1057                                cib_scope_local|cib_sync_call);
1058     rc = pcmk_legacy2rc(rc);
1059 
1060     if (rc != pcmk_rc_ok) {
1061         log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
1062     }
1063 
1064 done:
1065     if (cib == NULL) {
1066         cib__clean_up_connection(&cib_conn);
1067     }
1068 
1069     if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
1070         return pcmk_rc_no_input;
1071     }
1072     return rc;
1073 }
1074 
1075 int
1076 cib__clean_up_connection(cib_t **cib)
     /* [previous][next][first][last][top][bottom][index][help] */
1077 {
1078     int rc;
1079 
1080     if (*cib == NULL) {
1081         return pcmk_rc_ok;
1082     }
1083 
1084     rc = (*cib)->cmds->signoff(*cib);
1085     cib_delete(*cib);
1086     *cib = NULL;
1087     return pcmk_legacy2rc(rc);
1088 }
1089 
1090 // Deprecated functions kept only for backward API compatibility
1091 // LCOV_EXCL_START
1092 
1093 #include <crm/cib/util_compat.h>
1094 
1095 const char *
1096 get_object_path(const char *object_type)
     /* [previous][next][first][last][top][bottom][index][help] */
1097 {
1098     return pcmk_cib_xpath_for(object_type);
1099 }
1100 
1101 const char *
1102 get_object_parent(const char *object_type)
     /* [previous][next][first][last][top][bottom][index][help] */
1103 {
1104     return pcmk_cib_parent_name_for(object_type);
1105 }
1106 
1107 xmlNode *
1108 get_object_root(const char *object_type, xmlNode *the_root)
     /* [previous][next][first][last][top][bottom][index][help] */
1109 {
1110     return pcmk_find_cib_element(the_root, object_type);
1111 }
1112 
1113 // LCOV_EXCL_STOP
1114 // End deprecated API

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