root/lib/cib/cib_utils.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_version_details
  2. cib_diff_version_details
  3. cib__get_notify_patchset
  4. createEmptyCib
  5. cib_acl_enabled
  6. should_copy_cib
  7. cib_perform_op
  8. cib__create_op
  9. validate_transaction_request
  10. cib__extend_transaction
  11. cib_native_callback
  12. cib_native_notify
  13. cib_read_config
  14. cib_internal_op
  15. cib_apply_patch_event
  16. cib__signon_query
  17. cib__signon_attempts
  18. cib__clean_up_connection

   1 /*
   2  * Original copyright 2004 International Business Machines
   3  * Later changes copyright 2008-2025 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/common/cib_internal.h>
  23 #include <crm/common/xml.h>
  24 #include <crm/common/xml_internal.h>
  25 
  26 gboolean
  27 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
     /* [previous][next][first][last][top][bottom][index][help] */
  28 {
  29     *epoch = -1;
  30     *updates = -1;
  31     *admin_epoch = -1;
  32 
  33     if (cib == NULL) {
  34         return FALSE;
  35 
  36     } else {
  37         crm_element_value_int(cib, PCMK_XA_EPOCH, epoch);
  38         crm_element_value_int(cib, PCMK_XA_NUM_UPDATES, updates);
  39         crm_element_value_int(cib, PCMK_XA_ADMIN_EPOCH, admin_epoch);
  40     }
  41     return TRUE;
  42 }
  43 
  44 gboolean
  45 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
     /* [previous][next][first][last][top][bottom][index][help] */
  46                          int *_admin_epoch, int *_epoch, int *_updates)
  47 {
  48     int add[] = { 0, 0, 0 };
  49     int del[] = { 0, 0, 0 };
  50 
  51     xml_patch_versions(diff, add, del);
  52 
  53     *admin_epoch = add[0];
  54     *epoch = add[1];
  55     *updates = add[2];
  56 
  57     *_admin_epoch = del[0];
  58     *_epoch = del[1];
  59     *_updates = del[2];
  60 
  61     return TRUE;
  62 }
  63 
  64 /*!
  65  * \internal
  66  * \brief Get the XML patchset from a CIB diff notification
  67  *
  68  * \param[in]  msg       CIB diff notification
  69  * \param[out] patchset  Where to store XML patchset
  70  *
  71  * \return Standard Pacemaker return code
  72  */
  73 int
  74 cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76     int rc = pcmk_err_generic;
  77     xmlNode *wrapper = NULL;
  78 
  79     pcmk__assert(patchset != NULL);
  80     *patchset = NULL;
  81 
  82     if (msg == NULL) {
  83         crm_err("CIB diff notification received with no XML");
  84         return ENOMSG;
  85     }
  86 
  87     if ((crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc) != 0)
  88         || (rc != pcmk_ok)) {
  89 
  90         crm_warn("Ignore failed CIB update: %s " QB_XS " rc=%d",
  91                  pcmk_strerror(rc), rc);
  92         crm_log_xml_debug(msg, "failed");
  93         return pcmk_legacy2rc(rc);
  94     }
  95 
  96     wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL);
  97     *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
  98 
  99     if (*patchset == NULL) {
 100         crm_err("CIB diff notification received with no patchset");
 101         return ENOMSG;
 102     }
 103     return pcmk_rc_ok;
 104 }
 105 
 106 /*!
 107  * \brief Create XML for a new (empty) CIB
 108  *
 109  * \param[in] cib_epoch  What to use as \c PCMK_XA_EPOCH CIB attribute
 110  *
 111  * \return Newly created XML for empty CIB
 112  *
 113  * \note It is the caller's responsibility to free the result with
 114  *       \c pcmk__xml_free().
 115  */
 116 xmlNode *
 117 createEmptyCib(int cib_epoch)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119     xmlNode *cib_root = NULL, *config = NULL;
 120 
 121     cib_root = pcmk__xe_create(NULL, PCMK_XE_CIB);
 122     crm_xml_add(cib_root, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 123     crm_xml_add(cib_root, PCMK_XA_VALIDATE_WITH, pcmk__highest_schema_name());
 124 
 125     crm_xml_add_int(cib_root, PCMK_XA_EPOCH, cib_epoch);
 126     crm_xml_add_int(cib_root, PCMK_XA_NUM_UPDATES, 0);
 127     crm_xml_add_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0);
 128 
 129     config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION);
 130     pcmk__xe_create(cib_root, PCMK_XE_STATUS);
 131 
 132     pcmk__xe_create(config, PCMK_XE_CRM_CONFIG);
 133     pcmk__xe_create(config, PCMK_XE_NODES);
 134     pcmk__xe_create(config, PCMK_XE_RESOURCES);
 135     pcmk__xe_create(config, PCMK_XE_CONSTRAINTS);
 136 
 137 #if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
 138     {
 139         xmlNode *rsc_defaults = pcmk__xe_create(config, PCMK_XE_RSC_DEFAULTS);
 140         xmlNode *meta = pcmk__xe_create(rsc_defaults, PCMK_XE_META_ATTRIBUTES);
 141         xmlNode *nvpair = pcmk__xe_create(meta, PCMK_XE_NVPAIR);
 142 
 143         crm_xml_add(meta, PCMK_XA_ID, "build-resource-defaults");
 144         crm_xml_add(nvpair, PCMK_XA_ID, "build-" PCMK_META_RESOURCE_STICKINESS);
 145         crm_xml_add(nvpair, PCMK_XA_NAME, PCMK_META_RESOURCE_STICKINESS);
 146         crm_xml_add_int(nvpair, PCMK_XA_VALUE,
 147                         PCMK__RESOURCE_STICKINESS_DEFAULT);
 148     }
 149 #endif
 150     return cib_root;
 151 }
 152 
 153 static bool
 154 cib_acl_enabled(xmlNode *xml, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     bool rc = FALSE;
 157 
 158     if(pcmk_acl_required(user)) {
 159         const char *value = NULL;
 160         GHashTable *options = pcmk__strkey_table(free, free);
 161 
 162         cib_read_config(options, xml);
 163         value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL);
 164         rc = crm_is_true(value);
 165         g_hash_table_destroy(options);
 166     }
 167 
 168     crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
 169     return rc;
 170 }
 171 
 172 /*!
 173  * \internal
 174  * \brief Determine whether to perform operations on a scratch copy of the CIB
 175  *
 176  * \param[in] op            CIB operation
 177  * \param[in] section       CIB section
 178  * \param[in] call_options  CIB call options
 179  *
 180  * \return \p true if we should make a copy of the CIB, or \p false otherwise
 181  */
 182 static bool
 183 should_copy_cib(const char *op, const char *section, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     if (pcmk_is_set(call_options, cib_dryrun)) {
 186         // cib_dryrun implies a scratch copy by definition; no side effects
 187         return true;
 188     }
 189 
 190     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
 191         /* Commit-transaction must make a copy for atomicity. We must revert to
 192          * the original CIB if the entire transaction cannot be applied
 193          * successfully.
 194          */
 195         return true;
 196     }
 197 
 198     if (pcmk_is_set(call_options, cib_transaction)) {
 199         /* If cib_transaction is set, then we're in the process of committing a
 200          * transaction. The commit-transaction request already made a scratch
 201          * copy, and we're accumulating changes in that copy.
 202          */
 203         return false;
 204     }
 205 
 206     if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) {
 207         /* Copying large CIBs accounts for a huge percentage of our CIB usage,
 208          * and this avoids some of it.
 209          *
 210          * @TODO: Is this safe? See discussion at
 211          * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
 212          */
 213         return false;
 214     }
 215 
 216     // Default behavior is to operate on a scratch copy
 217     return true;
 218 }
 219 
 220 int
 221 cib_perform_op(cib_t *cib, const char *op, uint32_t call_options,
     /* [previous][next][first][last][top][bottom][index][help] */
 222                cib__op_fn_t fn, bool is_query, const char *section,
 223                xmlNode *req, xmlNode *input, bool manage_counters,
 224                bool *config_changed, xmlNode **current_cib,
 225                xmlNode **result_cib, xmlNode **diff, xmlNode **output)
 226 {
 227     int rc = pcmk_ok;
 228     bool check_schema = true;
 229     bool make_copy = true;
 230     xmlNode *top = NULL;
 231     xmlNode *scratch = NULL;
 232     xmlNode *patchset_cib = NULL;
 233     xmlNode *local_diff = NULL;
 234 
 235     const char *user = crm_element_value(req, PCMK__XA_CIB_USER);
 236     const bool enable_acl = cib_acl_enabled(*current_cib, user);
 237     bool with_digest = false;
 238 
 239     crm_trace("Begin %s%s%s op",
 240               (pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
 241               (is_query? "read-only " : ""), op);
 242 
 243     CRM_CHECK(output != NULL, return -ENOMSG);
 244     CRM_CHECK(current_cib != NULL, return -ENOMSG);
 245     CRM_CHECK(result_cib != NULL, return -ENOMSG);
 246     CRM_CHECK(config_changed != NULL, return -ENOMSG);
 247 
 248     if(output) {
 249         *output = NULL;
 250     }
 251 
 252     *result_cib = NULL;
 253     *config_changed = false;
 254 
 255     if (fn == NULL) {
 256         return -EINVAL;
 257     }
 258 
 259     if (is_query) {
 260         xmlNode *cib_ro = *current_cib;
 261         xmlNode *cib_filtered = NULL;
 262 
 263         if (enable_acl
 264             && xml_acl_filtered_copy(user, *current_cib, *current_cib,
 265                                      &cib_filtered)) {
 266 
 267             if (cib_filtered == NULL) {
 268                 crm_debug("Pre-filtered the entire cib");
 269                 return -EACCES;
 270             }
 271             cib_ro = cib_filtered;
 272             crm_log_xml_trace(cib_ro, "filtered");
 273         }
 274 
 275         rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
 276 
 277         if(output == NULL || *output == NULL) {
 278             /* nothing */
 279 
 280         } else if(cib_filtered == *output) {
 281             cib_filtered = NULL; /* Let them have this copy */
 282 
 283         } else if (*output == *current_cib) {
 284             /* They already know not to free it */
 285 
 286         } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
 287             /* We're about to free the document of which *output is a part */
 288             *output = pcmk__xml_copy(NULL, *output);
 289 
 290         } else if ((*output)->doc == (*current_cib)->doc) {
 291             /* Give them a copy they can free */
 292             *output = pcmk__xml_copy(NULL, *output);
 293         }
 294 
 295         pcmk__xml_free(cib_filtered);
 296         return rc;
 297     }
 298 
 299     make_copy = should_copy_cib(op, section, call_options);
 300 
 301     if (!make_copy) {
 302         /* Conditional on v2 patch style */
 303 
 304         scratch = *current_cib;
 305 
 306         // Make a copy of the top-level element to store version details
 307         top = pcmk__xe_create(NULL, (const char *) scratch->name);
 308         pcmk__xe_copy_attrs(top, scratch, pcmk__xaf_none);
 309         patchset_cib = top;
 310 
 311         pcmk__xml_commit_changes(scratch->doc);
 312         pcmk__xml_doc_set_flags(scratch->doc, pcmk__xf_tracking);
 313         if (enable_acl) {
 314             pcmk__enable_acl(*current_cib, scratch, user);
 315         }
 316 
 317         rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
 318 
 319         /* If scratch points to a new object now (for example, after an erase
 320          * operation), then *current_cib should point to the same object.
 321          *
 322          * @TODO Enable tracking and ACLs and calculate changes? Change tracking
 323          * and unpacked ACLs didn't carry over to new object.
 324          */
 325         *current_cib = scratch;
 326 
 327     } else {
 328         scratch = pcmk__xml_copy(NULL, *current_cib);
 329         patchset_cib = *current_cib;
 330 
 331         pcmk__xml_doc_set_flags(scratch->doc, pcmk__xf_tracking);
 332         if (enable_acl) {
 333             pcmk__enable_acl(*current_cib, scratch, user);
 334         }
 335 
 336         rc = (*fn) (op, call_options, section, req, input, *current_cib,
 337                     &scratch, output);
 338 
 339         /* @TODO This appears to be a hack to determine whether scratch points
 340          * to a new object now, without saving the old pointer (which may be
 341          * invalid now) for comparison. Confirm this, and check more clearly.
 342          */
 343         if (!pcmk__xml_doc_all_flags_set(scratch->doc, pcmk__xf_tracking)) {
 344             crm_trace("Inferring changes after %s op", op);
 345             pcmk__xml_commit_changes(scratch->doc);
 346             if (enable_acl) {
 347                 pcmk__enable_acl(*current_cib, scratch, user);
 348             }
 349             pcmk__xml_mark_changes(*current_cib, scratch);
 350         }
 351         CRM_CHECK(*current_cib != scratch, return -EINVAL);
 352     }
 353 
 354     xml_acl_disable(scratch); /* Allow the system to make any additional changes */
 355 
 356     if (rc == pcmk_ok && scratch == NULL) {
 357         rc = -EINVAL;
 358         goto done;
 359 
 360     } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
 361         crm_trace("ACL rejected part or all of the proposed changes");
 362         rc = -EACCES;
 363         goto done;
 364 
 365     } else if (rc != pcmk_ok) {
 366         goto done;
 367     }
 368 
 369     /* If the CIB is from a file, we don't need to check that the feature set is
 370      * supported.  All we care about in that case is the schema version, which
 371      * is checked elsewhere.
 372      */
 373     if (scratch && (cib == NULL || cib->variant != cib_file)) {
 374         const char *new_version = crm_element_value(scratch, PCMK_XA_CRM_FEATURE_SET);
 375 
 376         rc = pcmk__check_feature_set(new_version);
 377         if (rc != pcmk_rc_ok) {
 378             crm_err("Discarding update with feature set '%s' greater than "
 379                     "our own '%s'", new_version, CRM_FEATURE_SET);
 380             rc = pcmk_rc2legacy(rc);
 381             goto done;
 382         }
 383     }
 384 
 385     if (patchset_cib != NULL) {
 386         int old = 0;
 387         int new = 0;
 388 
 389         crm_element_value_int(scratch, PCMK_XA_ADMIN_EPOCH, &new);
 390         crm_element_value_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old);
 391 
 392         if (old > new) {
 393             crm_err("%s went backwards: %d -> %d (Opts: %#x)",
 394                     PCMK_XA_ADMIN_EPOCH, old, new, call_options);
 395             crm_log_xml_warn(req, "Bad Op");
 396             crm_log_xml_warn(input, "Bad Data");
 397             rc = -pcmk_err_old_data;
 398 
 399         } else if (old == new) {
 400             crm_element_value_int(scratch, PCMK_XA_EPOCH, &new);
 401             crm_element_value_int(patchset_cib, PCMK_XA_EPOCH, &old);
 402             if (old > new) {
 403                 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
 404                         PCMK_XA_EPOCH, old, new, call_options);
 405                 crm_log_xml_warn(req, "Bad Op");
 406                 crm_log_xml_warn(input, "Bad Data");
 407                 rc = -pcmk_err_old_data;
 408             }
 409         }
 410     }
 411 
 412     crm_trace("Massaging CIB contents");
 413     pcmk__strip_xml_text(scratch);
 414 
 415     if (make_copy) {
 416         static time_t expires = 0;
 417         time_t tm_now = time(NULL);
 418 
 419         if (expires < tm_now) {
 420             expires = tm_now + 60;  /* Validate clients are correctly applying v2-style diffs at most once a minute */
 421             with_digest = true;
 422         }
 423     }
 424 
 425     local_diff = xml_create_patchset(0, patchset_cib, scratch,
 426                                      config_changed, manage_counters);
 427 
 428     pcmk__log_xml_changes(LOG_TRACE, scratch);
 429     pcmk__xml_commit_changes(scratch->doc);
 430 
 431     if(local_diff) {
 432         patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
 433         pcmk__log_xml_patchset(LOG_INFO, local_diff);
 434         crm_log_xml_trace(local_diff, "raw patch");
 435     }
 436 
 437     if (make_copy && (local_diff != NULL)) {
 438         // Original to compare against doesn't exist
 439         pcmk__if_tracing(
 440             {
 441                 // Validate the calculated patch set
 442                 int test_rc = pcmk_ok;
 443                 int format = 1;
 444                 xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib);
 445 
 446                 crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
 447                 test_rc = xml_apply_patchset(cib_copy, local_diff,
 448                                              manage_counters);
 449 
 450                 if (test_rc != pcmk_ok) {
 451                     save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
 452                     save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
 453                     save_xml_to_file(scratch, "PatchApply:actual", NULL);
 454                     save_xml_to_file(local_diff, "PatchApply:diff", NULL);
 455                     crm_err("v%d patchset error, patch failed to apply: %s "
 456                             "(%d)",
 457                             format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
 458                             test_rc);
 459                 }
 460                 pcmk__xml_free(cib_copy);
 461             },
 462             {}
 463         );
 464     }
 465 
 466     if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) {
 467         /* Throttle the amount of costly validation we perform due to status updates
 468          * a) we don't really care whats in the status section
 469          * b) we don't validate any of its contents at the moment anyway
 470          */
 471         check_schema = false;
 472     }
 473 
 474     /* === scratch must not be modified after this point ===
 475      * Exceptions, anything in:
 476 
 477      static filter_t filter[] = {
 478      { 0, PCMK_XA_CRM_DEBUG_ORIGIN },
 479      { 0, PCMK_XA_CIB_LAST_WRITTEN },
 480      { 0, PCMK_XA_UPDATE_ORIGIN },
 481      { 0, PCMK_XA_UPDATE_CLIENT },
 482      { 0, PCMK_XA_UPDATE_USER },
 483      };
 484      */
 485 
 486     if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
 487         const char *schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
 488 
 489         if (schema == NULL) {
 490             rc = -pcmk_err_cib_corrupt;
 491         }
 492 
 493         pcmk__xe_add_last_written(scratch);
 494         pcmk__warn_if_schema_deprecated(schema);
 495 
 496         /* Make values of origin, client, and user in scratch match
 497          * the ones in req (if the schema allows the attributes)
 498          */
 499         if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) {
 500             const char *origin = crm_element_value(req, PCMK__XA_SRC);
 501             const char *client = crm_element_value(req,
 502                                                    PCMK__XA_CIB_CLIENTNAME);
 503 
 504             if (origin != NULL) {
 505                 crm_xml_add(scratch, PCMK_XA_UPDATE_ORIGIN, origin);
 506             } else {
 507                 pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_ORIGIN);
 508             }
 509 
 510             if (client != NULL) {
 511                 crm_xml_add(scratch, PCMK_XA_UPDATE_CLIENT, user);
 512             } else {
 513                 pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_CLIENT);
 514             }
 515 
 516             if (user != NULL) {
 517                 crm_xml_add(scratch, PCMK_XA_UPDATE_USER, user);
 518             } else {
 519                 pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_USER);
 520             }
 521         }
 522     }
 523 
 524     crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
 525     if ((rc == pcmk_ok) && check_schema
 526         && !pcmk__configured_schema_validates(scratch)) {
 527         rc = -pcmk_err_schema_validation;
 528     }
 529 
 530   done:
 531 
 532     *result_cib = scratch;
 533 
 534     /* @TODO: This may not work correctly with !make_copy, since we don't
 535      * keep the original CIB.
 536      */
 537     if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
 538         && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
 539 
 540         if (*result_cib == NULL) {
 541             crm_debug("Pre-filtered the entire cib result");
 542         }
 543         pcmk__xml_free(scratch);
 544     }
 545 
 546     if(diff) {
 547         *diff = local_diff;
 548     } else {
 549         pcmk__xml_free(local_diff);
 550     }
 551 
 552     pcmk__xml_free(top);
 553     crm_trace("Done");
 554     return rc;
 555 }
 556 
 557 int
 558 cib__create_op(cib_t *cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 559                const char *section, xmlNode *data, int call_options,
 560                const char *user_name, const char *client_name,
 561                xmlNode **op_msg)
 562 {
 563     CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
 564 
 565     *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND);
 566 
 567     cib->call_id++;
 568     if (cib->call_id < 1) {
 569         cib->call_id = 1;
 570     }
 571 
 572     crm_xml_add(*op_msg, PCMK__XA_T, PCMK__VALUE_CIB);
 573     crm_xml_add(*op_msg, PCMK__XA_CIB_OP, op);
 574     crm_xml_add(*op_msg, PCMK__XA_CIB_HOST, host);
 575     crm_xml_add(*op_msg, PCMK__XA_CIB_SECTION, section);
 576     crm_xml_add(*op_msg, PCMK__XA_CIB_USER, user_name);
 577     crm_xml_add(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
 578     crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLID, cib->call_id);
 579 
 580     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
 581     crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options);
 582 
 583     if (data != NULL) {
 584         xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA);
 585 
 586         pcmk__xml_copy(wrapper, data);
 587     }
 588 
 589     return pcmk_ok;
 590 }
 591 
 592 /*!
 593  * \internal
 594  * \brief Check whether a CIB request is supported in a transaction
 595  *
 596  * \param[in] request  CIB request
 597  *
 598  * \return Standard Pacemaker return code
 599  */
 600 static int
 601 validate_transaction_request(const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 602 {
 603     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 604     const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
 605     const cib__operation_t *operation = NULL;
 606     int rc = cib__get_operation(op, &operation);
 607 
 608     if (rc != pcmk_rc_ok) {
 609         // cib__get_operation() logs error
 610         return rc;
 611     }
 612 
 613     if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
 614         crm_err("Operation %s is not supported in CIB transactions", op);
 615         return EOPNOTSUPP;
 616     }
 617 
 618     if (host != NULL) {
 619         crm_err("Operation targeting a specific node (%s) is not supported in "
 620                 "a CIB transaction",
 621                 host);
 622         return EOPNOTSUPP;
 623     }
 624     return pcmk_rc_ok;
 625 }
 626 
 627 /*!
 628  * \internal
 629  * \brief Append a CIB request to a CIB transaction
 630  *
 631  * \param[in,out] cib      CIB client whose transaction to extend
 632  * \param[in,out] request  Request to add to transaction
 633  *
 634  * \return Legacy Pacemaker return code
 635  */
 636 int
 637 cib__extend_transaction(cib_t *cib, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639     int rc = pcmk_rc_ok;
 640 
 641     pcmk__assert((cib != NULL) && (request != NULL));
 642 
 643     rc = validate_transaction_request(request);
 644 
 645     if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
 646         rc = pcmk_rc_no_transaction;
 647     }
 648 
 649     if (rc == pcmk_rc_ok) {
 650         pcmk__xml_copy(cib->transaction, request);
 651 
 652     } else {
 653         const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 654         const char *client_id = NULL;
 655 
 656         cib->cmds->client_id(cib, NULL, &client_id);
 657         crm_err("Failed to add '%s' operation to transaction for client %s: %s",
 658                 op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
 659         crm_log_xml_info(request, "failed");
 660     }
 661     return pcmk_rc2legacy(rc);
 662 }
 663 
 664 void
 665 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 666 {
 667     xmlNode *output = NULL;
 668     cib_callback_client_t *blob = NULL;
 669 
 670     if (msg != NULL) {
 671         xmlNode *wrapper = NULL;
 672 
 673         crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
 674         crm_element_value_int(msg, PCMK__XA_CIB_CALLID, &call_id);
 675         wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL);
 676         output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 677     }
 678 
 679     blob = cib__lookup_id(call_id);
 680 
 681     if (blob == NULL) {
 682         crm_trace("No callback found for call %d", call_id);
 683     }
 684 
 685     if (cib == NULL) {
 686         crm_debug("No cib object supplied");
 687     }
 688 
 689     if (rc == -pcmk_err_diff_resync) {
 690         /* This is an internal value that clients do not and should not care about */
 691         rc = pcmk_ok;
 692     }
 693 
 694     if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
 695         crm_trace("Invoking callback %s for call %d",
 696                   pcmk__s(blob->id, "without ID"), call_id);
 697         blob->callback(msg, call_id, rc, output, blob->user_data);
 698 
 699     } else if ((cib != NULL) && (rc != pcmk_ok)) {
 700         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
 701         crm_log_xml_debug(msg, "Failed CIB Update");
 702     }
 703 
 704     /* This may free user_data, so do it after the callback */
 705     if (blob) {
 706         remove_cib_op_callback(call_id, FALSE);
 707     }
 708 
 709     crm_trace("OP callback activated for %d", call_id);
 710 }
 711 
 712 void
 713 cib_native_notify(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 714 {
 715     xmlNode *msg = user_data;
 716     cib_notify_client_t *entry = data;
 717     const char *event = NULL;
 718 
 719     if (msg == NULL) {
 720         crm_warn("Skipping callback - NULL message");
 721         return;
 722     }
 723 
 724     event = crm_element_value(msg, PCMK__XA_SUBT);
 725 
 726     if (entry == NULL) {
 727         crm_warn("Skipping callback - NULL callback client");
 728         return;
 729 
 730     } else if (entry->callback == NULL) {
 731         crm_warn("Skipping callback - NULL callback");
 732         return;
 733 
 734     } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
 735         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
 736         return;
 737     }
 738 
 739     crm_trace("Invoking callback for %p/%s event...", entry, event);
 740     entry->callback(event, msg);
 741     crm_trace("Callback invoked...");
 742 }
 743 
 744 gboolean
 745 cib_read_config(GHashTable * options, xmlNode * current_cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 746 {
 747     xmlNode *config = NULL;
 748     crm_time_t *now = NULL;
 749 
 750     if (options == NULL || current_cib == NULL) {
 751         return FALSE;
 752     }
 753 
 754     now = crm_time_new(NULL);
 755 
 756     g_hash_table_remove_all(options);
 757 
 758     config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG);
 759     if (config) {
 760         pcmk_rule_input_t rule_input = {
 761             .now = now,
 762         };
 763 
 764         pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET,
 765                                   PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input,
 766                                   options, NULL);
 767     }
 768 
 769     pcmk__validate_cluster_options(options);
 770 
 771     crm_time_free(now);
 772 
 773     return TRUE;
 774 }
 775 
 776 int
 777 cib_internal_op(cib_t * cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 778                 const char *section, xmlNode * data,
 779                 xmlNode ** output_data, int call_options, const char *user_name)
 780 {
 781     int (*delegate)(cib_t *cib, const char *op, const char *host,
 782                     const char *section, xmlNode *data, xmlNode **output_data,
 783                     int call_options, const char *user_name) = NULL;
 784 
 785     if (cib == NULL) {
 786         return -EINVAL;
 787     }
 788 
 789     delegate = cib->delegate_fn;
 790     if (delegate == NULL) {
 791         return -EPROTONOSUPPORT;
 792     }
 793     if (user_name == NULL) {
 794         user_name = getenv("CIB_user");
 795     }
 796     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
 797 }
 798 
 799 /*!
 800  * \brief Apply a CIB update patch to a given CIB
 801  *
 802  * \param[in]  event   CIB update patch
 803  * \param[in]  input   CIB to patch
 804  * \param[out] output  Resulting CIB after patch
 805  * \param[in]  level   Log the patch at this log level (unless LOG_CRIT)
 806  *
 807  * \return Legacy Pacemaker return code
 808  * \note sbd calls this function
 809  */
 810 int
 811 cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
     /* [previous][next][first][last][top][bottom][index][help] */
 812                       int level)
 813 {
 814     int rc = pcmk_err_generic;
 815 
 816     xmlNode *wrapper = NULL;
 817     xmlNode *diff = NULL;
 818 
 819     pcmk__assert((event != NULL) && (input != NULL) && (output != NULL));
 820 
 821     crm_element_value_int(event, PCMK__XA_CIB_RC, &rc);
 822     wrapper = pcmk__xe_first_child(event, PCMK__XE_CIB_UPDATE_RESULT, NULL,
 823                                    NULL);
 824     diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 825 
 826     if (rc < pcmk_ok || diff == NULL) {
 827         return rc;
 828     }
 829 
 830     if (level > LOG_CRIT) {
 831         pcmk__log_xml_patchset(level, diff);
 832     }
 833 
 834     if (input != NULL) {
 835         rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
 836                               NULL);
 837 
 838         if (rc != pcmk_ok) {
 839             crm_debug("Update didn't apply: %s (%d) %p",
 840                       pcmk_strerror(rc), rc, *output);
 841 
 842             if (rc == -pcmk_err_old_data) {
 843                 crm_trace("Masking error, we already have the supplied update");
 844                 return pcmk_ok;
 845             }
 846             pcmk__xml_free(*output);
 847             *output = NULL;
 848             return rc;
 849         }
 850     }
 851     return rc;
 852 }
 853 
 854 #define log_signon_query_err(out, fmt, args...) do {    \
 855         if (out != NULL) {                              \
 856             out->err(out, fmt, ##args);                 \
 857         } else {                                        \
 858             crm_err(fmt, ##args);                       \
 859         }                                               \
 860     } while (0)
 861 
 862 int
 863 cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
     /* [previous][next][first][last][top][bottom][index][help] */
 864 {
 865     int rc = pcmk_rc_ok;
 866     cib_t *cib_conn = NULL;
 867 
 868     pcmk__assert(cib_object != NULL);
 869 
 870     if (cib == NULL) {
 871         cib_conn = cib_new();
 872     } else {
 873         if (*cib == NULL) {
 874             *cib = cib_new();
 875         }
 876         cib_conn = *cib;
 877     }
 878 
 879     if (cib_conn == NULL) {
 880         return ENOMEM;
 881     }
 882 
 883     if (cib_conn->state == cib_disconnected) {
 884         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
 885         rc = pcmk_legacy2rc(rc);
 886     }
 887 
 888     if (rc != pcmk_rc_ok) {
 889         log_signon_query_err(out, "Could not connect to the CIB: %s",
 890                              pcmk_rc_str(rc));
 891         goto done;
 892     }
 893 
 894     if (out != NULL) {
 895         out->transient(out, "Querying CIB...");
 896     }
 897     rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, cib_sync_call);
 898     rc = pcmk_legacy2rc(rc);
 899 
 900     if (rc != pcmk_rc_ok) {
 901         log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
 902     }
 903 
 904 done:
 905     if (cib == NULL) {
 906         cib__clean_up_connection(&cib_conn);
 907     }
 908 
 909     if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
 910         return pcmk_rc_no_input;
 911     }
 912     return rc;
 913 }
 914 
 915 int
 916 cib__signon_attempts(cib_t *cib, enum cib_conn_type type, int attempts)
     /* [previous][next][first][last][top][bottom][index][help] */
 917 {
 918     int rc = pcmk_rc_ok;
 919 
 920     crm_trace("Attempting connection to CIB manager (up to %d time%s)",
 921               attempts, pcmk__plural_s(attempts));
 922 
 923     for (int remaining = attempts - 1; remaining >= 0; --remaining) {
 924         rc = cib->cmds->signon(cib, crm_system_name, type);
 925 
 926         if ((rc == pcmk_rc_ok)
 927             || (remaining == 0)
 928             || ((errno != EAGAIN) && (errno != EALREADY))) {
 929             break;
 930         }
 931 
 932         // Retry after soft error (interrupted by signal, etc.)
 933         pcmk__sleep_ms((attempts - remaining) * 500);
 934         crm_debug("Re-attempting connection to CIB manager (%d attempt%s remaining)",
 935                   remaining, pcmk__plural_s(remaining));
 936     }
 937 
 938     return rc;
 939 }
 940 
 941 int
 942 cib__clean_up_connection(cib_t **cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 943 {
 944     int rc;
 945 
 946     if (*cib == NULL) {
 947         return pcmk_rc_ok;
 948     }
 949 
 950     rc = (*cib)->cmds->signoff(*cib);
 951     cib_delete(*cib);
 952     *cib = NULL;
 953     return pcmk_legacy2rc(rc);
 954 }

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