1 /*
2  * Copyright (c) 2004 International Business Machines
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  */
19 #include <crm_internal.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <sys/utsname.h>
26 
27 #include <glib.h>
28 
29 #include <crm/crm.h>
30 #include <crm/cib/internal.h>
31 #include <crm/msg_xml.h>
32 #include <crm/common/xml.h>
33 #include <crm/pengine/rules.h>
34 
35 struct config_root_s {
36     const char *name;
37     const char *parent;
38     const char *path;
39 };
40 
41  /*
42   * "//crm_config" will also work in place of "/cib/configuration/crm_config"
43   * The / prefix means find starting from the root, whereas the // prefix means
44   * find anywhere and risks multiple matches
45   */
46 /* *INDENT-OFF* */
47 struct config_root_s known_paths[] = {
48     { NULL,			NULL,                 "//cib" },
49     { XML_TAG_CIB,		NULL,                 "//cib" },
50     { XML_CIB_TAG_STATUS,       "/cib",               "//cib/status" },
51     { XML_CIB_TAG_CONFIGURATION,"/cib",               "//cib/configuration" },
52     { XML_CIB_TAG_CRMCONFIG,    "/cib/configuration", "//cib/configuration/crm_config" },
53     { XML_CIB_TAG_NODES,        "/cib/configuration", "//cib/configuration/nodes" },
54     { XML_CIB_TAG_DOMAINS,      "/cib/configuration", "//cib/configuration/domains" },
55     { XML_CIB_TAG_RESOURCES,    "/cib/configuration", "//cib/configuration/resources" },
56     { XML_CIB_TAG_CONSTRAINTS,  "/cib/configuration", "//cib/configuration/constraints" },
57     { XML_CIB_TAG_OPCONFIG,	"/cib/configuration", "//cib/configuration/op_defaults" },
58     { XML_CIB_TAG_RSCCONFIG,	"/cib/configuration", "//cib/configuration/rsc_defaults" },
59     { XML_CIB_TAG_ACLS,		"/cib/configuration", "//cib/configuration/acls" },
60     { XML_TAG_FENCING_TOPOLOGY,	"/cib/configuration", "//cib/configuration/fencing-topology" },
61     { XML_CIB_TAG_SECTION_ALL,  NULL,                 "//cib" },
62 };
63 /* *INDENT-ON* */
64 
65 int
cib_compare_generation(xmlNode * left,xmlNode * right)66 cib_compare_generation(xmlNode * left, xmlNode * right)
67 {
68     int lpc = 0;
69 
70     const char *attributes[] = {
71         XML_ATTR_GENERATION_ADMIN,
72         XML_ATTR_GENERATION,
73         XML_ATTR_NUMUPDATES,
74     };
75 
76     crm_log_xml_trace(left, "left");
77     crm_log_xml_trace(right, "right");
78 
79     for (lpc = 0; lpc < DIMOF(attributes); lpc++) {
80         int int_elem_l = -1;
81         int int_elem_r = -1;
82         const char *elem_r = NULL;
83         const char *elem_l = crm_element_value(left, attributes[lpc]);
84 
85         if (right != NULL) {
86             elem_r = crm_element_value(right, attributes[lpc]);
87         }
88 
89         if (elem_l != NULL) {
90             int_elem_l = crm_parse_int(elem_l, NULL);
91         }
92         if (elem_r != NULL) {
93             int_elem_r = crm_parse_int(elem_r, NULL);
94         }
95 
96         if (int_elem_l < int_elem_r) {
97             crm_trace("%s (%s < %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
98             return -1;
99 
100         } else if (int_elem_l > int_elem_r) {
101             crm_trace("%s (%s > %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
102             return 1;
103         }
104     }
105 
106     return 0;
107 }
108 
109 /* Deprecated - doesn't expose -EACCES */
110 xmlNode *
get_cib_copy(cib_t * cib)111 get_cib_copy(cib_t * cib)
112 {
113     xmlNode *xml_cib;
114     int options = cib_scope_local | cib_sync_call;
115     int rc = pcmk_ok;
116 
117     if (cib->state == cib_disconnected) {
118         return NULL;
119     }
120 
121     rc = cib->cmds->query(cib, NULL, &xml_cib, options);
122     if (rc == -EACCES) {
123         return NULL;
124 
125     } else if (rc != pcmk_ok) {
126         crm_err("Couldn't retrieve the CIB");
127         free_xml(xml_cib);
128         return NULL;
129 
130     } else if (xml_cib == NULL) {
131         crm_err("The CIB result was empty");
132         free_xml(xml_cib);
133         return NULL;
134     }
135 
136     if (safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) {
137         return xml_cib;
138     }
139     free_xml(xml_cib);
140     return NULL;
141 }
142 
143 xmlNode *
cib_get_generation(cib_t * cib)144 cib_get_generation(cib_t * cib)
145 {
146     xmlNode *the_cib = NULL;
147     xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
148 
149     cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
150     if (the_cib != NULL) {
151         copy_in_properties(generation, the_cib);
152         free_xml(the_cib);
153     }
154 
155     return generation;
156 }
157 
158 gboolean
cib_version_details(xmlNode * cib,int * admin_epoch,int * epoch,int * updates)159 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
160 {
161     *epoch = -1;
162     *updates = -1;
163     *admin_epoch = -1;
164 
165     if (cib == NULL) {
166         return FALSE;
167 
168     } else {
169         crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
170         crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
171         crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
172     }
173     return TRUE;
174 }
175 
176 gboolean
cib_diff_version_details(xmlNode * diff,int * admin_epoch,int * epoch,int * updates,int * _admin_epoch,int * _epoch,int * _updates)177 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
178                          int *_admin_epoch, int *_epoch, int *_updates)
179 {
180     int add[] = { 0, 0, 0 };
181     int del[] = { 0, 0, 0 };
182 
183     xml_patch_versions(diff, add, del);
184 
185     *admin_epoch = add[0];
186     *epoch = add[1];
187     *updates = add[2];
188 
189     *_admin_epoch = del[0];
190     *_epoch = del[1];
191     *_updates = del[2];
192 
193     return TRUE;
194 }
195 
196 /*
197  * The caller should never free the return value
198  */
199 
200 const char *
get_object_path(const char * object_type)201 get_object_path(const char *object_type)
202 {
203     int lpc = 0;
204     int max = DIMOF(known_paths);
205 
206     for (; lpc < max; lpc++) {
207         if ((object_type == NULL && known_paths[lpc].name == NULL)
208             || safe_str_eq(object_type, known_paths[lpc].name)) {
209             return known_paths[lpc].path;
210         }
211     }
212     return NULL;
213 }
214 
215 const char *
get_object_parent(const char * object_type)216 get_object_parent(const char *object_type)
217 {
218     int lpc = 0;
219     int max = DIMOF(known_paths);
220 
221     for (; lpc < max; lpc++) {
222         if (safe_str_eq(object_type, known_paths[lpc].name)) {
223             return known_paths[lpc].parent;
224         }
225     }
226     return NULL;
227 }
228 
229 xmlNode *
get_object_root(const char * object_type,xmlNode * the_root)230 get_object_root(const char *object_type, xmlNode * the_root)
231 {
232     const char *xpath = get_object_path(object_type);
233 
234     if (xpath == NULL) {
235         return the_root;        /* or return NULL? */
236     }
237 
238     return get_xpath_object(xpath, the_root, LOG_DEBUG_4);
239 }
240 
241 /*
242  * It is the callers responsibility to free both the new CIB (output)
243  *     and the new CIB (input)
244  */
245 xmlNode *
createEmptyCib(int admin_epoch)246 createEmptyCib(int admin_epoch)
247 {
248     xmlNode *cib_root = NULL, *config = NULL;
249 
250     cib_root = create_xml_node(NULL, XML_TAG_CIB);
251     crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
252     crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema());
253 
254     crm_xml_add_int(cib_root, XML_ATTR_GENERATION, admin_epoch);
255     crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0);
256     crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0);
257 
258     config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
259     create_xml_node(cib_root, XML_CIB_TAG_STATUS);
260 
261     create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
262     create_xml_node(config, XML_CIB_TAG_NODES);
263     create_xml_node(config, XML_CIB_TAG_RESOURCES);
264     create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
265 
266     return cib_root;
267 }
268 
269 static bool
cib_acl_enabled(xmlNode * xml,const char * user)270 cib_acl_enabled(xmlNode *xml, const char *user)
271 {
272     bool rc = FALSE;
273 
274 #if ENABLE_ACL
275     if(pcmk_acl_required(user)) {
276         const char *value = NULL;
277         GHashTable *options = crm_str_table_new();
278 
279         cib_read_config(options, xml);
280         value = cib_pref(options, "enable-acl");
281         rc = crm_is_true(value);
282         g_hash_table_destroy(options);
283     }
284 
285     crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
286 #endif
287     return rc;
288 }
289 
290 int
cib_perform_op(const char * op,int call_options,cib_op_t * fn,gboolean is_query,const char * section,xmlNode * req,xmlNode * input,gboolean manage_counters,gboolean * config_changed,xmlNode * current_cib,xmlNode ** result_cib,xmlNode ** diff,xmlNode ** output)291 cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
292                const char *section, xmlNode * req, xmlNode * input,
293                gboolean manage_counters, gboolean * config_changed,
294                xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
295 {
296     int rc = pcmk_ok;
297     gboolean check_dtd = TRUE;
298     xmlNode *top = NULL;
299     xmlNode *scratch = NULL;
300     xmlNode *local_diff = NULL;
301 
302     const char *new_version = NULL;
303     static struct qb_log_callsite *diff_cs = NULL;
304     const char *user = crm_element_value(req, F_CIB_USER);
305     bool with_digest = FALSE;
306 
307     crm_trace("Begin %s%s%s op", is_set(call_options, cib_dryrun)?"dry-run of ":"", is_query ? "read-only " : "", op);
308 
309     CRM_CHECK(output != NULL, return -ENOMSG);
310     CRM_CHECK(result_cib != NULL, return -ENOMSG);
311     CRM_CHECK(config_changed != NULL, return -ENOMSG);
312 
313     if(output) {
314         *output = NULL;
315     }
316 
317     *result_cib = NULL;
318     *config_changed = FALSE;
319 
320     if (fn == NULL) {
321         return -EINVAL;
322     }
323 
324     if (is_query) {
325         xmlNode *cib_ro = current_cib;
326         xmlNode *cib_filtered = NULL;
327 
328         if(cib_acl_enabled(cib_ro, user)) {
329             if(xml_acl_filtered_copy(user, current_cib, current_cib, &cib_filtered)) {
330                 if (cib_filtered == NULL) {
331                     crm_debug("Pre-filtered the entire cib");
332                     return -EACCES;
333                 }
334                 cib_ro = cib_filtered;
335                 crm_log_xml_trace(cib_ro, "filtered");
336             }
337         }
338 
339         rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
340 
341         if(output == NULL || *output == NULL) {
342             /* nothing */
343 
344         } else if(cib_filtered == *output) {
345             cib_filtered = NULL; /* Let them have this copy */
346 
347         } else if(*output == current_cib) {
348             /* They already know not to free it */
349 
350         } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
351             /* We're about to free the document of which *output is a part */
352             *output = copy_xml(*output);
353 
354         } else if((*output)->doc == current_cib->doc) {
355             /* Give them a copy they can free */
356             *output = copy_xml(*output);
357         }
358 
359         free_xml(cib_filtered);
360         return rc;
361     }
362 
363 
364     if (is_set(call_options, cib_zero_copy)) {
365         /* Conditional on v2 patch style */
366 
367         scratch = current_cib;
368 
369         /* Create a shallow copy of current_cib for the version details */
370         current_cib = create_xml_node(NULL, (const char *)scratch->name);
371         copy_in_properties(current_cib, scratch);
372         top = current_cib;
373 
374         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
375         rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
376 
377     } else {
378         scratch = copy_xml(current_cib);
379         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
380         rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
381 
382         if(scratch && xml_tracking_changes(scratch) == FALSE) {
383             crm_trace("Inferring changes after %s op", op);
384             xml_track_changes(scratch, user, current_cib, cib_acl_enabled(current_cib, user));
385             xml_calculate_changes(current_cib, scratch);
386         }
387         CRM_CHECK(current_cib != scratch, return -EINVAL);
388     }
389 
390     xml_acl_disable(scratch); /* Allow the system to make any additional changes */
391 
392     if (rc == pcmk_ok && scratch == NULL) {
393         rc = -EINVAL;
394         goto done;
395 
396     } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
397         crm_trace("ACL rejected part or all of the proposed changes");
398         rc = -EACCES;
399         goto done;
400 
401     } else if (rc != pcmk_ok) {
402         goto done;
403     }
404 
405     if (scratch) {
406         new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
407 
408         if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
409             crm_err("Discarding update with feature set '%s' greater than our own '%s'",
410                     new_version, CRM_FEATURE_SET);
411             rc = -EPROTONOSUPPORT;
412             goto done;
413         }
414     }
415 
416     if (current_cib) {
417         int old = 0;
418         int new = 0;
419 
420         crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
421         crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
422 
423         if (old > new) {
424             crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
425                     XML_ATTR_GENERATION_ADMIN, old, new, call_options);
426             crm_log_xml_warn(req, "Bad Op");
427             crm_log_xml_warn(input, "Bad Data");
428             rc = -pcmk_err_old_data;
429 
430         } else if (old == new) {
431             crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
432             crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
433             if (old > new) {
434                 crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
435                         XML_ATTR_GENERATION, old, new, call_options);
436                 crm_log_xml_warn(req, "Bad Op");
437                 crm_log_xml_warn(input, "Bad Data");
438                 rc = -pcmk_err_old_data;
439             }
440         }
441     }
442 
443     crm_trace("Massaging CIB contents");
444     strip_text_nodes(scratch);
445     fix_plus_plus_recursive(scratch);
446 
447     if (is_set(call_options, cib_zero_copy)) {
448         /* At this point, current_cib is just the 'cib' tag and its properties,
449          *
450          * The v1 format would barf on this, but we know the v2 patch
451          * format only needs it for the top-level version fields
452          */
453         local_diff = xml_create_patchset(2, current_cib, scratch, (bool*)config_changed, manage_counters);
454 
455     } else {
456         static time_t expires = 0;
457         time_t tm_now = time(NULL);
458 
459         if (expires < tm_now) {
460             expires = tm_now + 60;  /* Validate clients are correctly applying v2-style diffs at most once a minute */
461             with_digest = TRUE;
462         }
463 
464         local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)config_changed, manage_counters);
465     }
466 
467     xml_log_changes(LOG_TRACE, __FUNCTION__, scratch);
468     xml_accept_changes(scratch);
469 
470     if (diff_cs == NULL) {
471         diff_cs = qb_log_callsite_get(__PRETTY_FUNCTION__, __FILE__, "diff-validation", LOG_DEBUG, __LINE__, crm_trace_nonlog);
472     }
473 
474     if(local_diff) {
475         patchset_process_digest(local_diff, current_cib, scratch, with_digest);
476 
477         xml_log_patchset(LOG_INFO, __FUNCTION__, local_diff);
478         crm_log_xml_trace(local_diff, "raw patch");
479     }
480 
481     if (is_not_set(call_options, cib_zero_copy) /* The original to compare against doesn't exist */
482         && local_diff
483         && crm_is_callsite_active(diff_cs, LOG_TRACE, 0)) {
484 
485         /* Validate the calculated patch set */
486         int test_rc, format = 1;
487         xmlNode * c = copy_xml(current_cib);
488 
489         crm_element_value_int(local_diff, "format", &format);
490         test_rc = xml_apply_patchset(c, local_diff, manage_counters);
491 
492         if(test_rc != pcmk_ok) {
493             save_xml_to_file(c,           "PatchApply:calculated", NULL);
494             save_xml_to_file(current_cib, "PatchApply:input", NULL);
495             save_xml_to_file(scratch,     "PatchApply:actual", NULL);
496             save_xml_to_file(local_diff,  "PatchApply:diff", NULL);
497             crm_err("v%d patchset error, patch failed to apply: %s (%d)", format, pcmk_strerror(test_rc), test_rc);
498         }
499         free_xml(c);
500     }
501 
502     if (safe_str_eq(section, XML_CIB_TAG_STATUS)) {
503         /* Throttle the amount of costly validation we perform due to status updates
504          * a) we don't really care whats in the status section
505          * b) we don't validate any of its contents at the moment anyway
506          */
507         check_dtd = FALSE;
508     }
509 
510     /* === scratch must not be modified after this point ===
511      * Exceptions, anything in:
512 
513      static filter_t filter[] = {
514      { 0, XML_ATTR_ORIGIN },
515      { 0, XML_CIB_ATTR_WRITTEN },
516      { 0, XML_ATTR_UPDATE_ORIG },
517      { 0, XML_ATTR_UPDATE_CLIENT },
518      { 0, XML_ATTR_UPDATE_USER },
519      };
520      */
521 
522     if (*config_changed && is_not_set(call_options, cib_no_mtime)) {
523         char *now_str = NULL;
524         time_t now = time(NULL);
525         const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
526 
527         now_str = ctime(&now);
528         now_str[24] = EOS;      /* replace the newline */
529         crm_xml_replace(scratch, XML_CIB_ATTR_WRITTEN, now_str);
530 
531         if (schema) {
532             static int minimum_schema = 0;
533             int current_schema = get_schema_version(schema);
534 
535             if (minimum_schema == 0) {
536                 minimum_schema = get_schema_version("pacemaker-1.2");
537             }
538 
539             /* Does the CIB support the "update-*" attributes... */
540             if (current_schema >= minimum_schema) {
541                 const char *origin = crm_element_value(req, F_ORIG);
542 
543                 CRM_LOG_ASSERT(origin != NULL);
544                 crm_xml_replace(scratch, XML_ATTR_UPDATE_ORIG, origin);
545                 crm_xml_replace(scratch, XML_ATTR_UPDATE_CLIENT,
546                                 crm_element_value(req, F_CIB_CLIENTNAME));
547 #if ENABLE_ACL
548                 crm_xml_replace(scratch, XML_ATTR_UPDATE_USER, crm_element_value(req, F_CIB_USER));
549 #endif
550             }
551         }
552     }
553 
554     crm_trace("Perform validation: %s", check_dtd ? "true" : "false");
555     if (rc == pcmk_ok && check_dtd && validate_xml(scratch, NULL, TRUE) == FALSE) {
556         const char *current_dtd = crm_element_value(scratch, XML_ATTR_VALIDATION);
557 
558         crm_warn("Updated CIB does not validate against %s schema/dtd", crm_str(current_dtd));
559         rc = -pcmk_err_schema_validation;
560     }
561 
562   done:
563 
564     *result_cib = scratch;
565 #if ENABLE_ACL
566     if(rc != pcmk_ok && cib_acl_enabled(current_cib, user)) {
567         if(xml_acl_filtered_copy(user, current_cib, scratch, result_cib)) {
568             if (*result_cib == NULL) {
569                 crm_debug("Pre-filtered the entire cib result");
570             }
571             free_xml(scratch);
572         }
573     }
574 #endif
575 
576     if(diff) {
577         *diff = local_diff;
578     } else {
579         free_xml(local_diff);
580     }
581 
582     free_xml(top);
583     crm_trace("Done");
584     return rc;
585 }
586 
587 xmlNode *
cib_create_op(int call_id,const char * token,const char * op,const char * host,const char * section,xmlNode * data,int call_options,const char * user_name)588 cib_create_op(int call_id, const char *token, const char *op, const char *host, const char *section,
589               xmlNode * data, int call_options, const char *user_name)
590 {
591     xmlNode *op_msg = create_xml_node(NULL, "cib_command");
592 
593     CRM_CHECK(op_msg != NULL, return NULL);
594     CRM_CHECK(token != NULL, return NULL);
595 
596     crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
597 
598     crm_xml_add(op_msg, F_TYPE, T_CIB);
599     crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
600     crm_xml_add(op_msg, F_CIB_OPERATION, op);
601     crm_xml_add(op_msg, F_CIB_HOST, host);
602     crm_xml_add(op_msg, F_CIB_SECTION, section);
603     crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
604 #if ENABLE_ACL
605     if (user_name) {
606         crm_xml_add(op_msg, F_CIB_USER, user_name);
607     }
608 #endif
609     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
610     crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
611 
612     if (data != NULL) {
613         add_message_xml(op_msg, F_CIB_CALLDATA, data);
614     }
615 
616     if (call_options & cib_inhibit_bcast) {
617         CRM_CHECK((call_options & cib_scope_local), return NULL);
618     }
619     return op_msg;
620 }
621 
622 void
cib_native_callback(cib_t * cib,xmlNode * msg,int call_id,int rc)623 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
624 {
625     xmlNode *output = NULL;
626     cib_callback_client_t *blob = NULL;
627 
628     if (msg != NULL) {
629         crm_element_value_int(msg, F_CIB_RC, &rc);
630         crm_element_value_int(msg, F_CIB_CALLID, &call_id);
631         output = get_message_xml(msg, F_CIB_CALLDATA);
632     }
633 
634     blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id));
635     if (blob == NULL) {
636         crm_trace("No callback found for call %d", call_id);
637     }
638 
639     if (cib == NULL) {
640         crm_debug("No cib object supplied");
641     }
642 
643     if (rc == -pcmk_err_diff_resync) {
644         /* This is an internal value that clients do not and should not care about */
645         rc = pcmk_ok;
646     }
647 
648     if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
649         crm_trace("Invoking callback %s for call %d", crm_str(blob->id), call_id);
650         blob->callback(msg, call_id, rc, output, blob->user_data);
651 
652     } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
653         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
654         crm_log_xml_debug(msg, "Failed CIB Update");
655     }
656 
657     /* This may free user_data, so do it after the callback */
658     if (blob) {
659         remove_cib_op_callback(call_id, FALSE);
660     }
661 
662     if (cib && cib->op_callback != NULL) {
663         crm_trace("Invoking global callback for call %d", call_id);
664         cib->op_callback(msg, call_id, rc, output);
665     }
666     crm_trace("OP callback activated for %d", call_id);
667 }
668 
669 void
cib_native_notify(gpointer data,gpointer user_data)670 cib_native_notify(gpointer data, gpointer user_data)
671 {
672     xmlNode *msg = user_data;
673     cib_notify_client_t *entry = data;
674     const char *event = NULL;
675 
676     if (msg == NULL) {
677         crm_warn("Skipping callback - NULL message");
678         return;
679     }
680 
681     event = crm_element_value(msg, F_SUBTYPE);
682 
683     if (entry == NULL) {
684         crm_warn("Skipping callback - NULL callback client");
685         return;
686 
687     } else if (entry->callback == NULL) {
688         crm_warn("Skipping callback - NULL callback");
689         return;
690 
691     } else if (safe_str_neq(entry->event, event)) {
692         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
693         return;
694     }
695 
696     crm_trace("Invoking callback for %p/%s event...", entry, event);
697     entry->callback(event, msg);
698     crm_trace("Callback invoked...");
699 }
700 
701 pe_cluster_option cib_opts[] = {
702     /*
703      * name, legacy name,
704      * type, allowed values, default, validator,
705      * short description,
706      * long description
707      */
708     {
709         "enable-acl", NULL,
710         "boolean", NULL, "false", &check_boolean,
711         "Enable CIB ACL",
712         NULL
713     },
714     {
715         "cluster-ipc-limit", NULL,
716         "integer", NULL, "500", &check_positive_number,
717         "Maximum IPC message backlog before disconnecting a cluster daemon",
718         "Raise this if log has \"Evicting client\" messages for cluster daemon"
719             " PIDs (a good value is the number of resources in the cluster"
720             " multiplied by the number of nodes)"
721     },
722 };
723 
724 void
cib_metadata(void)725 cib_metadata(void)
726 {
727     config_metadata("Cluster Information Base", "1.0",
728                     "Cluster Information Base Options",
729                     "This is a fake resource that details the options that can be configured for the Cluster Information Base.",
730                     cib_opts, DIMOF(cib_opts));
731 }
732 
733 void
verify_cib_options(GHashTable * options)734 verify_cib_options(GHashTable * options)
735 {
736     verify_all_options(options, cib_opts, DIMOF(cib_opts));
737 }
738 
739 const char *
cib_pref(GHashTable * options,const char * name)740 cib_pref(GHashTable * options, const char *name)
741 {
742     return get_cluster_pref(options, cib_opts, DIMOF(cib_opts), name);
743 }
744 
745 gboolean
cib_read_config(GHashTable * options,xmlNode * current_cib)746 cib_read_config(GHashTable * options, xmlNode * current_cib)
747 {
748     xmlNode *config = NULL;
749     crm_time_t *now = NULL;
750 
751     if (options == NULL || current_cib == NULL) {
752         return FALSE;
753     }
754 
755     now = crm_time_new(NULL);
756 
757     g_hash_table_remove_all(options);
758 
759     config = get_object_root(XML_CIB_TAG_CRMCONFIG, current_cib);
760     if (config) {
761         unpack_instance_attributes(current_cib, config, XML_CIB_TAG_PROPSET, NULL, options,
762                                    CIB_OPTIONS_FIRST, TRUE, now);
763     }
764 
765     verify_cib_options(options);
766 
767     crm_time_free(now);
768 
769     return TRUE;
770 }
771 
772 int
cib_apply_patch_event(xmlNode * event,xmlNode * input,xmlNode ** output,int level)773 cib_apply_patch_event(xmlNode * event, xmlNode * input, xmlNode ** output, int level)
774 {
775     int rc = pcmk_err_generic;
776 
777     xmlNode *diff = NULL;
778 
779     CRM_ASSERT(event);
780     CRM_ASSERT(input);
781     CRM_ASSERT(output);
782 
783     crm_element_value_int(event, F_CIB_RC, &rc);
784     diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
785 
786     if (rc < pcmk_ok || diff == NULL) {
787         return rc;
788     }
789 
790     if (level > LOG_CRIT) {
791         xml_log_patchset(level, "Config update", diff);
792     }
793 
794     if (input != NULL) {
795         rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output, NULL);
796 
797         if (rc != pcmk_ok) {
798             crm_debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), rc, *output);
799 
800             if (rc == -pcmk_err_old_data) {
801                 crm_trace("Masking error, we already have the supplied update");
802                 return pcmk_ok;
803             }
804             free_xml(*output); *output = NULL;
805 
806             return rc;
807         }
808     }
809 
810     return rc;
811 }
812 
813 /* v2 and v2 patch formats */
814 #define XPATH_CONFIG_CHANGE \
815     "//" XML_CIB_TAG_CRMCONFIG " | " \
816     "//" XML_DIFF_CHANGE "[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
817 
818 gboolean
cib_internal_config_changed(xmlNode * diff)819 cib_internal_config_changed(xmlNode *diff)
820 {
821     gboolean changed = FALSE;
822 
823     if (diff) {
824         xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
825 
826         if (numXpathResults(xpathObj) > 0) {
827             changed = TRUE;
828         }
829         freeXpathObject(xpathObj);
830     }
831     return changed;
832 }
833 
834 int
cib_internal_op(cib_t * cib,const char * op,const char * host,const char * section,xmlNode * data,xmlNode ** output_data,int call_options,const char * user_name)835 cib_internal_op(cib_t * cib, const char *op, const char *host,
836                 const char *section, xmlNode * data,
837                 xmlNode ** output_data, int call_options, const char *user_name)
838 {
839     int (*delegate) (cib_t * cib, const char *op, const char *host,
840                      const char *section, xmlNode * data,
841                      xmlNode ** output_data, int call_options, const char *user_name) =
842         cib->delegate_fn;
843 
844 #if ENABLE_ACL
845     if(user_name == NULL) {
846         user_name = getenv("CIB_user");
847     }
848 #endif
849 
850     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
851 }
852