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