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