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