1 /*
2 * Copyright 2004-2019 Andrew Beekhof <andrew@beekhof.net>
3 *
4 * This source code is licensed under the GNU Lesser General Public License
5 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6 */
7
8 #ifndef CRM_COMMON_XML__H
9 # define CRM_COMMON_XML__H
10
11 /**
12 * \file
13 * \brief Wrappers for and extensions to libxml2
14 * \ingroup core
15 */
16
17 # include <stdio.h>
18 # include <sys/types.h>
19 # include <unistd.h>
20
21 # include <stdlib.h>
22 # include <errno.h>
23 # include <fcntl.h>
24
25 # include <libxml/tree.h>
26 # include <libxml/xpath.h>
27
28 # include <crm/crm.h>
29 # include <crm/common/nvpair.h>
30
31 /* Define compression parameters for IPC messages
32 *
33 * Compression costs a LOT, so we don't want to do it unless we're hitting
34 * message limits. Currently, we use 128KB as the threshold, because higher
35 * values don't play well with the heartbeat stack. With an earlier limit of
36 * 10KB, compressing 184 of 1071 messages accounted for 23% of the total CPU
37 * used by the cib.
38 */
39 # define CRM_BZ2_BLOCKS 4
40 # define CRM_BZ2_WORK 20
41 # define CRM_BZ2_THRESHOLD 128 * 1024
42
43 # define XML_PARANOIA_CHECKS 0
44
45 gboolean add_message_xml(xmlNode * msg, const char *field, xmlNode * xml);
46 xmlNode *get_message_xml(xmlNode * msg, const char *field);
47
48 xmlDoc *getDocPtr(xmlNode * node);
49
50 /*
51 * Replacement function for xmlCopyPropList which at the very least,
52 * doesn't work the way *I* would expect it to.
53 *
54 * Copy all the attributes/properties from src into target.
55 *
56 * Not recursive, does not return anything.
57 *
58 */
59 void copy_in_properties(xmlNode * target, xmlNode * src);
60 void expand_plus_plus(xmlNode * target, const char *name, const char *value);
61 void fix_plus_plus_recursive(xmlNode * target);
62
63 /*
64 * Create a node named "name" as a child of "parent"
65 * If parent is NULL, creates an unconnected node.
66 *
67 * Returns the created node
68 *
69 */
70 xmlNode *create_xml_node(xmlNode * parent, const char *name);
71
72 /*
73 * Unlink the node and set its doc pointer to NULL so free_xml()
74 * will act appropriately
75 */
76 void unlink_xml_node(xmlNode * node);
77
78 /*
79 *
80 */
81 void purge_diff_markers(xmlNode * a_node);
82
83 /*
84 * Returns a deep copy of src_node
85 *
86 */
87 xmlNode *copy_xml(xmlNode * src_node);
88
89 /*
90 * Add a copy of xml_node to new_parent
91 */
92 xmlNode *add_node_copy(xmlNode * new_parent, xmlNode * xml_node);
93
94 int add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child);
95
96 /*
97 * XML I/O Functions
98 *
99 * Whitespace between tags is discarded.
100 */
101 xmlNode *filename2xml(const char *filename);
102
103 xmlNode *stdin2xml(void);
104
105 xmlNode *string2xml(const char *input);
106
107 int write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress);
108 int write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress);
109
110 char *dump_xml_formatted(xmlNode * msg);
111 /* Also dump the text node with xml_log_option_text enabled */
112 char *dump_xml_formatted_with_text(xmlNode * msg);
113
114 char *dump_xml_unformatted(xmlNode * msg);
115
116 /*
117 * Diff related Functions
118 */
119 xmlNode *diff_xml_object(xmlNode * left, xmlNode * right, gboolean suppress);
120
121 xmlNode *subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
122 gboolean full, gboolean * changed, const char *marker);
123
124 gboolean can_prune_leaf(xmlNode * xml_node);
125
126 void print_xml_diff(FILE * where, xmlNode * diff);
127
128 gboolean apply_xml_diff(xmlNode * old, xmlNode * diff, xmlNode ** new);
129
130 /*
131 * Searching & Modifying
132 */
133 xmlNode *find_xml_node(xmlNode * cib, const char *node_path, gboolean must_find);
134
135 xmlNode *find_entity(xmlNode * parent, const char *node_name, const char *id);
136
137 void xml_remove_prop(xmlNode * obj, const char *name);
138
139 gboolean replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update,
140 gboolean delete_only);
141
142 gboolean update_xml_child(xmlNode * child, xmlNode * to_update);
143
144 int find_xml_children(xmlNode ** children, xmlNode * root,
145 const char *tag, const char *field, const char *value,
146 gboolean search_matches);
147
148 xmlNode *get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level);
149 xmlNode *get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level);
150
151 static inline const char *
crm_element_name(const xmlNode * xml)152 crm_element_name(const xmlNode *xml)
153 {
154 return xml? (const char *)(xml->name) : NULL;
155 }
156
157 void xml_validate(const xmlNode * root);
158
159 gboolean xml_has_children(const xmlNode * root);
160
161 char *calculate_on_disk_digest(xmlNode * local_cib);
162 char *calculate_operation_digest(xmlNode * local_cib, const char *version);
163 char *calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter,
164 const char *version);
165
166 /* schema-related functions (from schemas.c) */
167 gboolean validate_xml(xmlNode * xml_blob, const char *validation, gboolean to_logs);
168 gboolean validate_xml_verbose(xmlNode * xml_blob);
169
170 /*!
171 * \brief Update CIB XML to most recent schema version
172 *
173 * "Update" means either actively employ XSLT-based transformation(s)
174 * (if intermediate product to transform valid per its declared schema version,
175 * transformation available, proceeded successfully with a result valid per
176 * expectated newer schema version), or just try to bump the marked validating
177 * schema until all gradually rising schema versions attested or the first
178 * such attempt subsequently fails to validate. Which of the two styles will
179 * be used depends on \p transform parameter (positive/negative, respectively).
180 *
181 * \param[in,out] xml_blob XML tree representing CIB, may be swapped with
182 * an "updated" one
183 * \param[out] best The highest configuration version (per its index
184 * in the global schemas table) it was possible to
185 * reach during the update steps while ensuring
186 * the validity of the result; if no validation
187 * success was observed against possibly multiple
188 * schemas, the value is less or equal the result
189 * of <tt>get_schema_version</tt> applied on the
190 * input \p xml_blob value (unless that function
191 * maps it to -1, then 0 would be used instead)
192 * \param[in] max When \p transform is positive, this allows to
193 * set upper boundary schema (per its index in the
194 * global schemas table) beyond which its forbidden
195 * to update by the means of XSLT transformation
196 * \param[in] transform Whether to employ XSLT-based transformation so
197 * as allow overcoming possible incompatibilities
198 * between major schema versions (see above)
199 * \param[in] to_logs If true, output notable progress info to
200 * internal log streams; if false, to stderr
201 *
202 * \return <tt>pcmk_ok</tt> if no non-recoverable error encountered (up to
203 * caller to evaluate if the update satisfies the requirements
204 * per returned \p best value), negative value carrying the reason
205 * otherwise
206 */
207 int update_validation(xmlNode **xml_blob, int *best, int max,
208 gboolean transform, gboolean to_logs);
209
210 int get_schema_version(const char *name);
211 const char *get_schema_name(int version);
212 const char *xml_latest_schema(void);
213 gboolean cli_config_update(xmlNode ** xml, int *best_version, gboolean to_logs);
214
215 void crm_xml_init(void);
216 void crm_xml_cleanup(void);
217
218 static inline xmlNode *
__xml_first_child(xmlNode * parent)219 __xml_first_child(xmlNode * parent)
220 {
221 xmlNode *child = NULL;
222
223 if (parent) {
224 child = parent->children;
225 while (child && child->type == XML_TEXT_NODE) {
226 child = child->next;
227 }
228 }
229 return child;
230 }
231
232 static inline xmlNode *
__xml_next(xmlNode * child)233 __xml_next(xmlNode * child)
234 {
235 if (child) {
236 child = child->next;
237 while (child && child->type == XML_TEXT_NODE) {
238 child = child->next;
239 }
240 }
241 return child;
242 }
243
244 static inline xmlNode *
__xml_first_child_element(xmlNode * parent)245 __xml_first_child_element(xmlNode * parent)
246 {
247 xmlNode *child = NULL;
248
249 if (parent) {
250 child = parent->children;
251 }
252
253 while (child) {
254 if(child->type == XML_ELEMENT_NODE) {
255 return child;
256 }
257 child = child->next;
258 }
259 return NULL;
260 }
261
262 static inline xmlNode *
__xml_next_element(xmlNode * child)263 __xml_next_element(xmlNode * child)
264 {
265 while (child) {
266 child = child->next;
267 if(child && child->type == XML_ELEMENT_NODE) {
268 return child;
269 }
270 }
271 return NULL;
272 }
273
274 void pcmk_free_xml_subtree(xmlNode *xml);
275 void free_xml(xmlNode * child);
276
277 xmlNode *first_named_child(xmlNode * parent, const char *name);
278 xmlNode *crm_next_same_xml(xmlNode *sibling);
279
280 xmlNode *sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive);
281 xmlXPathObjectPtr xpath_search(xmlNode * xml_top, const char *path);
282 void crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
283 void (*helper)(xmlNode*, void*), void *user_data);
284 xmlNode *expand_idref(xmlNode * input, xmlNode * top);
285
286 void freeXpathObject(xmlXPathObjectPtr xpathObj);
287 xmlNode *getXpathResult(xmlXPathObjectPtr xpathObj, int index);
288 void dedupXpathResults(xmlXPathObjectPtr xpathObj);
289
numXpathResults(xmlXPathObjectPtr xpathObj)290 static inline int numXpathResults(xmlXPathObjectPtr xpathObj)
291 {
292 if(xpathObj == NULL || xpathObj->nodesetval == NULL) {
293 return 0;
294 }
295 return xpathObj->nodesetval->nodeNr;
296 }
297
298 bool xml_acl_enabled(xmlNode *xml);
299 void xml_acl_disable(xmlNode *xml);
300 bool xml_acl_denied(xmlNode *xml); /* Part or all of a change was rejected */
301 bool xml_acl_filtered_copy(const char *user, xmlNode* acl_source, xmlNode *xml, xmlNode ** result);
302
303 bool xml_tracking_changes(xmlNode * xml);
304 bool xml_document_dirty(xmlNode *xml);
305 void xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls);
306 void xml_calculate_changes(xmlNode * old, xmlNode * new); /* For comparing two documents after the fact */
307 void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml);
308 void xml_accept_changes(xmlNode * xml);
309 void xml_log_changes(uint8_t level, const char *function, xmlNode *xml);
310 void xml_log_patchset(uint8_t level, const char *function, xmlNode *xml);
311 bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3]);
312
313 xmlNode *xml_create_patchset(
314 int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version);
315 int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version);
316
317 void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest);
318
319 void save_xml_to_file(xmlNode * xml, const char *desc, const char *filename);
320 char *xml_get_path(xmlNode *xml);
321
322 char * crm_xml_escape(const char *text);
323 void crm_xml_sanitize_id(char *id);
324 void crm_xml_set_id(xmlNode *xml, const char *format, ...)
325 __attribute__ ((__format__ (__printf__, 2, 3)));
326
327 /*!
328 * \brief xmlNode destructor which can be used in glib collections
329 */
330 void crm_destroy_xml(gpointer data);
331
332 #endif
333