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

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