root/lib/cib/cib_ops.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib__get_operation
  2. cib_process_query
  3. update_counter
  4. cib_process_erase
  5. cib_process_upgrade
  6. cib_process_bump
  7. cib_process_replace
  8. delete_child
  9. cib_process_delete
  10. cib_process_modify
  11. add_cib_object
  12. update_results
  13. cib_process_create
  14. cib_process_diff
  15. cib__config_changed_v1
  16. cib_process_xpath

   1 /*
   2  * Copyright 2004-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <stdlib.h>
  15 #include <errno.h>
  16 #include <fcntl.h>
  17 #include <time.h>
  18 
  19 #include <sys/param.h>
  20 #include <sys/types.h>
  21 
  22 #include <glib.h>
  23 #include <libxml/tree.h>
  24 
  25 #include <crm/crm.h>
  26 #include <crm/cib/internal.h>
  27 
  28 #include <crm/common/xml.h>
  29 #include <crm/common/xml_internal.h>
  30 
  31 // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon
  32 static GHashTable *operation_table = NULL;
  33 
  34 static const cib__operation_t cib_ops[] = {
  35     {
  36         PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete,
  37         cib__op_attr_modifies|cib__op_attr_privileged
  38     },
  39     {
  40         PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch,
  41         cib__op_attr_modifies
  42         |cib__op_attr_privileged
  43         |cib__op_attr_transaction
  44     },
  45     {
  46         PCMK__CIB_REQUEST_BUMP, cib__op_bump,
  47         cib__op_attr_modifies
  48         |cib__op_attr_privileged
  49         |cib__op_attr_transaction
  50     },
  51     {
  52         PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact,
  53         cib__op_attr_modifies
  54         |cib__op_attr_privileged
  55         |cib__op_attr_replaces
  56         |cib__op_attr_writes_through
  57     },
  58     {
  59         PCMK__CIB_REQUEST_CREATE, cib__op_create,
  60         cib__op_attr_modifies
  61         |cib__op_attr_privileged
  62         |cib__op_attr_transaction
  63     },
  64     {
  65         PCMK__CIB_REQUEST_DELETE, cib__op_delete,
  66         cib__op_attr_modifies
  67         |cib__op_attr_privileged
  68         |cib__op_attr_transaction
  69     },
  70     {
  71         PCMK__CIB_REQUEST_ERASE, cib__op_erase,
  72         cib__op_attr_modifies
  73         |cib__op_attr_privileged
  74         |cib__op_attr_replaces
  75         |cib__op_attr_transaction
  76     },
  77     {
  78         PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary,
  79         cib__op_attr_privileged
  80     },
  81     {
  82         PCMK__CIB_REQUEST_MODIFY, cib__op_modify,
  83         cib__op_attr_modifies
  84         |cib__op_attr_privileged
  85         |cib__op_attr_transaction
  86     },
  87     {
  88         PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none
  89     },
  90     {
  91         CRM_OP_PING, cib__op_ping, cib__op_attr_none
  92     },
  93     {
  94         // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support
  95         PCMK__CIB_REQUEST_PRIMARY, cib__op_primary,
  96         cib__op_attr_modifies|cib__op_attr_privileged|cib__op_attr_local
  97     },
  98     {
  99         PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none
 100     },
 101     {
 102         PCMK__CIB_REQUEST_REPLACE, cib__op_replace,
 103         cib__op_attr_modifies
 104         |cib__op_attr_privileged
 105         |cib__op_attr_replaces
 106         |cib__op_attr_writes_through
 107         |cib__op_attr_transaction
 108     },
 109     {
 110         PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary,
 111         cib__op_attr_privileged|cib__op_attr_local
 112     },
 113     {
 114         PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged
 115     },
 116     {
 117         PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_all, cib__op_attr_privileged
 118     },
 119     {
 120         PCMK__CIB_REQUEST_SYNC_TO_ONE, cib__op_sync_one, cib__op_attr_privileged
 121     },
 122     {
 123         PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade,
 124         cib__op_attr_modifies
 125         |cib__op_attr_privileged
 126         |cib__op_attr_writes_through
 127         |cib__op_attr_transaction
 128     },
 129     {
 130         PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local
 131     }
 132 };
 133 
 134 /*!
 135  * \internal
 136  * \brief Get the \c cib__operation_t object for a given CIB operation name
 137  *
 138  * \param[in]  op         CIB operation name
 139  * \param[out] operation  Where to store CIB operation object
 140  *
 141  * \return Standard Pacemaker return code
 142  */
 143 int
 144 cib__get_operation(const char *op, const cib__operation_t **operation)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146     CRM_ASSERT((op != NULL) && (operation != NULL));
 147 
 148     if (operation_table == NULL) {
 149         operation_table = pcmk__strkey_table(NULL, NULL);
 150 
 151         for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
 152             const cib__operation_t *oper = &(cib_ops[lpc]);
 153 
 154             g_hash_table_insert(operation_table, (gpointer) oper->name,
 155                                 (gpointer) oper);
 156         }
 157     }
 158 
 159     *operation = g_hash_table_lookup(operation_table, op);
 160     if (*operation == NULL) {
 161         crm_err("Operation %s is invalid", op);
 162         return EINVAL;
 163     }
 164     return pcmk_rc_ok;
 165 }
 166 
 167 int
 168 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 169                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 170 {
 171     xmlNode *obj_root = NULL;
 172     int result = pcmk_ok;
 173 
 174     crm_trace("Processing %s for %s section",
 175               op, pcmk__s(section, "unspecified"));
 176 
 177     if (options & cib_xpath) {
 178         return cib_process_xpath(op, options, section, req, input,
 179                                  existing_cib, result_cib, answer);
 180     }
 181 
 182     CRM_CHECK(*answer == NULL, free_xml(*answer));
 183     *answer = NULL;
 184 
 185     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
 186         section = NULL;
 187     }
 188 
 189     obj_root = pcmk_find_cib_element(existing_cib, section);
 190 
 191     if (obj_root == NULL) {
 192         result = -ENXIO;
 193 
 194     } else if (options & cib_no_children) {
 195         xmlNode *shallow = pcmk__xe_create(*answer,
 196                                            (const char *) obj_root->name);
 197 
 198         pcmk__xe_copy_attrs(shallow, obj_root, pcmk__xaf_none);
 199         *answer = shallow;
 200 
 201     } else {
 202         *answer = obj_root;
 203     }
 204 
 205     if (result == pcmk_ok && *answer == NULL) {
 206         crm_err("Error creating query response");
 207         result = -ENOMSG;
 208     }
 209 
 210     return result;
 211 }
 212 
 213 static int
 214 update_counter(xmlNode *xml_obj, const char *field, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216     char *new_value = NULL;
 217     char *old_value = NULL;
 218     int int_value = -1;
 219 
 220     if (!reset && crm_element_value(xml_obj, field) != NULL) {
 221         old_value = crm_element_value_copy(xml_obj, field);
 222     }
 223     if (old_value != NULL) {
 224         int_value = atoi(old_value);
 225         new_value = pcmk__itoa(++int_value);
 226     } else {
 227         new_value = pcmk__str_copy("1");
 228     }
 229 
 230     crm_trace("Update %s from %s to %s",
 231               field, pcmk__s(old_value, "unset"), new_value);
 232     crm_xml_add(xml_obj, field, new_value);
 233 
 234     free(new_value);
 235     free(old_value);
 236 
 237     return pcmk_ok;
 238 }
 239 
 240 int
 241 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 242                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 243 {
 244     int result = pcmk_ok;
 245 
 246     crm_trace("Processing \"%s\" event", op);
 247 
 248     if (*result_cib != existing_cib) {
 249         free_xml(*result_cib);
 250     }
 251     *result_cib = createEmptyCib(0);
 252     pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none);
 253     update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
 254     *answer = NULL;
 255 
 256     return result;
 257 }
 258 
 259 int
 260 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 261                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 262                     xmlNode ** answer)
 263 {
 264     int rc = 0;
 265     const char *max_schema = crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX);
 266     const char *original_schema = NULL;
 267     const char *new_schema = NULL;
 268 
 269     *answer = NULL;
 270     crm_trace("Processing \"%s\" event with max=%s", op, max_schema);
 271 
 272     original_schema = crm_element_value(existing_cib, PCMK_XA_VALIDATE_WITH);
 273     rc = pcmk__update_schema(result_cib, max_schema, true,
 274                              !pcmk_is_set(options, cib_verbose));
 275     rc = pcmk_rc2legacy(rc);
 276     new_schema = crm_element_value(*result_cib, PCMK_XA_VALIDATE_WITH);
 277 
 278     if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
 279         update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
 280         update_counter(*result_cib, PCMK_XA_EPOCH, true);
 281         update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true);
 282         return pcmk_ok;
 283     }
 284 
 285     return rc;
 286 }
 287 
 288 int
 289 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 290                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 291 {
 292     int result = pcmk_ok;
 293 
 294     crm_trace("Processing %s for epoch='%s'", op,
 295               pcmk__s(crm_element_value(existing_cib, PCMK_XA_EPOCH), ""));
 296 
 297     *answer = NULL;
 298     update_counter(*result_cib, PCMK_XA_EPOCH, false);
 299 
 300     return result;
 301 }
 302 
 303 int
 304 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 305                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 306                     xmlNode ** answer)
 307 {
 308     int result = pcmk_ok;
 309 
 310     crm_trace("Processing %s for %s section",
 311               op, pcmk__s(section, "unspecified"));
 312 
 313     if (options & cib_xpath) {
 314         return cib_process_xpath(op, options, section, req, input,
 315                                  existing_cib, result_cib, answer);
 316     }
 317 
 318     *answer = NULL;
 319 
 320     if (input == NULL) {
 321         return -EINVAL;
 322     }
 323 
 324     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
 325         section = NULL;
 326 
 327     } else if (pcmk__xe_is(input, section)) {
 328         section = NULL;
 329     }
 330 
 331     if (pcmk__xe_is(input, PCMK_XE_CIB)) {
 332         int updates = 0;
 333         int epoch = 0;
 334         int admin_epoch = 0;
 335 
 336         int replace_updates = 0;
 337         int replace_epoch = 0;
 338         int replace_admin_epoch = 0;
 339 
 340         const char *reason = NULL;
 341         const char *peer = crm_element_value(req, PCMK__XA_SRC);
 342         const char *digest = crm_element_value(req, PCMK__XA_DIGEST);
 343 
 344         if (digest) {
 345             const char *version = crm_element_value(req,
 346                                                     PCMK_XA_CRM_FEATURE_SET);
 347             char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
 348                                                                  version ? version :
 349                                                                  CRM_FEATURE_SET);
 350 
 351             if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
 352                 crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
 353                         digest_verify, digest);
 354                 reason = "digest mismatch";
 355 
 356             } else {
 357                 crm_info("Digest matched on replace from %s: %s", peer, digest);
 358             }
 359             free(digest_verify);
 360 
 361         } else {
 362             crm_trace("No digest to verify");
 363         }
 364 
 365         cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
 366         cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
 367 
 368         if (replace_admin_epoch < admin_epoch) {
 369             reason = PCMK_XA_ADMIN_EPOCH;
 370 
 371         } else if (replace_admin_epoch > admin_epoch) {
 372             /* no more checks */
 373 
 374         } else if (replace_epoch < epoch) {
 375             reason = PCMK_XA_EPOCH;
 376 
 377         } else if (replace_epoch > epoch) {
 378             /* no more checks */
 379 
 380         } else if (replace_updates < updates) {
 381             reason = PCMK_XA_NUM_UPDATES;
 382         }
 383 
 384         if (reason != NULL) {
 385             crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
 386                      " current %s is greater than the replacement",
 387                      replace_admin_epoch, replace_epoch,
 388                      replace_updates, peer, admin_epoch, epoch, updates, reason);
 389             result = -pcmk_err_old_data;
 390         } else {
 391             crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
 392                      admin_epoch, epoch, updates,
 393                      replace_admin_epoch, replace_epoch, replace_updates, peer);
 394         }
 395 
 396         if (*result_cib != existing_cib) {
 397             free_xml(*result_cib);
 398         }
 399         *result_cib = pcmk__xml_copy(NULL, input);
 400 
 401     } else {
 402         xmlNode *obj_root = NULL;
 403 
 404         obj_root = pcmk_find_cib_element(*result_cib, section);
 405         result = pcmk__xe_replace_match(obj_root, input);
 406         result = pcmk_rc2legacy(result);
 407         if (result != pcmk_ok) {
 408             crm_trace("No matching object to replace");
 409         }
 410     }
 411 
 412     return result;
 413 }
 414 
 415 static int
 416 delete_child(xmlNode *child, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 {
 418     xmlNode *obj_root = userdata;
 419 
 420     if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) {
 421         crm_trace("No matching object to delete: %s=%s",
 422                   child->name, pcmk__xe_id(child));
 423     }
 424 
 425     return pcmk_rc_ok;
 426 }
 427 
 428 int
 429 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 430                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 431 {
 432     xmlNode *obj_root = NULL;
 433 
 434     crm_trace("Processing \"%s\" event", op);
 435 
 436     if (options & cib_xpath) {
 437         return cib_process_xpath(op, options, section, req, input,
 438                                  existing_cib, result_cib, answer);
 439     }
 440 
 441     if (input == NULL) {
 442         crm_err("Cannot perform modification with no data");
 443         return -EINVAL;
 444     }
 445 
 446     obj_root = pcmk_find_cib_element(*result_cib, section);
 447     if (pcmk__xe_is(input, section)) {
 448         pcmk__xe_foreach_child(input, NULL, delete_child, obj_root);
 449     } else {
 450         delete_child(input, obj_root);
 451     }
 452 
 453     return pcmk_ok;
 454 }
 455 
 456 int
 457 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 458                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 459 {
 460     xmlNode *obj_root = NULL;
 461     uint32_t flags = pcmk__xaf_none;
 462 
 463     crm_trace("Processing \"%s\" event", op);
 464 
 465     if (options & cib_xpath) {
 466         return cib_process_xpath(op, options, section, req, input,
 467                                  existing_cib, result_cib, answer);
 468     }
 469 
 470     if (input == NULL) {
 471         crm_err("Cannot perform modification with no data");
 472         return -EINVAL;
 473     }
 474 
 475     obj_root = pcmk_find_cib_element(*result_cib, section);
 476     if (obj_root == NULL) {
 477         xmlNode *tmp_section = NULL;
 478         const char *path = pcmk_cib_parent_name_for(section);
 479 
 480         if (path == NULL) {
 481             return -EINVAL;
 482         }
 483 
 484         tmp_section = pcmk__xe_create(NULL, section);
 485         cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
 486                           NULL, result_cib, answer);
 487         free_xml(tmp_section);
 488 
 489         obj_root = pcmk_find_cib_element(*result_cib, section);
 490     }
 491 
 492     CRM_CHECK(obj_root != NULL, return -EINVAL);
 493 
 494     if (pcmk_is_set(options, cib_score_update)) {
 495         flags |= pcmk__xaf_score_update;
 496     }
 497 
 498     if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) {
 499         if (options & cib_can_create) {
 500             pcmk__xml_copy(obj_root, input);
 501         } else {
 502             return -ENXIO;
 503         }
 504     }
 505 
 506     // @COMPAT cib_mixed_update is deprecated as of 2.1.7
 507     if (pcmk_is_set(options, cib_mixed_update)) {
 508         int max = 0, lpc;
 509         xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
 510 
 511         if (xpathObj) {
 512             max = numXpathResults(xpathObj);
 513             crm_log_xml_trace(*result_cib, "Mixed result");
 514         }
 515 
 516         for (lpc = 0; lpc < max; lpc++) {
 517             xmlNode *match = getXpathResult(xpathObj, lpc);
 518             xmlChar *match_path = xmlGetNodePath(match);
 519 
 520             crm_debug("Destroying %s", match_path);
 521             free(match_path);
 522             free_xml(match);
 523         }
 524 
 525         freeXpathObject(xpathObj);
 526     }
 527     return pcmk_ok;
 528 }
 529 
 530 static int
 531 add_cib_object(xmlNode * parent, xmlNode * new_obj)
     /* [previous][next][first][last][top][bottom][index][help] */
 532 {
 533     const char *object_name = NULL;
 534     const char *object_id = NULL;
 535 
 536     if ((parent == NULL) || (new_obj == NULL)) {
 537         return -EINVAL;
 538     }
 539 
 540     object_name = (const char *) new_obj->name;
 541     if (object_name == NULL) {
 542         return -EINVAL;
 543     }
 544 
 545     object_id = pcmk__xe_id(new_obj);
 546     if (pcmk__xe_first_child(parent, object_name,
 547                              ((object_id != NULL)? PCMK_XA_ID : NULL),
 548                              object_id)) {
 549         return -EEXIST;
 550     }
 551 
 552     if (object_id != NULL) {
 553         crm_trace("Processing creation of <%s " PCMK_XA_ID "='%s'>",
 554                   object_name, object_id);
 555     } else {
 556         crm_trace("Processing creation of <%s>", object_name);
 557     }
 558 
 559     /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use
 560      * case, PCMK__XA_REPLACE has special meaning and should not be included in
 561      * the newly created object until we can break behavioral backward
 562      * compatibility.
 563      *
 564      * At a compatibility break, drop this and drop the definition of
 565      * PCMK__XA_REPLACE. Treat it like any other attribute.
 566      */
 567     pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb,
 568                            (void *) PCMK__XA_REPLACE);
 569 
 570     pcmk__xml_copy(parent, new_obj);
 571     return pcmk_ok;
 572 }
 573 
 574 static bool
 575 update_results(xmlNode *failed, xmlNode *target, const char *operation,
     /* [previous][next][first][last][top][bottom][index][help] */
 576                int return_code)
 577 {
 578     xmlNode *xml_node = NULL;
 579     bool was_error = false;
 580     const char *error_msg = NULL;
 581 
 582     if (return_code != pcmk_ok) {
 583         error_msg = pcmk_strerror(return_code);
 584 
 585         was_error = true;
 586         xml_node = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE);
 587         pcmk__xml_copy(xml_node, target);
 588 
 589         crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(target));
 590         crm_xml_add(xml_node, PCMK_XA_OBJECT_TYPE, (const char *) target->name);
 591         crm_xml_add(xml_node, PCMK_XA_OPERATION, operation);
 592         crm_xml_add(xml_node, PCMK_XA_REASON, error_msg);
 593 
 594         crm_warn("Action %s failed: %s (cde=%d)",
 595                  operation, error_msg, return_code);
 596     }
 597 
 598     return was_error;
 599 }
 600 
 601 int
 602 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 603                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 604 {
 605     xmlNode *failed = NULL;
 606     int result = pcmk_ok;
 607     xmlNode *update_section = NULL;
 608 
 609     crm_trace("Processing %s for %s section",
 610               op, pcmk__s(section, "unspecified"));
 611     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
 612         section = NULL;
 613 
 614     } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) {
 615         section = NULL;
 616 
 617     } else if (pcmk__xe_is(input, PCMK_XE_CIB)) {
 618         section = NULL;
 619     }
 620 
 621     CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
 622 
 623     if (input == NULL) {
 624         crm_err("Cannot perform modification with no data");
 625         return -EINVAL;
 626     }
 627 
 628     if (section == NULL) {
 629         return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
 630                                   answer);
 631     }
 632 
 633     // @COMPAT Deprecated since 2.1.8
 634     failed = pcmk__xe_create(NULL, PCMK__XE_FAILED);
 635 
 636     update_section = pcmk_find_cib_element(*result_cib, section);
 637     if (pcmk__xe_is(input, section)) {
 638         xmlNode *a_child = NULL;
 639 
 640         for (a_child = pcmk__xml_first_child(input); a_child != NULL;
 641              a_child = pcmk__xml_next(a_child)) {
 642             result = add_cib_object(update_section, a_child);
 643             if (update_results(failed, a_child, op, result)) {
 644                 break;
 645             }
 646         }
 647 
 648     } else {
 649         result = add_cib_object(update_section, input);
 650         update_results(failed, input, op, result);
 651     }
 652 
 653     if ((result == pcmk_ok) && (failed->children != NULL)) {
 654         result = -EINVAL;
 655     }
 656 
 657     if (result != pcmk_ok) {
 658         crm_log_xml_err(failed, "CIB Update failures");
 659         *answer = failed;
 660 
 661     } else {
 662         free_xml(failed);
 663     }
 664 
 665     return result;
 666 }
 667 
 668 int
 669 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 670                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 671 {
 672     const char *originator = NULL;
 673 
 674     if (req != NULL) {
 675         originator = crm_element_value(req, PCMK__XA_SRC);
 676     }
 677 
 678     crm_trace("Processing \"%s\" event from %s%s",
 679               op, originator,
 680               (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
 681 
 682     if (*result_cib != existing_cib) {
 683         free_xml(*result_cib);
 684     }
 685     *result_cib = pcmk__xml_copy(NULL, existing_cib);
 686 
 687     return xml_apply_patchset(*result_cib, input, TRUE);
 688 }
 689 
 690 // @COMPAT: v1-only
 691 bool
 692 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
     /* [previous][next][first][last][top][bottom][index][help] */
 693 {
 694     int lpc = 0, max = 0;
 695     bool config_changes = false;
 696     xmlXPathObject *xpathObj = NULL;
 697     int format = 1;
 698 
 699     CRM_ASSERT(diff != NULL);
 700 
 701     if (*diff == NULL && last != NULL && next != NULL) {
 702         *diff = pcmk__diff_v1_xml_object(last, next, false);
 703     }
 704 
 705     if (*diff == NULL) {
 706         goto done;
 707     }
 708 
 709     crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
 710     CRM_LOG_ASSERT(format == 1);
 711 
 712     xpathObj = xpath_search(*diff, "//" PCMK_XE_CONFIGURATION);
 713     if (numXpathResults(xpathObj) > 0) {
 714         config_changes = true;
 715         goto done;
 716     }
 717     freeXpathObject(xpathObj);
 718 
 719     /*
 720      * Do not check PCMK__XE_DIFF_ADDED "//" PCMK_XE_CIB
 721      * This always contains every field and would produce a false positive
 722      * every time if the checked value existed
 723      */
 724     xpathObj = xpath_search(*diff, "//" PCMK__XE_DIFF_REMOVED "//" PCMK_XE_CIB);
 725     max = numXpathResults(xpathObj);
 726 
 727     for (lpc = 0; lpc < max; lpc++) {
 728         xmlNode *top = getXpathResult(xpathObj, lpc);
 729 
 730         if (crm_element_value(top, PCMK_XA_EPOCH) != NULL) {
 731             config_changes = true;
 732             goto done;
 733         }
 734         if (crm_element_value(top, PCMK_XA_ADMIN_EPOCH) != NULL) {
 735             config_changes = true;
 736             goto done;
 737         }
 738 
 739         if (crm_element_value(top, PCMK_XA_VALIDATE_WITH) != NULL) {
 740             config_changes = true;
 741             goto done;
 742         }
 743         if (crm_element_value(top, PCMK_XA_CRM_FEATURE_SET) != NULL) {
 744             config_changes = true;
 745             goto done;
 746         }
 747         if (crm_element_value(top, PCMK_XA_REMOTE_CLEAR_PORT) != NULL) {
 748             config_changes = true;
 749             goto done;
 750         }
 751         if (crm_element_value(top, PCMK_XA_REMOTE_TLS_PORT) != NULL) {
 752             config_changes = true;
 753             goto done;
 754         }
 755     }
 756 
 757   done:
 758     freeXpathObject(xpathObj);
 759     return config_changes;
 760 }
 761 
 762 int
 763 cib_process_xpath(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 764                   const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
 765                   xmlNode **result_cib, xmlNode **answer)
 766 {
 767     int lpc = 0;
 768     int max = 0;
 769     int rc = pcmk_ok;
 770     bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
 771 
 772     xmlXPathObjectPtr xpathObj = NULL;
 773 
 774     crm_trace("Processing \"%s\" event", op);
 775 
 776     if (is_query) {
 777         xpathObj = xpath_search(existing_cib, section);
 778     } else {
 779         xpathObj = xpath_search(*result_cib, section);
 780     }
 781 
 782     max = numXpathResults(xpathObj);
 783 
 784     if ((max < 1)
 785         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 786         crm_debug("%s was already removed", section);
 787 
 788     } else if (max < 1) {
 789         crm_debug("%s: %s does not exist", op, section);
 790         rc = -ENXIO;
 791 
 792     } else if (is_query) {
 793         if (max > 1) {
 794             *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
 795         }
 796     }
 797 
 798     if (pcmk_is_set(options, cib_multiple)
 799         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 800         dedupXpathResults(xpathObj);
 801     }
 802 
 803     for (lpc = 0; lpc < max; lpc++) {
 804         xmlChar *path = NULL;
 805         xmlNode *match = getXpathResult(xpathObj, lpc);
 806 
 807         if (match == NULL) {
 808             continue;
 809         }
 810 
 811         path = xmlGetNodePath(match);
 812         crm_debug("Processing %s op for %s with %s", op, section, path);
 813         free(path);
 814 
 815         if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 816             if (match == *result_cib) {
 817                 /* Attempting to delete the whole "/cib" */
 818                 crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
 819                 rc = -EINVAL;
 820                 break;
 821             }
 822 
 823             free_xml(match);
 824             if ((options & cib_multiple) == 0) {
 825                 break;
 826             }
 827 
 828         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
 829             uint32_t flags = pcmk__xaf_none;
 830 
 831             if (pcmk_is_set(options, cib_score_update)) {
 832                 flags |= pcmk__xaf_score_update;
 833             }
 834 
 835             if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) {
 836                 rc = -ENXIO;
 837             } else if ((options & cib_multiple) == 0) {
 838                 break;
 839             }
 840 
 841         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
 842             pcmk__xml_copy(match, input);
 843             break;
 844 
 845         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
 846 
 847             if (options & cib_no_children) {
 848                 xmlNode *shallow = pcmk__xe_create(*answer,
 849                                                    (const char *) match->name);
 850 
 851                 pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none);
 852 
 853                 if (*answer == NULL) {
 854                     *answer = shallow;
 855                 }
 856 
 857             } else if (options & cib_xpath_address) {
 858                 char *path = NULL;
 859                 xmlNode *parent = match;
 860 
 861                 while (parent && parent->type == XML_ELEMENT_NODE) {
 862                     const char *id = crm_element_value(parent, PCMK_XA_ID);
 863                     char *new_path = NULL;
 864 
 865                     if (id) {
 866                         new_path = crm_strdup_printf("/%s[@" PCMK_XA_ID "='%s']"
 867                                                      "%s",
 868                                                      parent->name, id,
 869                                                      pcmk__s(path, ""));
 870                     } else {
 871                         new_path = crm_strdup_printf("/%s%s", parent->name,
 872                                                      pcmk__s(path, ""));
 873                     }
 874                     free(path);
 875                     path = new_path;
 876                     parent = parent->parent;
 877                 }
 878                 crm_trace("Got: %s", path);
 879 
 880                 if (*answer == NULL) {
 881                     *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
 882                 }
 883                 parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH);
 884                 crm_xml_add(parent, PCMK_XA_ID, path);
 885                 free(path);
 886 
 887             } else if (*answer) {
 888                 pcmk__xml_copy(*answer, match);
 889 
 890             } else {
 891                 *answer = match;
 892             }
 893 
 894         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
 895                                 pcmk__str_none)) {
 896             xmlNode *parent = match->parent;
 897 
 898             free_xml(match);
 899             pcmk__xml_copy(parent, input);
 900 
 901             if ((options & cib_multiple) == 0) {
 902                 break;
 903             }
 904         }
 905     }
 906 
 907     freeXpathObject(xpathObj);
 908     return rc;
 909 }

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