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_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     pcmk__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, pcmk__xml_free(*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         pcmk__xml_free(*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             char *digest_verify = pcmk__digest_xml(input, true);
 346 
 347             if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
 348                 crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
 349                         digest_verify, digest);
 350                 reason = "digest mismatch";
 351 
 352             } else {
 353                 crm_info("Digest matched on replace from %s: %s", peer, digest);
 354             }
 355             free(digest_verify);
 356 
 357         } else {
 358             crm_trace("No digest to verify");
 359         }
 360 
 361         cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
 362         cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
 363 
 364         if (replace_admin_epoch < admin_epoch) {
 365             reason = PCMK_XA_ADMIN_EPOCH;
 366 
 367         } else if (replace_admin_epoch > admin_epoch) {
 368             /* no more checks */
 369 
 370         } else if (replace_epoch < epoch) {
 371             reason = PCMK_XA_EPOCH;
 372 
 373         } else if (replace_epoch > epoch) {
 374             /* no more checks */
 375 
 376         } else if (replace_updates < updates) {
 377             reason = PCMK_XA_NUM_UPDATES;
 378         }
 379 
 380         if (reason != NULL) {
 381             crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
 382                      " current %s is greater than the replacement",
 383                      replace_admin_epoch, replace_epoch,
 384                      replace_updates, peer, admin_epoch, epoch, updates, reason);
 385             result = -pcmk_err_old_data;
 386         } else {
 387             crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
 388                      admin_epoch, epoch, updates,
 389                      replace_admin_epoch, replace_epoch, replace_updates, peer);
 390         }
 391 
 392         if (*result_cib != existing_cib) {
 393             pcmk__xml_free(*result_cib);
 394         }
 395         *result_cib = pcmk__xml_copy(NULL, input);
 396 
 397     } else {
 398         xmlNode *obj_root = NULL;
 399 
 400         obj_root = pcmk_find_cib_element(*result_cib, section);
 401         result = pcmk__xe_replace_match(obj_root, input);
 402         result = pcmk_rc2legacy(result);
 403         if (result != pcmk_ok) {
 404             crm_trace("No matching object to replace");
 405         }
 406     }
 407 
 408     return result;
 409 }
 410 
 411 static int
 412 delete_child(xmlNode *child, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 413 {
 414     xmlNode *obj_root = userdata;
 415 
 416     if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) {
 417         crm_trace("No matching object to delete: %s=%s",
 418                   child->name, pcmk__xe_id(child));
 419     }
 420 
 421     return pcmk_rc_ok;
 422 }
 423 
 424 int
 425 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 426                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 427 {
 428     xmlNode *obj_root = NULL;
 429 
 430     crm_trace("Processing \"%s\" event", op);
 431 
 432     if (options & cib_xpath) {
 433         return cib_process_xpath(op, options, section, req, input,
 434                                  existing_cib, result_cib, answer);
 435     }
 436 
 437     if (input == NULL) {
 438         crm_err("Cannot perform modification with no data");
 439         return -EINVAL;
 440     }
 441 
 442     obj_root = pcmk_find_cib_element(*result_cib, section);
 443     if (pcmk__xe_is(input, section)) {
 444         pcmk__xe_foreach_child(input, NULL, delete_child, obj_root);
 445     } else {
 446         delete_child(input, obj_root);
 447     }
 448 
 449     return pcmk_ok;
 450 }
 451 
 452 int
 453 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 454                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 455 {
 456     xmlNode *obj_root = NULL;
 457     uint32_t flags = pcmk__xaf_none;
 458 
 459     crm_trace("Processing \"%s\" event", op);
 460 
 461     if (options & cib_xpath) {
 462         return cib_process_xpath(op, options, section, req, input,
 463                                  existing_cib, result_cib, answer);
 464     }
 465 
 466     if (input == NULL) {
 467         crm_err("Cannot perform modification with no data");
 468         return -EINVAL;
 469     }
 470 
 471     obj_root = pcmk_find_cib_element(*result_cib, section);
 472     if (obj_root == NULL) {
 473         xmlNode *tmp_section = NULL;
 474         const char *path = pcmk_cib_parent_name_for(section);
 475 
 476         if (path == NULL) {
 477             return -EINVAL;
 478         }
 479 
 480         tmp_section = pcmk__xe_create(NULL, section);
 481         cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
 482                           NULL, result_cib, answer);
 483         pcmk__xml_free(tmp_section);
 484 
 485         obj_root = pcmk_find_cib_element(*result_cib, section);
 486     }
 487 
 488     CRM_CHECK(obj_root != NULL, return -EINVAL);
 489 
 490     if (pcmk_is_set(options, cib_score_update)) {
 491         flags |= pcmk__xaf_score_update;
 492     }
 493 
 494     if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) {
 495         if (options & cib_can_create) {
 496             pcmk__xml_copy(obj_root, input);
 497         } else {
 498             return -ENXIO;
 499         }
 500     }
 501 
 502     return pcmk_ok;
 503 }
 504 
 505 static int
 506 add_cib_object(xmlNode * parent, xmlNode * new_obj)
     /* [previous][next][first][last][top][bottom][index][help] */
 507 {
 508     const char *object_name = NULL;
 509     const char *object_id = NULL;
 510 
 511     if ((parent == NULL) || (new_obj == NULL)) {
 512         return -EINVAL;
 513     }
 514 
 515     object_name = (const char *) new_obj->name;
 516     if (object_name == NULL) {
 517         return -EINVAL;
 518     }
 519 
 520     object_id = pcmk__xe_id(new_obj);
 521     if (pcmk__xe_first_child(parent, object_name,
 522                              ((object_id != NULL)? PCMK_XA_ID : NULL),
 523                              object_id)) {
 524         return -EEXIST;
 525     }
 526 
 527     if (object_id != NULL) {
 528         crm_trace("Processing creation of <%s " PCMK_XA_ID "='%s'>",
 529                   object_name, object_id);
 530     } else {
 531         crm_trace("Processing creation of <%s>", object_name);
 532     }
 533 
 534     /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use
 535      * case, PCMK__XA_REPLACE has special meaning and should not be included in
 536      * the newly created object until we can break behavioral backward
 537      * compatibility.
 538      *
 539      * At a compatibility break, drop this and drop the definition of
 540      * PCMK__XA_REPLACE. Treat it like any other attribute.
 541      */
 542     pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb,
 543                            (void *) PCMK__XA_REPLACE);
 544 
 545     pcmk__xml_copy(parent, new_obj);
 546     return pcmk_ok;
 547 }
 548 
 549 static bool
 550 update_results(xmlNode *failed, xmlNode *target, const char *operation,
     /* [previous][next][first][last][top][bottom][index][help] */
 551                int return_code)
 552 {
 553     xmlNode *xml_node = NULL;
 554     bool was_error = false;
 555     const char *error_msg = NULL;
 556 
 557     if (return_code != pcmk_ok) {
 558         error_msg = pcmk_strerror(return_code);
 559 
 560         was_error = true;
 561         xml_node = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE);
 562         pcmk__xml_copy(xml_node, target);
 563 
 564         crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(target));
 565         crm_xml_add(xml_node, PCMK_XA_OBJECT_TYPE, (const char *) target->name);
 566         crm_xml_add(xml_node, PCMK_XA_OPERATION, operation);
 567         crm_xml_add(xml_node, PCMK_XA_REASON, error_msg);
 568 
 569         crm_warn("Action %s failed: %s (cde=%d)",
 570                  operation, error_msg, return_code);
 571     }
 572 
 573     return was_error;
 574 }
 575 
 576 int
 577 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 578                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 579 {
 580     xmlNode *failed = NULL;
 581     int result = pcmk_ok;
 582     xmlNode *update_section = NULL;
 583 
 584     crm_trace("Processing %s for %s section",
 585               op, pcmk__s(section, "unspecified"));
 586     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
 587         section = NULL;
 588 
 589     } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) {
 590         section = NULL;
 591 
 592     } else if (pcmk__xe_is(input, PCMK_XE_CIB)) {
 593         section = NULL;
 594     }
 595 
 596     CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
 597 
 598     if (input == NULL) {
 599         crm_err("Cannot perform modification with no data");
 600         return -EINVAL;
 601     }
 602 
 603     if (section == NULL) {
 604         return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
 605                                   answer);
 606     }
 607 
 608     // @COMPAT Deprecated since 2.1.8
 609     failed = pcmk__xe_create(NULL, PCMK__XE_FAILED);
 610 
 611     update_section = pcmk_find_cib_element(*result_cib, section);
 612     if (pcmk__xe_is(input, section)) {
 613         xmlNode *a_child = NULL;
 614 
 615         for (a_child = pcmk__xml_first_child(input); a_child != NULL;
 616              a_child = pcmk__xml_next(a_child)) {
 617             result = add_cib_object(update_section, a_child);
 618             if (update_results(failed, a_child, op, result)) {
 619                 break;
 620             }
 621         }
 622 
 623     } else {
 624         result = add_cib_object(update_section, input);
 625         update_results(failed, input, op, result);
 626     }
 627 
 628     if ((result == pcmk_ok) && (failed->children != NULL)) {
 629         result = -EINVAL;
 630     }
 631 
 632     if (result != pcmk_ok) {
 633         crm_log_xml_err(failed, "CIB Update failures");
 634         *answer = failed;
 635 
 636     } else {
 637         pcmk__xml_free(failed);
 638     }
 639 
 640     return result;
 641 }
 642 
 643 int
 644 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 645                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 646 {
 647     const char *originator = NULL;
 648 
 649     if (req != NULL) {
 650         originator = crm_element_value(req, PCMK__XA_SRC);
 651     }
 652 
 653     crm_trace("Processing \"%s\" event from %s%s",
 654               op, originator,
 655               (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
 656 
 657     if (*result_cib != existing_cib) {
 658         pcmk__xml_free(*result_cib);
 659     }
 660     *result_cib = pcmk__xml_copy(NULL, existing_cib);
 661 
 662     return xml_apply_patchset(*result_cib, input, TRUE);
 663 }
 664 
 665 int
 666 cib_process_xpath(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 667                   const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
 668                   xmlNode **result_cib, xmlNode **answer)
 669 {
 670     int lpc = 0;
 671     int max = 0;
 672     int rc = pcmk_ok;
 673     bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
 674 
 675     xmlXPathObjectPtr xpathObj = NULL;
 676 
 677     crm_trace("Processing \"%s\" event", op);
 678 
 679     if (is_query) {
 680         xpathObj = xpath_search(existing_cib, section);
 681     } else {
 682         xpathObj = xpath_search(*result_cib, section);
 683     }
 684 
 685     max = numXpathResults(xpathObj);
 686 
 687     if ((max < 1)
 688         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 689         crm_debug("%s was already removed", section);
 690 
 691     } else if (max < 1) {
 692         crm_debug("%s: %s does not exist", op, section);
 693         rc = -ENXIO;
 694 
 695     } else if (is_query) {
 696         if (max > 1) {
 697             *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
 698         }
 699     }
 700 
 701     if (pcmk_is_set(options, cib_multiple)
 702         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 703         dedupXpathResults(xpathObj);
 704     }
 705 
 706     for (lpc = 0; lpc < max; lpc++) {
 707         xmlChar *path = NULL;
 708         xmlNode *match = getXpathResult(xpathObj, lpc);
 709 
 710         if (match == NULL) {
 711             continue;
 712         }
 713 
 714         path = xmlGetNodePath(match);
 715         crm_debug("Processing %s op for %s with %s", op, section, path);
 716         free(path);
 717 
 718         if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 719             if (match == *result_cib) {
 720                 /* Attempting to delete the whole "/cib" */
 721                 crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
 722                 rc = -EINVAL;
 723                 break;
 724             }
 725 
 726             pcmk__xml_free(match);
 727             if ((options & cib_multiple) == 0) {
 728                 break;
 729             }
 730 
 731         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
 732             uint32_t flags = pcmk__xaf_none;
 733 
 734             if (pcmk_is_set(options, cib_score_update)) {
 735                 flags |= pcmk__xaf_score_update;
 736             }
 737 
 738             if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) {
 739                 rc = -ENXIO;
 740             } else if ((options & cib_multiple) == 0) {
 741                 break;
 742             }
 743 
 744         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
 745             pcmk__xml_copy(match, input);
 746             break;
 747 
 748         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
 749 
 750             if (options & cib_no_children) {
 751                 xmlNode *shallow = pcmk__xe_create(*answer,
 752                                                    (const char *) match->name);
 753 
 754                 pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none);
 755 
 756                 if (*answer == NULL) {
 757                     *answer = shallow;
 758                 }
 759 
 760             } else if (options & cib_xpath_address) {
 761                 char *path = NULL;
 762                 xmlNode *parent = match;
 763 
 764                 while (parent && parent->type == XML_ELEMENT_NODE) {
 765                     const char *id = crm_element_value(parent, PCMK_XA_ID);
 766                     char *new_path = NULL;
 767 
 768                     if (id) {
 769                         new_path = crm_strdup_printf("/%s[@" PCMK_XA_ID "='%s']"
 770                                                      "%s",
 771                                                      parent->name, id,
 772                                                      pcmk__s(path, ""));
 773                     } else {
 774                         new_path = crm_strdup_printf("/%s%s", parent->name,
 775                                                      pcmk__s(path, ""));
 776                     }
 777                     free(path);
 778                     path = new_path;
 779                     parent = parent->parent;
 780                 }
 781                 crm_trace("Got: %s", path);
 782 
 783                 if (*answer == NULL) {
 784                     *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
 785                 }
 786                 parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH);
 787                 crm_xml_add(parent, PCMK_XA_ID, path);
 788                 free(path);
 789 
 790             } else if (*answer) {
 791                 pcmk__xml_copy(*answer, match);
 792 
 793             } else {
 794                 *answer = match;
 795             }
 796 
 797         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
 798                                 pcmk__str_none)) {
 799             xmlNode *parent = match->parent;
 800 
 801             pcmk__xml_free(match);
 802             pcmk__xml_copy(parent, input);
 803 
 804             if ((options & cib_multiple) == 0) {
 805                 break;
 806             }
 807         }
 808     }
 809 
 810     freeXpathObject(xpathObj);
 811     return rc;
 812 }

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