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

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