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. cib_process_delete
  9. cib_process_modify
  10. update_cib_object
  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-2023 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 #include <crm/msg_xml.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 
 132 /*!
 133  * \internal
 134  * \brief Get the \c cib__operation_t object for a given CIB operation name
 135  *
 136  * \param[in]  op         CIB operation name
 137  * \param[out] operation  Where to store CIB operation object
 138  *
 139  * \return Standard Pacemaker return code
 140  */
 141 int
 142 cib__get_operation(const char *op, const cib__operation_t **operation)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     CRM_ASSERT((op != NULL) && (operation != NULL));
 145 
 146     if (operation_table == NULL) {
 147         operation_table = pcmk__strkey_table(NULL, NULL);
 148 
 149         for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
 150             const cib__operation_t *oper = &(cib_ops[lpc]);
 151 
 152             g_hash_table_insert(operation_table, (gpointer) oper->name,
 153                                 (gpointer) oper);
 154         }
 155     }
 156 
 157     *operation = g_hash_table_lookup(operation_table, op);
 158     if (*operation == NULL) {
 159         crm_err("Operation %s is invalid", op);
 160         return EINVAL;
 161     }
 162     return pcmk_rc_ok;
 163 }
 164 
 165 int
 166 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 167                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 168 {
 169     xmlNode *obj_root = NULL;
 170     int result = pcmk_ok;
 171 
 172     crm_trace("Processing %s for %s section",
 173               op, pcmk__s(section, "unspecified"));
 174 
 175     if (options & cib_xpath) {
 176         return cib_process_xpath(op, options, section, req, input,
 177                                  existing_cib, result_cib, answer);
 178     }
 179 
 180     CRM_CHECK(*answer == NULL, free_xml(*answer));
 181     *answer = NULL;
 182 
 183     if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
 184         section = NULL;
 185     }
 186 
 187     obj_root = pcmk_find_cib_element(existing_cib, section);
 188 
 189     if (obj_root == NULL) {
 190         result = -ENXIO;
 191 
 192     } else if (options & cib_no_children) {
 193         xmlNode *shallow = create_xml_node(*answer,
 194                                            (const char *) obj_root->name);
 195 
 196         copy_in_properties(shallow, obj_root);
 197         *answer = shallow;
 198 
 199     } else {
 200         *answer = obj_root;
 201     }
 202 
 203     if (result == pcmk_ok && *answer == NULL) {
 204         crm_err("Error creating query response");
 205         result = -ENOMSG;
 206     }
 207 
 208     return result;
 209 }
 210 
 211 static int
 212 update_counter(xmlNode *xml_obj, const char *field, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
 213 {
 214     char *new_value = NULL;
 215     char *old_value = NULL;
 216     int int_value = -1;
 217 
 218     if (!reset && crm_element_value(xml_obj, field) != NULL) {
 219         old_value = crm_element_value_copy(xml_obj, field);
 220     }
 221     if (old_value != NULL) {
 222         int_value = atoi(old_value);
 223         new_value = pcmk__itoa(++int_value);
 224     } else {
 225         new_value = strdup("1");
 226         CRM_ASSERT(new_value != NULL);
 227     }
 228 
 229     crm_trace("Update %s from %s to %s",
 230               field, pcmk__s(old_value, "unset"), new_value);
 231     crm_xml_add(xml_obj, field, new_value);
 232 
 233     free(new_value);
 234     free(old_value);
 235 
 236     return pcmk_ok;
 237 }
 238 
 239 int
 240 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 241                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 242 {
 243     int result = pcmk_ok;
 244 
 245     crm_trace("Processing \"%s\" event", op);
 246 
 247     if (*result_cib != existing_cib) {
 248         free_xml(*result_cib);
 249     }
 250     *result_cib = createEmptyCib(0);
 251     copy_in_properties(*result_cib, existing_cib);
 252     update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
 253     *answer = NULL;
 254 
 255     return result;
 256 }
 257 
 258 int
 259 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 260                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 261                     xmlNode ** answer)
 262 {
 263     int rc = 0;
 264     int new_version = 0;
 265     int current_version = 0;
 266     int max_version = 0;
 267     const char *max = crm_element_value(req, F_CIB_SCHEMA_MAX);
 268     const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
 269 
 270     *answer = NULL;
 271     crm_trace("Processing \"%s\" event with max=%s", op, max);
 272 
 273     if (value != NULL) {
 274         current_version = get_schema_version(value);
 275     }
 276 
 277     if (max) {
 278         max_version = get_schema_version(max);
 279     }
 280 
 281     rc = update_validation(result_cib, &new_version, max_version, TRUE,
 282                            !(options & cib_verbose));
 283     if (new_version > current_version) {
 284         update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
 285         update_counter(*result_cib, XML_ATTR_GENERATION, true);
 286         update_counter(*result_cib, XML_ATTR_NUMUPDATES, true);
 287         return pcmk_ok;
 288     }
 289 
 290     return rc;
 291 }
 292 
 293 int
 294 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 295                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 296 {
 297     int result = pcmk_ok;
 298 
 299     crm_trace("Processing %s for epoch='%s'", op,
 300               pcmk__s(crm_element_value(existing_cib, XML_ATTR_GENERATION), ""));
 301 
 302     *answer = NULL;
 303     update_counter(*result_cib, XML_ATTR_GENERATION, false);
 304 
 305     return result;
 306 }
 307 
 308 int
 309 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
     /* [previous][next][first][last][top][bottom][index][help] */
 310                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
 311                     xmlNode ** answer)
 312 {
 313     int result = pcmk_ok;
 314 
 315     crm_trace("Processing %s for %s section",
 316               op, pcmk__s(section, "unspecified"));
 317 
 318     if (options & cib_xpath) {
 319         return cib_process_xpath(op, options, section, req, input,
 320                                  existing_cib, result_cib, answer);
 321     }
 322 
 323     *answer = NULL;
 324 
 325     if (input == NULL) {
 326         return -EINVAL;
 327     }
 328 
 329     if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
 330         section = NULL;
 331 
 332     } else if (pcmk__xe_is(input, section)) {
 333         section = NULL;
 334     }
 335 
 336     if (pcmk__xe_is(input, XML_TAG_CIB)) {
 337         int updates = 0;
 338         int epoch = 0;
 339         int admin_epoch = 0;
 340 
 341         int replace_updates = 0;
 342         int replace_epoch = 0;
 343         int replace_admin_epoch = 0;
 344 
 345         const char *reason = NULL;
 346         const char *peer = crm_element_value(req, F_ORIG);
 347         const char *digest = crm_element_value(req, XML_ATTR_DIGEST);
 348 
 349         if (digest) {
 350             const char *version = crm_element_value(req, XML_ATTR_CRM_VERSION);
 351             char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
 352                                                                  version ? version :
 353                                                                  CRM_FEATURE_SET);
 354 
 355             if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
 356                 crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
 357                         digest_verify, digest);
 358                 reason = "digest mismatch";
 359 
 360             } else {
 361                 crm_info("Digest matched on replace from %s: %s", peer, digest);
 362             }
 363             free(digest_verify);
 364 
 365         } else {
 366             crm_trace("No digest to verify");
 367         }
 368 
 369         cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
 370         cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
 371 
 372         if (replace_admin_epoch < admin_epoch) {
 373             reason = XML_ATTR_GENERATION_ADMIN;
 374 
 375         } else if (replace_admin_epoch > admin_epoch) {
 376             /* no more checks */
 377 
 378         } else if (replace_epoch < epoch) {
 379             reason = XML_ATTR_GENERATION;
 380 
 381         } else if (replace_epoch > epoch) {
 382             /* no more checks */
 383 
 384         } else if (replace_updates < updates) {
 385             reason = XML_ATTR_NUMUPDATES;
 386         }
 387 
 388         if (reason != NULL) {
 389             crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
 390                      " current %s is greater than the replacement",
 391                      replace_admin_epoch, replace_epoch,
 392                      replace_updates, peer, admin_epoch, epoch, updates, reason);
 393             result = -pcmk_err_old_data;
 394         } else {
 395             crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
 396                      admin_epoch, epoch, updates,
 397                      replace_admin_epoch, replace_epoch, replace_updates, peer);
 398         }
 399 
 400         if (*result_cib != existing_cib) {
 401             free_xml(*result_cib);
 402         }
 403         *result_cib = copy_xml(input);
 404 
 405     } else {
 406         xmlNode *obj_root = NULL;
 407         gboolean ok = TRUE;
 408 
 409         obj_root = pcmk_find_cib_element(*result_cib, section);
 410         ok = replace_xml_child(NULL, obj_root, input, FALSE);
 411         if (ok == FALSE) {
 412             crm_trace("No matching object to replace");
 413             result = -ENXIO;
 414         }
 415     }
 416 
 417     return result;
 418 }
 419 
 420 int
 421 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 422                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 423 {
 424     xmlNode *obj_root = NULL;
 425 
 426     crm_trace("Processing \"%s\" event", op);
 427 
 428     if (options & cib_xpath) {
 429         return cib_process_xpath(op, options, section, req, input,
 430                                  existing_cib, result_cib, answer);
 431     }
 432 
 433     if (input == NULL) {
 434         crm_err("Cannot perform modification with no data");
 435         return -EINVAL;
 436     }
 437 
 438     obj_root = pcmk_find_cib_element(*result_cib, section);
 439     if (pcmk__xe_is(input, section)) {
 440         xmlNode *child = NULL;
 441         for (child = pcmk__xml_first_child(input); child;
 442              child = pcmk__xml_next(child)) {
 443             if (replace_xml_child(NULL, obj_root, child, TRUE) == FALSE) {
 444                 crm_trace("No matching object to delete: %s=%s", child->name, ID(child));
 445             }
 446         }
 447 
 448     } else if (replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) {
 449             crm_trace("No matching object to delete: %s=%s", input->name, ID(input));
 450     }
 451 
 452     return pcmk_ok;
 453 }
 454 
 455 int
 456 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 457                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 458 {
 459     xmlNode *obj_root = NULL;
 460 
 461     crm_trace("Processing \"%s\" event", op);
 462 
 463     if (options & cib_xpath) {
 464         return cib_process_xpath(op, options, section, req, input,
 465                                  existing_cib, result_cib, answer);
 466     }
 467 
 468     if (input == NULL) {
 469         crm_err("Cannot perform modification with no data");
 470         return -EINVAL;
 471     }
 472 
 473     obj_root = pcmk_find_cib_element(*result_cib, section);
 474     if (obj_root == NULL) {
 475         xmlNode *tmp_section = NULL;
 476         const char *path = pcmk_cib_parent_name_for(section);
 477 
 478         if (path == NULL) {
 479             return -EINVAL;
 480         }
 481 
 482         tmp_section = create_xml_node(NULL, section);
 483         cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
 484                           NULL, result_cib, answer);
 485         free_xml(tmp_section);
 486 
 487         obj_root = pcmk_find_cib_element(*result_cib, section);
 488     }
 489 
 490     CRM_CHECK(obj_root != NULL, return -EINVAL);
 491 
 492     if (update_xml_child(obj_root, input) == FALSE) {
 493         if (options & cib_can_create) {
 494             add_node_copy(obj_root, input);
 495         } else {
 496             return -ENXIO;
 497         }
 498     }
 499 
 500     // @COMPAT cib_mixed_update is deprecated as of 2.1.7
 501     if (pcmk_is_set(options, cib_mixed_update)) {
 502         int max = 0, lpc;
 503         xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
 504 
 505         if (xpathObj) {
 506             max = numXpathResults(xpathObj);
 507             crm_log_xml_trace(*result_cib, "Mixed result");
 508         }
 509 
 510         for (lpc = 0; lpc < max; lpc++) {
 511             xmlNode *match = getXpathResult(xpathObj, lpc);
 512             xmlChar *match_path = xmlGetNodePath(match);
 513 
 514             crm_debug("Destroying %s", match_path);
 515             free(match_path);
 516             free_xml(match);
 517         }
 518 
 519         freeXpathObject(xpathObj);
 520     }
 521     return pcmk_ok;
 522 }
 523 
 524 static int
 525 update_cib_object(xmlNode * parent, xmlNode * update)
     /* [previous][next][first][last][top][bottom][index][help] */
 526 {
 527     int result = pcmk_ok;
 528     xmlNode *target = NULL;
 529     xmlNode *a_child = NULL;
 530     const char *replace = NULL;
 531     const char *object_id = NULL;
 532     const char *object_name = NULL;
 533 
 534     CRM_CHECK(update != NULL, return -EINVAL);
 535     CRM_CHECK(parent != NULL, return -EINVAL);
 536 
 537     object_name = (const char *) update->name;
 538     CRM_CHECK(object_name != NULL, return -EINVAL);
 539 
 540     object_id = ID(update);
 541     crm_trace("Processing update for <%s%s%s%s>", object_name,
 542               ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
 543               pcmk__s(object_id, ""),
 544               ((object_id == NULL)? "" : "'"));
 545 
 546     if (object_id == NULL) {
 547         /*  placeholder object */
 548         target = find_xml_node(parent, object_name, FALSE);
 549 
 550     } else {
 551         target = pcmk__xe_match(parent, object_name, XML_ATTR_ID, object_id);
 552     }
 553 
 554     if (target == NULL) {
 555         target = create_xml_node(parent, object_name);
 556     }
 557 
 558     crm_trace("Found node <%s%s%s%s> to update", object_name,
 559               ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
 560               pcmk__s(object_id, ""),
 561               ((object_id == NULL)? "" : "'"));
 562 
 563     // @COMPAT: XML_CIB_ATTR_REPLACE is unused internally. Remove at break.
 564     replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
 565     if (replace != NULL) {
 566         int last = 0;
 567         int len = strlen(replace);
 568 
 569         for (int lpc = 0; lpc <= len; ++lpc) {
 570             if (replace[lpc] == ',' || replace[lpc] == 0) {
 571                 if (last != lpc) {
 572                     char *replace_item = strndup(replace + last, lpc - last);
 573                     xmlNode *remove = find_xml_node(target, replace_item,
 574                                                     FALSE);
 575 
 576                     if (remove != NULL) {
 577                         crm_trace("Replacing node <%s> in <%s>",
 578                                   replace_item, target->name);
 579                         free_xml(remove);
 580                     }
 581                     free(replace_item);
 582                 }
 583                 last = lpc + 1;
 584             }
 585         }
 586         xml_remove_prop(update, XML_CIB_ATTR_REPLACE);
 587         xml_remove_prop(target, XML_CIB_ATTR_REPLACE);
 588     }
 589 
 590     copy_in_properties(target, update);
 591 
 592     if (xml_acl_denied(target)) {
 593         crm_notice("Cannot update <%s " XML_ATTR_ID "=%s>",
 594                    pcmk__s(object_name, "<null>"),
 595                    pcmk__s(object_id, "<null>"));
 596         return -EACCES;
 597     }
 598 
 599     crm_trace("Processing children of <%s%s%s%s>", object_name,
 600               ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
 601               pcmk__s(object_id, ""),
 602               ((object_id == NULL)? "" : "'"));
 603 
 604     for (a_child = pcmk__xml_first_child(update); a_child != NULL;
 605          a_child = pcmk__xml_next(a_child)) {
 606         int tmp_result = 0;
 607 
 608         crm_trace("Updating child <%s%s%s%s>", a_child->name,
 609                   ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
 610                   pcmk__s(ID(a_child), ""), ((ID(a_child) == NULL)? "" : "'"));
 611 
 612         tmp_result = update_cib_object(target, a_child);
 613 
 614         /*  only the first error is likely to be interesting */
 615         if (tmp_result != pcmk_ok) {
 616             crm_err("Error updating child <%s%s%s%s>",
 617                     a_child->name,
 618                     ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
 619                     pcmk__s(ID(a_child), ""),
 620                     ((ID(a_child) == NULL)? "" : "'"));
 621 
 622             if (result == pcmk_ok) {
 623                 result = tmp_result;
 624             }
 625         }
 626     }
 627 
 628     crm_trace("Finished handling update for <%s%s%s%s>", object_name,
 629               ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
 630               pcmk__s(object_id, ""),
 631               ((object_id == NULL)? "" : "'"));
 632 
 633     return result;
 634 }
 635 
 636 static int
 637 add_cib_object(xmlNode * parent, xmlNode * new_obj)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639     const char *object_name = NULL;
 640     const char *object_id = NULL;
 641     xmlNode *equiv_node = NULL;
 642 
 643     if ((parent == NULL) || (new_obj == NULL)) {
 644         return -EINVAL;
 645     }
 646 
 647     object_name = (const char *) new_obj->name;
 648     if (object_name == NULL) {
 649         return -EINVAL;
 650     }
 651 
 652     object_id = ID(new_obj);
 653 
 654     crm_trace("Processing creation of <%s%s%s%s>", object_name,
 655               ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
 656               pcmk__s(object_id, ""),
 657               ((object_id == NULL)? "" : "'"));
 658 
 659     if (object_id == NULL) {
 660         equiv_node = find_xml_node(parent, object_name, FALSE);
 661     } else {
 662         equiv_node = pcmk__xe_match(parent, object_name, XML_ATTR_ID,
 663                                     object_id);
 664     }
 665     if (equiv_node != NULL) {
 666         return -EEXIST;
 667     }
 668 
 669     return update_cib_object(parent, new_obj);
 670 }
 671 
 672 static bool
 673 update_results(xmlNode *failed, xmlNode *target, const char *operation,
     /* [previous][next][first][last][top][bottom][index][help] */
 674                int return_code)
 675 {
 676     xmlNode *xml_node = NULL;
 677     bool was_error = false;
 678     const char *error_msg = NULL;
 679 
 680     if (return_code != pcmk_ok) {
 681         error_msg = pcmk_strerror(return_code);
 682 
 683         was_error = true;
 684         xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB);
 685         add_node_copy(xml_node, target);
 686 
 687         crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target));
 688         crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE,
 689                     (const char *) target->name);
 690         crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation);
 691         crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg);
 692 
 693         crm_warn("Action %s failed: %s (cde=%d)",
 694                  operation, error_msg, return_code);
 695     }
 696 
 697     return was_error;
 698 }
 699 
 700 int
 701 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 702                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 703 {
 704     xmlNode *failed = NULL;
 705     int result = pcmk_ok;
 706     xmlNode *update_section = NULL;
 707 
 708     crm_trace("Processing %s for %s section",
 709               op, pcmk__s(section, "unspecified"));
 710     if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
 711         section = NULL;
 712 
 713     } else if (pcmk__str_eq(XML_TAG_CIB, section, pcmk__str_casei)) {
 714         section = NULL;
 715 
 716     } else if (pcmk__xe_is(input, XML_TAG_CIB)) {
 717         section = NULL;
 718     }
 719 
 720     CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
 721 
 722     if (input == NULL) {
 723         crm_err("Cannot perform modification with no data");
 724         return -EINVAL;
 725     }
 726 
 727     if (section == NULL) {
 728         return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
 729                                   answer);
 730     }
 731 
 732     failed = create_xml_node(NULL, XML_TAG_FAILED);
 733 
 734     update_section = pcmk_find_cib_element(*result_cib, section);
 735     if (pcmk__xe_is(input, section)) {
 736         xmlNode *a_child = NULL;
 737 
 738         for (a_child = pcmk__xml_first_child(input); a_child != NULL;
 739              a_child = pcmk__xml_next(a_child)) {
 740             result = add_cib_object(update_section, a_child);
 741             if (update_results(failed, a_child, op, result)) {
 742                 break;
 743             }
 744         }
 745 
 746     } else {
 747         result = add_cib_object(update_section, input);
 748         update_results(failed, input, op, result);
 749     }
 750 
 751     if ((result == pcmk_ok) && (failed->children != NULL)) {
 752         result = -EINVAL;
 753     }
 754 
 755     if (result != pcmk_ok) {
 756         crm_log_xml_err(failed, "CIB Update failures");
 757         *answer = failed;
 758 
 759     } else {
 760         free_xml(failed);
 761     }
 762 
 763     return result;
 764 }
 765 
 766 int
 767 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
     /* [previous][next][first][last][top][bottom][index][help] */
 768                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 769 {
 770     const char *originator = NULL;
 771 
 772     if (req != NULL) {
 773         originator = crm_element_value(req, F_ORIG);
 774     }
 775 
 776     crm_trace("Processing \"%s\" event from %s%s",
 777               op, originator,
 778               (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
 779 
 780     if (*result_cib != existing_cib) {
 781         free_xml(*result_cib);
 782     }
 783     *result_cib = copy_xml(existing_cib);
 784 
 785     return xml_apply_patchset(*result_cib, input, TRUE);
 786 }
 787 
 788 // @COMPAT: v1-only
 789 bool
 790 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
     /* [previous][next][first][last][top][bottom][index][help] */
 791 {
 792     int lpc = 0, max = 0;
 793     bool config_changes = false;
 794     xmlXPathObject *xpathObj = NULL;
 795     int format = 1;
 796 
 797     CRM_ASSERT(diff != NULL);
 798 
 799     if (*diff == NULL && last != NULL && next != NULL) {
 800         *diff = diff_xml_object(last, next, FALSE);
 801     }
 802 
 803     if (*diff == NULL) {
 804         goto done;
 805     }
 806 
 807     crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
 808     CRM_LOG_ASSERT(format == 1);
 809 
 810     xpathObj = xpath_search(*diff, "//" XML_CIB_TAG_CONFIGURATION);
 811     if (numXpathResults(xpathObj) > 0) {
 812         config_changes = true;
 813         goto done;
 814     }
 815     freeXpathObject(xpathObj);
 816 
 817     /*
 818      * Do not check XML_TAG_DIFF_ADDED "//" XML_TAG_CIB
 819      * This always contains every field and would produce a false positive
 820      * every time if the checked value existed
 821      */
 822     xpathObj = xpath_search(*diff, "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_CIB);
 823     max = numXpathResults(xpathObj);
 824 
 825     for (lpc = 0; lpc < max; lpc++) {
 826         xmlNode *top = getXpathResult(xpathObj, lpc);
 827 
 828         if (crm_element_value(top, XML_ATTR_GENERATION) != NULL) {
 829             config_changes = true;
 830             goto done;
 831         }
 832         if (crm_element_value(top, XML_ATTR_GENERATION_ADMIN) != NULL) {
 833             config_changes = true;
 834             goto done;
 835         }
 836 
 837         if (crm_element_value(top, XML_ATTR_VALIDATION) != NULL) {
 838             config_changes = true;
 839             goto done;
 840         }
 841         if (crm_element_value(top, XML_ATTR_CRM_VERSION) != NULL) {
 842             config_changes = true;
 843             goto done;
 844         }
 845         if (crm_element_value(top, "remote-clear-port") != NULL) {
 846             config_changes = true;
 847             goto done;
 848         }
 849         if (crm_element_value(top, "remote-tls-port") != NULL) {
 850             config_changes = true;
 851             goto done;
 852         }
 853     }
 854 
 855   done:
 856     freeXpathObject(xpathObj);
 857     return config_changes;
 858 }
 859 
 860 int
 861 cib_process_xpath(const char *op, int options, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 862                   const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
 863                   xmlNode **result_cib, xmlNode **answer)
 864 {
 865     int lpc = 0;
 866     int max = 0;
 867     int rc = pcmk_ok;
 868     bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
 869 
 870     xmlXPathObjectPtr xpathObj = NULL;
 871 
 872     crm_trace("Processing \"%s\" event", op);
 873 
 874     if (is_query) {
 875         xpathObj = xpath_search(existing_cib, section);
 876     } else {
 877         xpathObj = xpath_search(*result_cib, section);
 878     }
 879 
 880     max = numXpathResults(xpathObj);
 881 
 882     if ((max < 1)
 883         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 884         crm_debug("%s was already removed", section);
 885 
 886     } else if (max < 1) {
 887         crm_debug("%s: %s does not exist", op, section);
 888         rc = -ENXIO;
 889 
 890     } else if (is_query) {
 891         if (max > 1) {
 892             *answer = create_xml_node(NULL, "xpath-query");
 893         }
 894     }
 895 
 896     if (pcmk_is_set(options, cib_multiple)
 897         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 898         dedupXpathResults(xpathObj);
 899     }
 900 
 901     for (lpc = 0; lpc < max; lpc++) {
 902         xmlChar *path = NULL;
 903         xmlNode *match = getXpathResult(xpathObj, lpc);
 904 
 905         if (match == NULL) {
 906             continue;
 907         }
 908 
 909         path = xmlGetNodePath(match);
 910         crm_debug("Processing %s op for %s with %s", op, section, path);
 911         free(path);
 912 
 913         if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
 914             if (match == *result_cib) {
 915                 /* Attempting to delete the whole "/cib" */
 916                 crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
 917                 rc = -EINVAL;
 918                 break;
 919             }
 920 
 921             free_xml(match);
 922             if ((options & cib_multiple) == 0) {
 923                 break;
 924             }
 925 
 926         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
 927             if (update_xml_child(match, input) == FALSE) {
 928                 rc = -ENXIO;
 929             } else if ((options & cib_multiple) == 0) {
 930                 break;
 931             }
 932 
 933         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
 934             add_node_copy(match, input);
 935             break;
 936 
 937         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
 938 
 939             if (options & cib_no_children) {
 940                 xmlNode *shallow = create_xml_node(*answer,
 941                                                    (const char *) match->name);
 942 
 943                 copy_in_properties(shallow, match);
 944 
 945                 if (*answer == NULL) {
 946                     *answer = shallow;
 947                 }
 948 
 949             } else if (options & cib_xpath_address) {
 950                 char *path = NULL;
 951                 xmlNode *parent = match;
 952 
 953                 while (parent && parent->type == XML_ELEMENT_NODE) {
 954                     const char *id = crm_element_value(parent, XML_ATTR_ID);
 955                     char *new_path = NULL;
 956 
 957                     if (id) {
 958                         new_path = crm_strdup_printf("/%s[@" XML_ATTR_ID "='%s']"
 959                                                      "%s",
 960                                                      parent->name, id,
 961                                                      pcmk__s(path, ""));
 962                     } else {
 963                         new_path = crm_strdup_printf("/%s%s", parent->name,
 964                                                      pcmk__s(path, ""));
 965                     }
 966                     free(path);
 967                     path = new_path;
 968                     parent = parent->parent;
 969                 }
 970                 crm_trace("Got: %s", path);
 971 
 972                 if (*answer == NULL) {
 973                     *answer = create_xml_node(NULL, "xpath-query");
 974                 }
 975                 parent = create_xml_node(*answer, "xpath-query-path");
 976                 crm_xml_add(parent, XML_ATTR_ID, path);
 977                 free(path);
 978 
 979             } else if (*answer) {
 980                 add_node_copy(*answer, match);
 981 
 982             } else {
 983                 *answer = match;
 984             }
 985 
 986         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
 987                                 pcmk__str_none)) {
 988             xmlNode *parent = match->parent;
 989 
 990             free_xml(match);
 991             if (input != NULL) {
 992                 add_node_copy(parent, input);
 993             }
 994 
 995             if ((options & cib_multiple) == 0) {
 996                 break;
 997             }
 998         }
 999     }
1000 
1001     freeXpathObject(xpathObj);
1002     return rc;
1003 }

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