xref: /titanic_41/usr/src/lib/libpool/common/pool_xml.c (revision 71269a2275bf5a143dad6461eee2710a344e7261)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <thread.h>
35 #include <time.h>
36 #include <unistd.h>
37 
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/utsname.h>
43 
44 #include <libxml/debugXML.h>
45 #include <libxml/parser.h>
46 #include <libxml/tree.h>
47 #include <libxml/xmlerror.h>
48 #include <libxml/xpath.h>
49 #include <libxml/xmlmemory.h>
50 
51 #include <pool.h>
52 #include "pool_internal.h"
53 #include "pool_impl.h"
54 #include "pool_xml_impl.h"
55 
56 /*
57  * libpool XML Manipulation Routines
58  *
59  * pool_xml.c implements the XML manipulation routines used by the libpool
60  * XML datastore. The functions are grouped into the following logical areas
61  * - Result Sets
62  * The XPath API is used to search the XML document represented by a
63  * configuration. The results of XPath queries are represented through
64  * pool_result_set_t structures as part of the abstraction of the datastore
65  * representation. (see pool.c comment for more details)
66  *
67  * - Property Manipulation
68  * Validated XML (XML associated with a DTD) does not allow the introduction
69  * of attributes which are not recognised by the DTD. This is a limitation
70  * since we want to allow libpool to associate an arbitrary number of
71  * properties with an element. The property manipulation code overcomes this
72  * limitation by allowing property sub-elements to be created and manipulated
73  * through a single API so that they are indistinguishable from attributes
74  * to the libpool user.
75  *
76  * - XML Element/Attribute Manipulation
77  * These routines manipulate XML elements and attributes and are the routines
78  * which interact most directly with libxml.
79  *
80  * - File Processing/IO
81  * Since libpool must present its data in a consistent fashion, we have to
82  * implement file locking above libxml. These routines allow us to lock files
83  * during processing and maintain data integrity between processes. Note
84  * that locks are at the process scope and are advisory (see fcntl).
85  *
86  * - Utilities
87  * Sundry utility functions that aren't easily categorised.
88  */
89 
90 #define	MAX_PROP_SIZE	1024	/* Size of property buffer */
91 /*
92  * The PAGE_READ_SIZE value is used to determine the size of the input buffer
93  * used to parse XML files.
94  */
95 #define	PAGE_READ_SIZE	8192
96 #define	ELEM_TYPE_COUNT	6	/* Count of Element types */
97 
98 typedef struct dtype_tbl
99 {
100 	xmlChar *dt_name;
101 	int dt_type;
102 } dtype_tbl_t;
103 
104 typedef struct elem_type_tbl
105 {
106 	xmlChar *ett_elem;
107 	dtype_tbl_t (*ett_dtype)[];
108 } elem_type_tbl_t;
109 
110 extern int xmlDoValidityCheckingDefaultValue;
111 
112 /*
113  * The _xml_lock is used to lock the state of libpool during
114  * xml initialisation operations.
115  */
116 static mutex_t _xml_lock;
117 
118 const char *element_class_tags[] = {
119 	"any",
120 	"system",
121 	"pool",
122 	"res_comp",
123 	"res_agg",
124 	"comp",
125 	NULL
126 };
127 
128 static const char *data_type_tags[] = {
129 	"uint",
130 	"int",
131 	"float",
132 	"boolean",
133 	"string"
134 };
135 
136 const char *dtd_location = "file:///usr/share/lib/xml/dtd/rm_pool.dtd.1";
137 
138 static elem_type_tbl_t elem_tbl[ELEM_TYPE_COUNT] = {0};
139 
140 /* libpool initialisation indicator */
141 static int _libpool_xml_initialised = PO_FALSE;
142 
143 /*
144  * Utility functions
145  */
146 /*
147  * Those functions which are not static are shared with pool_kernel.c
148  * They provide the required XML support for exporting a kernel
149  * configuration as an XML document.
150  */
151 void xml_init(void);
152 static int create_shadow(xmlNodePtr node);
153 static int pool_xml_free_doc(pool_conf_t *conf);
154 static int prop_sort(const void *a, const void *b);
155 static int dtd_exists(const char *path);
156 static void build_dtype_accelerator(void);
157 static dtype_tbl_t (*build_dtype_tbl(const xmlChar *rawdata))[];
158 static int get_fast_dtype(xmlNodePtr node, xmlChar *name);
159 static int pool_assoc_default_resource_type(pool_t *,
160     pool_resource_elem_class_t);
161 
162 /*
163  * XML Data access and navigation APIs
164  */
165 static int pool_build_xpath_buf(pool_xml_connection_t *, const pool_elem_t *,
166     pool_elem_class_t, pool_value_t **, char_buf_t *, int);
167 /*
168  * SHARED WITH pool_kernel.c for XML export support
169  */
170 xmlNodePtr node_create(xmlNodePtr parent, const xmlChar *name);
171 static xmlNodePtr node_create_with_id(xmlNodePtr parent, const xmlChar *name);
172 
173 /* Configuration */
174 static int pool_xml_close(pool_conf_t *);
175 static int pool_xml_validate(const pool_conf_t *, pool_valid_level_t);
176 static int pool_xml_commit(pool_conf_t *conf);
177 static int pool_xml_export(const pool_conf_t *conf, const char *location,
178     pool_export_format_t fmt);
179 static int pool_xml_rollback(pool_conf_t *conf);
180 static pool_result_set_t *pool_xml_exec_query(const pool_conf_t *conf,
181     const pool_elem_t *src, const char *src_attr,
182     pool_elem_class_t classes, pool_value_t **props);
183 static int pool_xml_remove(pool_conf_t *conf);
184 static int pool_xml_res_transfer(pool_resource_t *, pool_resource_t *,
185     uint64_t);
186 static int pool_xml_res_xtransfer(pool_resource_t *, pool_resource_t *,
187     pool_component_t **);
188 
189 /* Connections */
190 static void pool_xml_connection_free(pool_xml_connection_t *prov);
191 
192 /* Result Sets */
193 static pool_xml_result_set_t *pool_xml_result_set_alloc(const pool_conf_t *);
194 static void pool_xml_result_set_free(pool_xml_result_set_t *rs);
195 static pool_elem_t *pool_xml_rs_next(pool_result_set_t *set);
196 static pool_elem_t *pool_xml_rs_prev(pool_result_set_t *set);
197 static pool_elem_t *pool_xml_rs_first(pool_result_set_t *set);
198 static pool_elem_t *pool_xml_rs_last(pool_result_set_t *set);
199 static int pool_xml_rs_set_index(pool_result_set_t *set, int index);
200 static int pool_xml_rs_get_index(pool_result_set_t *set);
201 static int pool_xml_rs_count(pool_result_set_t *set);
202 static int pool_xml_rs_close(pool_result_set_t *set);
203 
204 /* Element (and sub-type) */
205 static void pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem,
206     pool_elem_class_t, pool_resource_elem_class_t, pool_component_elem_class_t);
207 static int pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class,
208     pool_resource_elem_class_t, pool_component_elem_class_t);
209 static pool_elem_t *pool_xml_elem_create(pool_conf_t *, pool_elem_class_t,
210     pool_resource_elem_class_t, pool_component_elem_class_t);
211 static int pool_xml_elem_remove(pool_elem_t *pe);
212 static int pool_xml_set_container(pool_elem_t *, pool_elem_t *);
213 static pool_elem_t *pool_xml_get_container(const pool_elem_t *);
214 
215 /*
216  * Pool element specific
217  */
218 static int pool_xml_pool_associate(pool_t *, const pool_resource_t *);
219 static int pool_xml_pool_dissociate(pool_t *, const pool_resource_t *);
220 
221 /*
222  * Resource elements specific
223  */
224 static int pool_xml_resource_is_system(const pool_resource_t *);
225 static int pool_xml_resource_can_associate(const pool_resource_t *);
226 
227 /* Properties */
228 static pool_value_class_t pool_xml_get_property(const pool_elem_t *,
229     const char *, pool_value_t *);
230 static int pool_xml_put_property(pool_elem_t *, const char *,
231     const pool_value_t *);
232 static int pool_xml_rm_property(pool_elem_t *, const char *);
233 static xmlNodePtr property_create(xmlNodePtr, const char *,
234     pool_value_class_t);
235 
236 /* Internal Attribute/Property manipulation */
237 static int pool_is_xml_attr(xmlDocPtr, const char *, const char *);
238 static pool_value_class_t pool_xml_get_attr(xmlNodePtr node, xmlChar *name,
239     pool_value_t *value);
240 int pool_xml_set_attr(xmlNodePtr node, xmlChar *name,
241     const pool_value_t *value);
242 static pool_value_class_t pool_xml_get_prop(xmlNodePtr node, xmlChar *name,
243     pool_value_t *value);
244 int pool_xml_set_prop(xmlNodePtr node, xmlChar *name,
245     const pool_value_t *value);
246 static pool_value_t **pool_xml_get_properties(const pool_elem_t *, uint_t *);
247 /* XML Error handling */
248 void pool_error_func(void *ctx, const char *msg, ...);
249 
250 /* XML File Input Processing */
251 static int pool_xml_open_file(pool_conf_t *conf);
252 static int pool_xml_parse_document(pool_conf_t *);
253 
254 /*
255  * Initialise this module
256  */
257 void
258 xml_init()
259 {
260 	(void) mutex_lock(&_xml_lock);
261 	if (_libpool_xml_initialised == PO_TRUE) {
262 		(void) mutex_unlock(&_xml_lock);
263 		return;
264 	}
265 	xmlInitParser();
266 
267 	/*
268 	 * DTD validation, with line numbers.
269 	 */
270 	xmlLineNumbersDefault(1);
271 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
272 	xmlDoValidityCheckingDefaultValue = 1;
273 	/* Try to improve indentation and readability */
274 	xmlKeepBlanksDefault(0);
275 	/* Send all XML errors to our debug handler */
276 	xmlSetGenericErrorFunc(NULL, pool_error_func);
277 	/* Load up DTD element a-dtype data to improve performance */
278 	build_dtype_accelerator();
279 	_libpool_xml_initialised = PO_TRUE;
280 	(void) mutex_unlock(&_xml_lock);
281 }
282 
283 /*
284  * Get the next ID for this configuration
285  */
286 static int
287 get_unique_id(xmlNodePtr node, char *id)
288 {
289 	pool_value_t val = POOL_VALUE_INITIALIZER;
290 	uint64_t nid = 0;
291 	if (node->doc->_private) {
292 		if (pool_get_ns_property(
293 		    pool_conf_to_elem((pool_conf_t *)node->doc->_private),
294 		    "_next_id", &val) == POC_UINT)
295 			(void) pool_value_get_uint64(&val, &nid);
296 	}
297 	if (snprintf(id, KEY_BUFFER_SIZE, "id_%llx", nid) > KEY_BUFFER_SIZE) {
298 		pool_seterror(POE_SYSTEM);
299 		return (PO_FAIL);
300 	}
301 	pool_value_set_uint64(&val, ++nid);
302 	return (pool_put_ns_property(
303 	    pool_conf_to_elem((pool_conf_t *)node->doc->_private), "_next_id",
304 	    &val));
305 }
306 
307 /* Document building functions */
308 
309 /*
310  * node_create() creates a child node of type name of the supplied parent in
311  * the supplied document. If the parent or document is NULL, create the node
312  * but do not associate it with a parent or document.
313  */
314 xmlNodePtr
315 node_create(xmlNodePtr parent, const xmlChar *name)
316 {
317 	xmlNodePtr node;
318 
319 	if (parent == NULL)
320 		node = xmlNewNode(NULL, name);
321 	else
322 		node = xmlNewChild(parent, NULL, name, NULL);
323 	return (node);
324 }
325 
326 /*
327  * node_create_with_id() creates a child node of type name of the supplied
328  * parent with the ref_id generated by get_unique_id(). Actual node creation
329  * is performed by node_create() and this function just sets the ref_id
330  * property to the value of the id.
331  */
332 static xmlNodePtr
333 node_create_with_id(xmlNodePtr parent, const xmlChar *name)
334 {
335 	char id[KEY_BUFFER_SIZE]; /* Must be big enough for key below */
336 	xmlNodePtr node = node_create(parent, name);
337 	if (node != NULL) {
338 		if (get_unique_id(node, id) != PO_SUCCESS) {
339 			xmlUnlinkNode(node);
340 			xmlFreeNode(node); /* recurses all children */
341 			pool_seterror(POE_DATASTORE);
342 			return (NULL);
343 		}
344 		if (xmlSetProp(node, BAD_CAST c_ref_id, BAD_CAST id) == NULL) {
345 			xmlUnlinkNode(node);
346 			xmlFreeNode(node); /* recurses all children */
347 			pool_seterror(POE_DATASTORE);
348 			return (NULL);
349 		}
350 	}
351 	return (node);
352 }
353 
354 /* Supporting Data Conversion Routines */
355 
356 /* XML Parser Utility Functions */
357 
358 /*
359  * Handler for XML Errors. Called by libxml at libxml Error.
360  */
361 /*ARGSUSED*/
362 void
363 pool_error_func(void *ctx, const char *msg, ...)
364 {
365 	va_list ap;
366 
367 	va_start(ap, msg);
368 	do_dprintf(msg, ap);
369 	va_end(ap);
370 }
371 
372 /*
373  * Free the shadowed elements from within the supplied document and then
374  * free the document. This function should always be called when freeing
375  * a pool document to ensure that all "shadow" resources are reclaimed.
376  * Returns PO_SUCCESS/PO_FAIL
377  */
378 static int
379 pool_xml_free_doc(pool_conf_t *conf)
380 {
381 	/* Only do any of this if there is a document */
382 	if (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc != NULL) {
383 		pool_elem_t *pe;
384 		pool_result_set_t *rs;
385 		/* Delete all the "shadowed" children of the doc */
386 		rs = pool_exec_query(conf, NULL, NULL, PEC_QRY_ANY, NULL);
387 		if (rs == NULL) {
388 			pool_seterror(POE_INVALID_CONF);
389 			return (PO_FAIL);
390 		}
391 		for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
392 			/*
393 			 * Work out the element type and free the elem
394 			 */
395 			free(pe);
396 		}
397 		(void) pool_rs_close(rs);
398 		xmlFreeDoc(((pool_xml_connection_t *)conf->pc_prov)->pxc_doc);
399 	}
400 	((pool_xml_connection_t *)conf->pc_prov)->pxc_doc = NULL;
401 	return (PO_SUCCESS);
402 }
403 
404 /*
405  * Remove an element from the document. Note that only three types of elements
406  * can be removed, res, comp and pools. comp are moved around to the
407  * default res when a res is deleted.
408  * Returns PO_SUCCESS/PO_FAIL
409  */
410 static int
411 pool_xml_elem_remove(pool_elem_t *pe)
412 {
413 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
414 
415 	/*
416 	 * You can only destroy three elements: pools, resources and
417 	 * components.
418 	 */
419 	switch (pe->pe_class) {
420 	case PEC_POOL:
421 	case PEC_RES_COMP:
422 	case PEC_RES_AGG:
423 	case PEC_COMP:
424 		if (pxe->pxe_node) {
425 			xmlUnlinkNode(pxe->pxe_node);
426 			xmlFreeNode(pxe->pxe_node); /* recurses all children */
427 		}
428 		free(pxe);
429 		break;
430 	default:
431 		break;
432 	}
433 	return (PO_SUCCESS);
434 }
435 
436 /*
437  * Create a property element.
438  */
439 static xmlNodePtr
440 property_create(xmlNodePtr parent, const char *name, pool_value_class_t type)
441 {
442 
443 	xmlNodePtr element;
444 	pool_value_t val = POOL_VALUE_INITIALIZER;
445 
446 	if ((element = node_create(parent, BAD_CAST "property")) == NULL) {
447 		pool_seterror(POE_DATASTORE);
448 		return (NULL);
449 	}
450 	if (pool_value_set_string(&val, name) != PO_SUCCESS) {
451 		xmlFree(element);
452 		return (NULL);
453 	}
454 	(void) pool_xml_set_attr(element, BAD_CAST c_name, &val);
455 	if (pool_value_set_string(&val, data_type_tags[type]) != PO_SUCCESS) {
456 		xmlFree(element);
457 		return (NULL);
458 	}
459 	(void) pool_xml_set_attr(element, BAD_CAST c_type, &val);
460 	return (element);
461 }
462 
463 /*
464  * External clients need to be able to put/get properties and this is the
465  * way to do it.
466  * This function is an interceptor, since it will *always* try to manipulate
467  * an attribute first. If the attribute doesn't exist, then it will treat
468  * the request as a property request.
469  */
470 static pool_value_class_t
471 pool_xml_get_property(const pool_elem_t *pe, const char *name,
472     pool_value_t *val)
473 {
474 	pool_value_class_t type;
475 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
476 
477 	/*
478 	 * "type" is a special attribute which is not visible ever outside of
479 	 * libpool. Use the specific type accessor function.
480 	 */
481 	if (strcmp(name, c_type) == 0) {
482 		return (pool_xml_get_attr(pxe->pxe_node, BAD_CAST name,
483 		    val));
484 	}
485 	if (is_ns_property(pe, name) != NULL) {	/* in ns */
486 		if ((type = pool_xml_get_attr(pxe->pxe_node,
487 		    BAD_CAST property_name_minus_ns(pe, name), val))
488 		    == POC_INVAL)
489 			return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name,
490 			    val));
491 	} else
492 		return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name, val));
493 
494 	return (type);
495 }
496 
497 /*
498  * Put a property on an element. Check if the property is an attribute,
499  * if it is update that value. If not add a property element.
500  *
501  * There are three possible conditions here:
502  * - the name is a ns
503  *	- the name is an attribute
504  *	- the name isn't an attribute
505  * - the name is not a ns
506  * Returns PO_SUCCESS/PO_FAIL
507  */
508 static int
509 pool_xml_put_property(pool_elem_t *pe, const char *name,
510     const pool_value_t *val)
511 {
512 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
513 
514 	/*
515 	 * "type" is a special attribute which is not visible ever outside of
516 	 * libpool. Use the specific type accessor function.
517 	 */
518 	if (strcmp(name, c_type) == 0) {
519 		return (pool_xml_set_attr(pxe->pxe_node, BAD_CAST name,
520 		    val));
521 	}
522 	if (is_ns_property(pe, name) != NULL) {	/* in ns */
523 		if (pool_xml_set_attr(pxe->pxe_node,
524 		    BAD_CAST property_name_minus_ns(pe, name), val) == PO_FAIL)
525 			return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name,
526 			    val));
527 	} else
528 		return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name, val));
529 	return (PO_SUCCESS);
530 }
531 
532 /*
533  * Remove a property from an element. Check if the property is an attribute,
534  * if it is fail. Otherwise remove the property subelement.
535  * Returns PO_SUCCESS/PO_FAIL
536  */
537 static int
538 pool_xml_rm_property(pool_elem_t *pe, const char *name)
539 {
540 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
541 	xmlXPathContextPtr ctx;
542 	xmlXPathObjectPtr path;
543 	char buf[MAX_PROP_SIZE];
544 	int ret;
545 
546 	if (xmlHasProp(pxe->pxe_node, BAD_CAST name) != NULL) {
547 		pool_seterror(POE_BADPARAM);
548 		return (PO_FAIL);
549 	}
550 
551 	/* use xpath to find the node with the appropriate value for name */
552 	(void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name);
553 	if ((ctx = xmlXPathNewContext(pxe->pxe_node->doc)) == NULL) {
554 		pool_seterror(POE_PUTPROP);
555 		return (PO_FAIL);
556 	}
557 	ctx->node = pxe->pxe_node;
558 	path = xmlXPathEval(BAD_CAST buf, ctx);
559 
560 	if (path && (path->type == XPATH_NODESET) &&
561 	    (path->nodesetval->nodeNr == 1)) {
562 		xmlUnlinkNode(path->nodesetval->nodeTab[0]);
563 		xmlFreeNode(path->nodesetval->nodeTab[0]);
564 		ret = PO_SUCCESS;
565 	} else {
566 		pool_seterror(POE_BADPARAM);
567 		ret = PO_FAIL;
568 	}
569 	xmlXPathFreeObject(path);
570 	xmlXPathFreeContext(ctx);
571 	return (ret);
572 }
573 
574 /*
575  * Get the data type for an attribute name from the element node. The data
576  * type is returned and the value of the attribute updates the supplied value
577  * pointer.
578  */
579 static pool_value_class_t
580 pool_xml_get_attr(xmlNodePtr node, xmlChar *name, pool_value_t *value)
581 {
582 	pool_value_class_t data_type;
583 	xmlChar *data;
584 	uint64_t uval;
585 	int64_t ival;
586 
587 	if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc,
588 	    (const char *) node->name, (const char *) name) == PO_FALSE) {
589 		pool_seterror(POE_BADPARAM);
590 		return (POC_INVAL);
591 	}
592 	if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) {
593 		pool_seterror(POE_INVALID_CONF);
594 		return (POC_INVAL);
595 	}
596 	data = xmlGetProp(node, name);
597 	data_type = get_fast_dtype(node, name);
598 	if (data_type != POC_STRING && data == NULL) {
599 		pool_seterror(POE_INVALID_CONF);
600 		return (POC_INVAL);
601 	}
602 	switch (data_type) {
603 	case POC_UINT:
604 		errno = 0;
605 		uval = strtoull((char *)data, NULL, 0);
606 		if (errno != 0) {
607 			data_type =  POC_INVAL;
608 		}
609 		else
610 			pool_value_set_uint64(value, uval);
611 		break;
612 	case POC_INT:
613 		errno = 0;
614 		ival = strtoll((char *)data, NULL, 0);
615 		if (errno != 0) {
616 			data_type =  POC_INVAL;
617 		}
618 		else
619 			pool_value_set_int64(value, ival);
620 		break;
621 	case POC_DOUBLE:
622 		pool_value_set_double(value, atof((const char *)data));
623 		break;
624 	case POC_BOOL:
625 		if (strcmp((const char *)data, "true") == 0)
626 			pool_value_set_bool(value, PO_TRUE);
627 		else
628 			pool_value_set_bool(value, PO_FALSE);
629 		break;
630 	case POC_STRING:
631 		if (pool_value_set_string(value, data ?
632 		    (const char *)data : "") != PO_SUCCESS) {
633 			xmlFree(data);
634 			return (POC_INVAL);
635 		}
636 		break;
637 	case POC_INVAL:
638 	default:
639 		break;
640 	}
641 	xmlFree(data);
642 	return (data_type);
643 }
644 
645 /*
646  * Set the data type for an attribute name from the element node. The
647  * supplied value is used to update the designated name using the data
648  * type supplied.
649  */
650 int
651 pool_xml_set_attr(xmlNodePtr node, xmlChar *name, const pool_value_t *value)
652 {
653 	xmlChar buf[MAX_PROP_SIZE] = {0};
654 	uint64_t ures;
655 	int64_t ires;
656 	uchar_t bres;
657 	double dres;
658 	const char *sres;
659 
660 	pool_value_class_t data_type;
661 
662 	if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc,
663 	    (const char *) node->name, (const char *) name) == PO_FALSE) {
664 		pool_seterror(POE_BADPARAM);
665 		return (PO_FAIL);
666 	}
667 
668 	if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) {
669 		pool_seterror(POE_INVALID_CONF);
670 		return (PO_FAIL);
671 	}
672 	data_type = get_fast_dtype(node, name);
673 	if (data_type != value->pv_class) {
674 		pool_seterror(POE_BADPARAM);
675 		return (PO_FAIL);
676 	}
677 	switch (value->pv_class) {
678 	case POC_UINT:
679 		(void) pool_value_get_uint64(value, &ures);
680 		(void) snprintf((char *)buf, sizeof (buf), "%llu",
681 		    (u_longlong_t)ures);
682 		break;
683 	case POC_INT:
684 		(void) pool_value_get_int64(value, &ires);
685 		(void) snprintf((char *)buf, sizeof (buf), "%lld",
686 		    (longlong_t)ires);
687 		break;
688 	case POC_DOUBLE:
689 		(void) pool_value_get_double(value, &dres);
690 		(void) snprintf((char *)buf, sizeof (buf), "%f", dres);
691 		break;
692 	case POC_BOOL:
693 		(void) pool_value_get_bool(value, &bres);
694 		if (bres == PO_FALSE)
695 			(void) snprintf((char *)buf, sizeof (buf),
696 			    "false");
697 		else
698 			(void) snprintf((char *)buf, sizeof (buf),
699 			    "true");
700 		break;
701 	case POC_STRING:
702 		(void) pool_value_get_string(value, &sres);
703 		if (sres != NULL)
704 			(void) snprintf((char *)buf, sizeof (buf), "%s",
705 			    sres);
706 		break;
707 	case POC_INVAL:
708 	default:
709 		break;
710 	}
711 	if (xmlSetProp(node, name, buf) == NULL) {
712 		pool_seterror(POE_DATASTORE);
713 		return (PO_FAIL);
714 	}
715 	return (PO_SUCCESS);
716 }
717 
718 /*
719  * Get the data type for a property name from the element node. The data
720  * type is returned and the value of the property updates the supplied value
721  * pointer. The user is responsible for freeing the memory associated with
722  * a string.
723  */
724 static pool_value_class_t
725 pool_xml_get_prop(xmlNodePtr node, xmlChar *name, pool_value_t *value)
726 {
727 	pool_value_class_t data_type;
728 	xmlChar *data, *node_data;
729 	xmlXPathContextPtr ctx;
730 	xmlXPathObjectPtr path;
731 	char buf[MAX_PROP_SIZE];
732 	int64_t uval;
733 	int64_t ival;
734 
735 	/* use xpath to find the node with the appropriate value for name */
736 	(void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name);
737 	if ((ctx = xmlXPathNewContext(node->doc)) == NULL) {
738 		pool_seterror(POE_BADPARAM);
739 		return (POC_INVAL);
740 	}
741 	ctx->node = node;
742 	path = xmlXPathEval(BAD_CAST buf, ctx);
743 
744 	if (path && (path->type == XPATH_NODESET) &&
745 	    (path->nodesetval->nodeNr == 1)) {
746 		int i;
747 		if (xmlHasProp(path->nodesetval->nodeTab[0],
748 		    BAD_CAST c_type) == NULL) {
749 			xmlXPathFreeObject(path);
750 			xmlXPathFreeContext(ctx);
751 			pool_seterror(POE_INVALID_CONF);
752 			return (POC_INVAL);
753 		}
754 		/* type is a string representation of the type */
755 		data = xmlGetProp(path->nodesetval->nodeTab[0],
756 		    BAD_CAST c_type);
757 		node_data = xmlNodeGetContent(path->nodesetval->nodeTab[0]);
758 		data_type = POC_INVAL;
759 		for (i = 0; i < (sizeof (data_type_tags) /
760 		    sizeof (data_type_tags[0])); i++) {
761 			if (strcmp((char *)data, data_type_tags[i]) == 0) {
762 				data_type = i;
763 				break;
764 			}
765 		}
766 		switch (data_type) {
767 		case POC_UINT:
768 			errno = 0;
769 			uval = strtoull((char *)node_data, NULL, 0);
770 			if (errno != 0)
771 				data_type =  POC_INVAL;
772 			else
773 				pool_value_set_uint64(value, uval);
774 			break;
775 		case POC_INT:
776 			errno = 0;
777 			ival = strtoll((char *)node_data, NULL, 0);
778 			if (errno != 0)
779 				data_type =  POC_INVAL;
780 			else
781 				pool_value_set_int64(value, ival);
782 			break;
783 		case POC_DOUBLE:
784 			pool_value_set_double(value,
785 			    atof((const char *)node_data));
786 			break;
787 		case POC_BOOL:
788 			if (strcmp((const char *)node_data, "true")
789 			    == 0)
790 				pool_value_set_bool(value, PO_TRUE);
791 			else
792 				pool_value_set_bool(value, PO_FALSE);
793 			break;
794 		case POC_STRING:
795 			if (pool_value_set_string(value,
796 			    (const char *)node_data) != PO_SUCCESS) {
797 				data_type = POC_INVAL;
798 				break;
799 			}
800 			break;
801 		case POC_INVAL:
802 		default:
803 			break;
804 		}
805 		xmlFree(data);
806 		xmlFree(node_data);
807 		xmlXPathFreeObject(path);
808 		xmlXPathFreeContext(ctx);
809 		return (data_type);
810 	} else { /* No property exists, clean up and return */
811 		xmlXPathFreeObject(path);
812 		xmlXPathFreeContext(ctx);
813 		pool_seterror(POE_BADPARAM);
814 		return (POC_INVAL);
815 	}
816 }
817 
818 /*
819  * Set the data type for a property name from the element node. The
820  * supplied value is used to update the designated name using the data
821  * type supplied.
822  */
823 int
824 pool_xml_set_prop(xmlNodePtr node, xmlChar *name, const pool_value_t *value)
825 {
826 /* First check if we have a property with this name (and type???). */
827 	xmlXPathContextPtr ctx;
828 	xmlXPathObjectPtr path;
829 	xmlChar buf[MAX_PROP_SIZE];
830 	xmlNodePtr element;
831 	uint64_t ures;
832 	int64_t ires;
833 	uchar_t bres;
834 	double dres;
835 	const char *sres;
836 
837 	/* use xpath to find the node with the appropriate value for name */
838 	(void) snprintf((char *)buf, sizeof (buf), "property[@name=\"%s\"]",
839 	    name);
840 	if ((ctx = xmlXPathNewContext(node->doc)) == NULL) {
841 		pool_seterror(POE_BADPARAM);
842 		return (PO_FAIL);
843 	}
844 	ctx->node = node;
845 	path = xmlXPathEval(buf, ctx);
846 	if (path == NULL || path->type != XPATH_NODESET) {
847 		xmlXPathFreeObject(path);
848 		xmlXPathFreeContext(ctx);
849 		pool_seterror(POE_BADPARAM);
850 		return (PO_FAIL);
851 	} else {
852 		if (path->nodesetval->nodeNr == 0)
853 			element = property_create
854 			    (node, (const char *)name, value->pv_class);
855 		else if (path->nodesetval->nodeNr == 1) {
856 			int i;
857 			xmlChar *data;
858 
859 			element = path->nodesetval->nodeTab[0];
860 			if (xmlHasProp(element, BAD_CAST c_type) == NULL) {
861 				xmlXPathFreeObject(path);
862 				xmlXPathFreeContext(ctx);
863 				pool_seterror(POE_INVALID_CONF);
864 				return (PO_FAIL);
865 			}
866 			data = xmlGetProp(element, BAD_CAST c_type);
867 			for (i = 0; i < (sizeof (data_type_tags) /
868 			    sizeof (data_type_tags[0])); i++)
869 				if (strcmp((char *)data, data_type_tags[i])
870 				    == 0) {
871 					break;
872 				}
873 			xmlFree(data);
874 			if (value->pv_class != i) {
875 				xmlXPathFreeObject(path);
876 				xmlXPathFreeContext(ctx);
877 				pool_seterror(POE_BADPARAM);
878 				return (PO_FAIL);
879 			}
880 		} else {
881 			xmlXPathFreeObject(path);
882 			xmlXPathFreeContext(ctx);
883 			pool_seterror(POE_BADPARAM);
884 			return (PO_FAIL);
885 		}
886 	}
887 
888 	switch (value->pv_class) {
889 	case POC_UINT:
890 		(void) pool_value_get_uint64(value, &ures);
891 		(void) snprintf((char *)buf, sizeof (buf), "%llu",
892 		    (u_longlong_t)ures);
893 		break;
894 	case POC_INT:
895 		(void) pool_value_get_int64(value, &ires);
896 		(void) snprintf((char *)buf, sizeof (buf), "%lld",
897 		    (longlong_t)ires);
898 		break;
899 	case POC_DOUBLE:
900 		(void) pool_value_get_double(value, &dres);
901 		(void) snprintf((char *)buf, sizeof (buf), "%f", dres);
902 		break;
903 	case POC_BOOL:
904 		(void) pool_value_get_bool(value, &bres);
905 		if (bres == PO_FALSE)
906 			(void) snprintf((char *)buf, sizeof (buf),
907 			    "false");
908 		else
909 			(void) snprintf((char *)buf, sizeof (buf),
910 			    "true");
911 		break;
912 	case POC_STRING:
913 		(void) pool_value_get_string(value, &sres);
914 		(void) snprintf((char *)buf, sizeof (buf), "%s", sres);
915 		break;
916 	case POC_INVAL:
917 	default:
918 		break;
919 	}
920 	xmlNodeSetContent(element, buf);
921 	xmlXPathFreeObject(path);
922 	xmlXPathFreeContext(ctx);
923 	return (PO_SUCCESS);
924 }
925 
926 /*
927  * Return a NULL terminated array of pool_value_t which represents all
928  * of the properties stored for an element
929  *
930  * Return NULL on failure. It is the caller's responsibility to free
931  * the returned array of values.
932  */
933 pool_value_t **
934 pool_xml_get_properties(const pool_elem_t *pe, uint_t *nprops)
935 {
936 	pool_value_t **result;
937 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe;
938 	int i, j;
939 	pool_conf_t *conf = TO_CONF(pe);
940 	xmlElementPtr elemDTD;
941 	xmlAttributePtr attr;
942 	xmlXPathContextPtr ctx;
943 	xmlXPathObjectPtr path;
944 	char_buf_t *cb = NULL;
945 
946 	*nprops = 0;
947 
948 	elemDTD = xmlGetDtdElementDesc(pxe->pxe_node->doc->extSubset,
949 	    pxe->pxe_node->name);
950 	for (attr = elemDTD->attributes; attr != NULL; attr = attr->nexth) {
951 		if (strcmp((const char *)attr->name, c_a_dtype) != 0 ||
952 		    strcmp((const char *)attr->name, c_type) != 0)
953 			(*nprops)++;
954 	}
955 	if ((ctx = xmlXPathNewContext(
956 	    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) {
957 		pool_seterror(POE_BADPARAM);
958 		return (NULL);
959 	}
960 	ctx->node = pxe->pxe_node;
961 	path = xmlXPathEval(BAD_CAST "property", ctx);
962 
963 	if (path != NULL && path->type == XPATH_NODESET &&
964 	    path->nodesetval != NULL)
965 		(*nprops) += path->nodesetval->nodeNr;
966 
967 	if ((result = calloc(*nprops + 1, sizeof (pool_value_t *))) == NULL) {
968 		xmlXPathFreeObject(path);
969 		xmlXPathFreeContext(ctx);
970 		pool_seterror(POE_SYSTEM);
971 		return (NULL);
972 	}
973 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) {
974 		xmlXPathFreeObject(path);
975 		xmlXPathFreeContext(ctx);
976 		free(result);
977 		return (NULL);
978 	}
979 	/*
980 	 * Now store our attributes and properties in result
981 	 */
982 	for (i = 0, attr = elemDTD->attributes; attr != NULL;
983 	    attr = attr->nexth, i++) {
984 		if (strcmp((const char *)attr->name, c_a_dtype) == 0 ||
985 		    strcmp((const char *)attr->name, c_type) == 0) {
986 			i--;
987 			continue;
988 		}
989 		result[i] = pool_value_alloc();
990 		if (pool_xml_get_attr(pxe->pxe_node,
991 		    BAD_CAST attr->name, result[i]) == POC_INVAL) {
992 			xmlXPathFreeObject(path);
993 			xmlXPathFreeContext(ctx);
994 			while (i-- >= 0)
995 				pool_value_free(result[i]);
996 			free(result);
997 			free_char_buf(cb);
998 			return (NULL);
999 		}
1000 		if (strcmp((const char *)attr->name, c_type) != 0) {
1001 			if (set_char_buf(cb, "%s.%s",
1002 			    pool_elem_class_string(pe), attr->name) !=
1003 			    PO_SUCCESS) {
1004 				xmlXPathFreeObject(path);
1005 				xmlXPathFreeContext(ctx);
1006 				while (i-- >= 0)
1007 					pool_value_free(result[i]);
1008 				free(result);
1009 				free_char_buf(cb);
1010 				return (NULL);
1011 			}
1012 			if (pool_value_set_name(result[i], cb->cb_buf) !=
1013 			    PO_SUCCESS) {
1014 				xmlXPathFreeObject(path);
1015 				xmlXPathFreeContext(ctx);
1016 				while (i-- >= 0)
1017 					pool_value_free(result[i]);
1018 				free(result);
1019 				free_char_buf(cb);
1020 				return (NULL);
1021 			}
1022 		} else {
1023 			if (pool_value_set_name(result[i],
1024 			    (const char *)attr->name) != PO_SUCCESS) {
1025 				xmlXPathFreeObject(path);
1026 				xmlXPathFreeContext(ctx);
1027 				while (i-- >= 0)
1028 					pool_value_free(result[i]);
1029 				free(result);
1030 				free_char_buf(cb);
1031 				return (NULL);
1032 			}
1033 		}
1034 	}
1035 	free_char_buf(cb);
1036 	for (j = 0; j < path->nodesetval->nodeNr; j++, i++) {
1037 		xmlChar *name = xmlGetProp(path->nodesetval->nodeTab[j],
1038 		    BAD_CAST c_name);
1039 
1040 		result[i] = pool_value_alloc();
1041 
1042 		if (pool_xml_get_prop(pxe->pxe_node, name, result[i]) ==
1043 		    POC_INVAL) {
1044 			xmlFree(name);
1045 			xmlXPathFreeObject(path);
1046 			xmlXPathFreeContext(ctx);
1047 			while (i-- >= 0)
1048 				pool_value_free(result[i]);
1049 			free(result);
1050 			return (NULL);
1051 		}
1052 		if (pool_value_set_name(result[i], (const char *)name) !=
1053 		    PO_SUCCESS) {
1054 			xmlFree(name);
1055 			xmlXPathFreeObject(path);
1056 			xmlXPathFreeContext(ctx);
1057 			while (i-- >= 0)
1058 				pool_value_free(result[i]);
1059 			free(result);
1060 			return (NULL);
1061 		}
1062 		xmlFree(name);
1063 	}
1064 	xmlXPathFreeObject(path);
1065 	xmlXPathFreeContext(ctx);
1066 	return (result);
1067 }
1068 
1069 /*
1070  * Store a pointer to one of our data types in the _private member of each
1071  * XML data node contained within the passed node. Note this function is
1072  * recursive and so all sub-nodes are also shadowed. Only shadow the nodes
1073  * which we are interested in, i.e. system, pool, res and comp
1074  */
1075 static int
1076 create_shadow(xmlNodePtr node)
1077 {
1078 	xmlNodePtr sib;
1079 	int ret = PO_SUCCESS;
1080 	/* Create a data structure of the appropriate type */
1081 
1082 	if (0 == (xmlStrcmp(node->name,
1083 	    BAD_CAST element_class_tags[PEC_SYSTEM]))) {
1084 		ret = pool_xml_elem_wrap(node, PEC_SYSTEM, PREC_INVALID,
1085 		    PCEC_INVALID);
1086 	} else if (0 == (xmlStrcmp(node->name,
1087 	    BAD_CAST element_class_tags[PEC_POOL]))) {
1088 		ret = pool_xml_elem_wrap(node, PEC_POOL, PREC_INVALID,
1089 		    PCEC_INVALID);
1090 	} else if (0 == (xmlStrcmp(node->name,
1091 	    BAD_CAST element_class_tags[PEC_RES_COMP]))) {
1092 		xmlChar *data;
1093 		pool_resource_elem_class_t res_class;
1094 		data = xmlGetProp(node, BAD_CAST c_type);
1095 
1096 		res_class = pool_resource_elem_class_from_string((char *)data);
1097 		xmlFree(data);
1098 		ret = pool_xml_elem_wrap(node, PEC_RES_COMP, res_class,
1099 		    PCEC_INVALID);
1100 	} else if (0 == (xmlStrcmp(node->name,
1101 	    BAD_CAST element_class_tags[PEC_RES_AGG]))) {
1102 		xmlChar *data;
1103 		pool_resource_elem_class_t res_class;
1104 		data = xmlGetProp(node, BAD_CAST c_type);
1105 
1106 		res_class = pool_resource_elem_class_from_string((char *)data);
1107 		xmlFree(data);
1108 		ret = pool_xml_elem_wrap(node, PEC_RES_AGG, res_class,
1109 		    PCEC_INVALID);
1110 	} else if (0 == (xmlStrcmp(node->name,
1111 	    BAD_CAST element_class_tags[PEC_COMP]))) {
1112 		xmlChar *data;
1113 		pool_component_elem_class_t comp_class;
1114 		data = xmlGetProp(node, BAD_CAST c_type);
1115 
1116 		comp_class = pool_component_elem_class_from_string(
1117 		    (char *)data);
1118 		xmlFree(data);
1119 		ret = pool_xml_elem_wrap(node, PEC_COMP, PREC_INVALID,
1120 		    comp_class);
1121 	}
1122 	/* Have to shadow all children and all siblings */
1123 	for (sib = node->children; sib != NULL; sib = sib->next) {
1124 		if ((ret = create_shadow(sib)) != PO_SUCCESS)
1125 			break;
1126 	}
1127 	return (ret);
1128 }
1129 
1130 
1131 /*
1132  * XML Data access and navigation APIs
1133  */
1134 
1135 /*
1136  * Close the configuration. There are a few steps to closing a configuration:
1137  * - Unlock the backing file (if there is one)
1138  * - Close the file (if there is one)
1139  * - Free the shadow memory	}Done in pool_xml_free_doc
1140  * - Free the document		}
1141  * - Free the data provider for this configuration
1142  * - Free the configuration location specifier
1143  * Returns PO_SUCCESS/PO_FAIL
1144  */
1145 static int
1146 pool_xml_close(pool_conf_t *conf)
1147 {
1148 	pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov;
1149 	int ret = PO_SUCCESS;
1150 
1151 	if (pxc->pxc_file != NULL) {
1152 		/* Close (and implicitly) unlock the file */
1153 		if (fclose(pxc->pxc_file) != 0) {
1154 			pool_seterror(POE_SYSTEM);
1155 			ret = PO_FAIL;
1156 		}
1157 		pxc->pxc_file = NULL;
1158 	}
1159 	/* Close the xml specific parts */
1160 	(void) pool_xml_free_doc(conf);
1161 	pool_xml_connection_free((pool_xml_connection_t *)conf->pc_prov);
1162 	return (ret);
1163 }
1164 
1165 /*
1166  * Remove the configuration from the backing store. In XML terms delete
1167  * the file backing the configuration. You need a copy of the location
1168  * since the pool_conf_close function, frees the location.
1169  * Returns PO_SUCCESS/PO_FAIL
1170  */
1171 static int
1172 pool_xml_remove(pool_conf_t *conf)
1173 {
1174 	if (pool_conf_location(conf) != NULL) {
1175 		/* First unlink the file, to prevent races on open */
1176 		if (unlink(pool_conf_location(conf)) != 0) {
1177 			pool_seterror(POE_SYSTEM);
1178 			return (PO_FAIL);
1179 		}
1180 		/* Now close the configuration */
1181 		(void) pool_conf_close(conf);
1182 		return (PO_SUCCESS);
1183 	}
1184 	return (PO_FAIL);
1185 }
1186 
1187 /*
1188  * Validate the configuration. There are three levels of validation, loose,
1189  * strict and runtime. In this, XML, implementation, loose is mapped to XML
1190  * validation, strict implements additional application level validation
1191  * checks, e.g. all pools must have unique names, runtime ensures that this
1192  * configuration would instantiate on the current system.
1193  *
1194  * Returns PO_SUCCESS/PO_FAIL
1195  */
1196 static int
1197 pool_xml_validate(const pool_conf_t *conf, pool_valid_level_t level)
1198 {
1199 	pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov;
1200 	xmlValidCtxtPtr cvp;
1201 
1202 	if ((cvp = xmlNewValidCtxt()) == NULL) {
1203 		pool_seterror(POE_INVALID_CONF);
1204 		return (PO_FAIL);
1205 	}
1206 	cvp->error    = pool_error_func;
1207 	cvp->warning  = pool_error_func;
1208 
1209 	if (xmlValidateDocument(cvp, pxc->pxc_doc) == 0) {
1210 		xmlFreeValidCtxt(cvp);
1211 		pool_seterror(POE_INVALID_CONF);
1212 		return (PO_FAIL);
1213 	}
1214 	xmlFreeValidCtxt(cvp);
1215 
1216 	if (level >= POV_RUNTIME) {
1217 		/*
1218 		 * Note: This is resource specific.
1219 		 */
1220 		return (((pool_validate_resource(conf, "pset", c_min_prop, 0) ==
1221 			    PO_SUCCESS) &&
1222 			(pool_validate_resource(conf, "pset", c_max_prop, 0) ==
1223 			    PO_SUCCESS)) ? PO_SUCCESS : PO_FAIL);
1224 	}
1225 	return (PO_SUCCESS);
1226 }
1227 
1228 /*
1229  * Commit the configuration to the backing store. In XML terms this means
1230  * write the changes to the backing file. Read the comments below for details
1231  * on exactly how this operation is performed.
1232  * Returns PO_SUCCESS/PO_FAIL
1233  */
1234 static int
1235 pool_xml_commit(pool_conf_t *conf)
1236 {
1237 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
1238 	xmlOutputBufferPtr buf;
1239 
1240 	/*
1241 	 * Ensure that the configuration file has no contents
1242 	 */
1243 	if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) {
1244 		pool_seterror(POE_SYSTEM);
1245 		return (PO_FAIL);
1246 	}
1247 
1248 	if (ftruncate(fileno(prov->pxc_file), 0) == -1) {
1249 		pool_seterror(POE_SYSTEM);
1250 		return (PO_FAIL);
1251 	}
1252 	/*
1253 	 * Create an XML output buffer and write out the contents of the
1254 	 * configuration to the file.
1255 	 */
1256 	if ((buf = xmlOutputBufferCreateFile(prov->pxc_file, NULL)) == NULL) {
1257 		pool_seterror(POE_DATASTORE);
1258 		return (PO_FAIL);
1259 	}
1260 
1261 	if (xmlSaveFormatFileTo(buf, prov->pxc_doc, NULL, 1) == -1) {
1262 		pool_seterror(POE_DATASTORE);
1263 		return (PO_FAIL);
1264 	}
1265 
1266 	return (PO_SUCCESS);
1267 }
1268 
1269 /*
1270  * Export the configuration in the specified format to the specified location.
1271  * The only format implemented now is the native format, which saves the
1272  * active configuration to the supplied location.
1273  * Returns PO_SUCCESS/PO_FAIL
1274  */
1275 static int
1276 pool_xml_export(const pool_conf_t *conf, const char *location,
1277     pool_export_format_t fmt)
1278 {
1279 	int ret;
1280 
1281 	switch (fmt) {
1282 	case POX_NATIVE:
1283 		ret = xmlSaveFormatFile(location,
1284 		    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc,
1285 		    1);
1286 		if (ret == -1) {
1287 			pool_seterror(POE_SYSTEM);
1288 			return (PO_FAIL);
1289 		} else
1290 			return (PO_SUCCESS);
1291 
1292 	default:
1293 		pool_seterror(POE_BADPARAM);
1294 		return (PO_FAIL);
1295 	}
1296 }
1297 
1298 /*
1299  * Discard the configuration and restore the configuration to the values
1300  * specified in the configuration location.
1301  * Returns PO_SUCCESS/PO_FAIL
1302  */
1303 static int
1304 pool_xml_rollback(pool_conf_t *conf)
1305 {
1306 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
1307 
1308 	/* Rollback the file pointer ready for the reparse */
1309 	if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) {
1310 		pool_seterror(POE_SYSTEM);
1311 		return (PO_FAIL);
1312 	}
1313 	/* Reparse the document */
1314 	/* In XML terms this means, discard and reparse the document */
1315 	(void) pool_xml_free_doc(conf);
1316 	if (pool_xml_parse_document(conf) == PO_FAIL)
1317 		return (PO_FAIL);
1318 	return (PO_SUCCESS);
1319 }
1320 
1321 /*
1322  * Allocate a new pool_elem_t in the supplied configuration of the specified
1323  * class.
1324  * Returns element pointer/NULL
1325  */
1326 static void
1327 pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem,
1328     pool_elem_class_t class, pool_resource_elem_class_t res_class,
1329     pool_component_elem_class_t comp_class)
1330 {
1331 	pool_elem_t *pe = TO_ELEM(elem);
1332 	pe->pe_conf = conf;
1333 	pe->pe_class = class;
1334 	pe->pe_resource_class = res_class;
1335 	pe->pe_component_class = comp_class;
1336 	/* Set up the function pointers for element manipulation */
1337 	pe->pe_get_prop = pool_xml_get_property;
1338 	pe->pe_put_prop = pool_xml_put_property;
1339 	pe->pe_rm_prop = pool_xml_rm_property;
1340 	pe->pe_get_props = pool_xml_get_properties;
1341 	pe->pe_remove = pool_xml_elem_remove;
1342 	pe->pe_get_container = pool_xml_get_container;
1343 	pe->pe_set_container = pool_xml_set_container;
1344 	/*
1345 	 * Specific initialisation for different types of element
1346 	 */
1347 	if (class == PEC_POOL) {
1348 		pool_xml_pool_t *pp = (pool_xml_pool_t *)elem;
1349 		pp->pp_associate = pool_xml_pool_associate;
1350 		pp->pp_dissociate = pool_xml_pool_dissociate;
1351 	}
1352 	if (class == PEC_RES_COMP || class == PEC_RES_AGG) {
1353 		pool_xml_resource_t *pr = (pool_xml_resource_t *)elem;
1354 		pr->pr_is_system = pool_xml_resource_is_system;
1355 		pr->pr_can_associate = pool_xml_resource_can_associate;
1356 	}
1357 }
1358 
1359 /*
1360  * "Wrap" a suplied XML node with a pool_elem_t sub-type of the supplied
1361  * class.
1362  * Returns PO_SUCCESS/PO_FAIL
1363  */
1364 static int
1365 pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class,
1366     pool_resource_elem_class_t res_class,
1367     pool_component_elem_class_t comp_class)
1368 {
1369 	pool_conf_t *conf = node->doc->_private;
1370 	pool_xml_elem_t *elem;
1371 	/* Need to do some messing about to support SubTypes */
1372 	switch (class) {
1373 	case PEC_SYSTEM:
1374 		if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) {
1375 			pool_seterror(POE_SYSTEM);
1376 			return (PO_FAIL);
1377 		}
1378 		(void) memset(elem, 0, sizeof (pool_xml_system_t));
1379 		break;
1380 	case PEC_POOL:
1381 		if ((elem = malloc(sizeof (pool_xml_pool_t))) == NULL) {
1382 			pool_seterror(POE_SYSTEM);
1383 			return (PO_FAIL);
1384 		}
1385 		(void) memset(elem, 0, sizeof (pool_xml_pool_t));
1386 		break;
1387 	case PEC_RES_COMP:
1388 	case PEC_RES_AGG:
1389 		if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) {
1390 			pool_seterror(POE_SYSTEM);
1391 			return (PO_FAIL);
1392 		}
1393 		(void) memset(elem, 0, sizeof (pool_xml_resource_t));
1394 		break;
1395 	case PEC_COMP:
1396 		if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) {
1397 			pool_seterror(POE_SYSTEM);
1398 			return (PO_FAIL);
1399 		}
1400 		(void) memset(elem, 0, sizeof (pool_xml_component_t));
1401 		break;
1402 	}
1403 	pool_xml_elem_init(conf, elem, class, res_class, comp_class);
1404 	node->_private = elem;
1405 	elem->pxe_node = node;
1406 	return (PO_SUCCESS);
1407 }
1408 
1409 /*
1410  * Associate a pool to the default resource for the supplied resource
1411  * type.
1412  */
1413 int
1414 pool_assoc_default_resource_type(pool_t *pool, pool_resource_elem_class_t type)
1415 {
1416 	pool_value_t *props[] = { NULL, NULL, NULL };
1417 	uint_t rl_size;
1418 	pool_resource_t **rsl;
1419 	pool_conf_t *conf = TO_ELEM(pool)->pe_conf;
1420 	char_buf_t *cb = NULL;
1421 	pool_value_t val0 = POOL_VALUE_INITIALIZER;
1422 	pool_value_t val1 = POOL_VALUE_INITIALIZER;
1423 
1424 	props[0] = &val0;
1425 	props[1] = &val1;
1426 
1427 
1428 	if (pool_value_set_string(props[0], pool_resource_type_string(type)) !=
1429 	    PO_SUCCESS ||
1430 	    pool_value_set_name(props[0], c_type) != PO_SUCCESS) {
1431 		return (PO_FAIL);
1432 	}
1433 
1434 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) {
1435 		return (PO_FAIL);
1436 	}
1437 
1438 	if (set_char_buf(cb, "%s.default",
1439 	    pool_resource_type_string(type)) !=
1440 	    PO_SUCCESS) {
1441 		free_char_buf(cb);
1442 		return (PO_FAIL);
1443 	}
1444 	if (pool_value_set_name(props[1], cb->cb_buf) != PO_SUCCESS) {
1445 		free_char_buf(cb);
1446 		return (PO_FAIL);
1447 	}
1448 	pool_value_set_bool(props[1], PO_TRUE);
1449 	free_char_buf(cb);
1450 
1451 	if ((rsl = pool_query_resources(conf, &rl_size, props)) == NULL) {
1452 		pool_seterror(POE_INVALID_CONF);
1453 		return (PO_FAIL);
1454 	}
1455 
1456 	/*
1457 	 * One default resource set per type
1458 	 */
1459 	if (rl_size != 1) {
1460 		free(rsl);
1461 		pool_seterror(POE_INVALID_CONF);
1462 		return (PO_FAIL);
1463 	}
1464 	if (pool_associate(conf, pool, rsl[0])  < 0) {
1465 		free(rsl);
1466 		pool_seterror(POE_INVALID_CONF);
1467 		return (PO_FAIL);
1468 	}
1469 	free(rsl);
1470 	return (PO_SUCCESS);
1471 }
1472 
1473 /*
1474  * Create an XML node in the supplied configuration with a pool_elem_t
1475  * sub-type of the supplied class.
1476  * Returns pool_elem_t pointer/NULL
1477  */
1478 static pool_elem_t *
1479 pool_xml_elem_create(pool_conf_t *conf, pool_elem_class_t class,
1480     pool_resource_elem_class_t res_class,
1481     pool_component_elem_class_t comp_class)
1482 {
1483 	/* In XML terms, create an element of the appropriate class */
1484 	pool_xml_elem_t *elem;
1485 	pool_elem_t *parent;
1486 	pool_system_t *parent_system;
1487 
1488 	if (class == PEC_INVALID) {
1489 		pool_seterror(POE_BADPARAM);
1490 		return (NULL);
1491 	}
1492 
1493 	/* Now create the XML component and add to it's parent */
1494 	/*
1495 	 * If we know the class of an element, we know it's parent.
1496 	 * PEC_POOL, the parent must be the system node
1497 	 * PEC_RES, treat as pool.
1498 	 * PEC_COMP, we don't know the parent, leave this up to the
1499 	 * create_comp function.
1500 	 */
1501 	/* Since we know the subtype we can create and populate the sub-type */
1502 	switch (class) {
1503 	case PEC_POOL:
1504 		if ((parent_system = pool_conf_system(conf)) == NULL) {
1505 			pool_seterror(POE_INVALID_CONF);
1506 			return (NULL);
1507 		}
1508 		if ((parent = pool_system_elem(parent_system)) == NULL) {
1509 			pool_seterror(POE_INVALID_CONF);
1510 			return (NULL);
1511 		}
1512 		if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) {
1513 			pool_seterror(POE_SYSTEM);
1514 			return (NULL);
1515 		}
1516 		(void) memset(elem, 0, sizeof (pool_xml_system_t));
1517 		if ((elem->pxe_node = node_create_with_id(
1518 		    ((pool_xml_elem_t *)parent)->pxe_node,
1519 		    BAD_CAST element_class_tags[class])) == NULL) {
1520 			pool_seterror(POE_DATASTORE);
1521 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1522 			return (NULL);
1523 		}
1524 		break;
1525 	case PEC_RES_COMP:
1526 	case PEC_RES_AGG:
1527 		if ((parent_system = pool_conf_system(conf)) == NULL) {
1528 			pool_seterror(POE_INVALID_CONF);
1529 			return (NULL);
1530 		}
1531 		if ((parent = pool_system_elem(parent_system)) == NULL) {
1532 			pool_seterror(POE_INVALID_CONF);
1533 			return (NULL);
1534 		}
1535 		if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) {
1536 			pool_seterror(POE_SYSTEM);
1537 			return (NULL);
1538 		}
1539 		(void) memset(elem, 0, sizeof (pool_xml_resource_t));
1540 		if ((elem->pxe_node = node_create_with_id
1541 		    (((pool_xml_elem_t *)parent)->pxe_node,
1542 			BAD_CAST element_class_tags[class])) == NULL) {
1543 			pool_seterror(POE_DATASTORE);
1544 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1545 			return (NULL);
1546 		}
1547 		break;
1548 	case PEC_COMP:
1549 		if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) {
1550 			pool_seterror(POE_SYSTEM);
1551 			return (NULL);
1552 		}
1553 		(void) memset(elem, 0, sizeof (pool_xml_component_t));
1554 		if ((elem->pxe_node = node_create(NULL,
1555 		    BAD_CAST element_class_tags[class])) == NULL) {
1556 			pool_seterror(POE_DATASTORE);
1557 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1558 			return (NULL);
1559 		}
1560 		break;
1561 	default:
1562 		pool_seterror(POE_BADPARAM);
1563 		return (NULL);
1564 	}
1565 	pool_xml_elem_init(conf, elem, class, res_class, comp_class);
1566 	elem->pxe_node->_private = elem;
1567 	if (class == PEC_RES_COMP || class == PEC_RES_AGG ||
1568 	    class == PEC_COMP) {
1569 		/*
1570 		 * Put the type and an invalid sys_id on the node.
1571 		 */
1572 		if (xmlSetProp(elem->pxe_node, BAD_CAST c_sys_prop,
1573 		    BAD_CAST POOL_SYSID_BAD_STRING) == NULL) {
1574 			pool_seterror(POE_DATASTORE);
1575 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1576 			return (NULL);
1577 		}
1578 		if (xmlSetProp(elem->pxe_node, BAD_CAST c_type,
1579 			BAD_CAST pool_elem_class_string(
1580 			    (pool_elem_t *)elem)) == NULL) {
1581 			pool_seterror(POE_DATASTORE);
1582 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1583 			return (NULL);
1584 		}
1585 	}
1586 	if (class == PEC_POOL) {
1587 		/*
1588 		 * Note: This is resource specific.
1589 		 */
1590 		if (pool_assoc_default_resource_type(pool_elem_pool(
1591 		    (pool_elem_t *)elem), PREC_PSET) == PO_FAIL) {
1592 			(void) pool_xml_elem_remove((pool_elem_t *)elem);
1593 			return (NULL);
1594 		}
1595 	}
1596 	return ((pool_elem_t *)elem);
1597 }
1598 
1599 /*
1600  * Allocate a data provider for the supplied configuration and optionally
1601  * discover resources.
1602  * The data provider is the cross over point from the "abstract" configuration
1603  * functions into the data representation specific manipulation routines.
1604  * This function sets up all the required pointers to create an XML aware
1605  * data provider.
1606  * Returns PO_SUCCESS/PO_FAIL
1607  */
1608 int
1609 pool_xml_connection_alloc(pool_conf_t *conf, int oflags)
1610 {
1611 	pool_xml_connection_t *prov;
1612 
1613 	xml_init();
1614 	if ((prov = malloc(sizeof (pool_xml_connection_t))) == NULL) {
1615 		pool_seterror(POE_SYSTEM);
1616 		return (PO_FAIL);
1617 	}
1618 	(void) memset(prov, 0, sizeof (pool_xml_connection_t));
1619 	/*
1620 	 * Initialise data members
1621 	 */
1622 	prov->pc_name = strdup("LIBXML 2.4.0");
1623 	prov->pc_store_type = XML_DATA_STORE;
1624 	prov->pc_oflags = oflags;
1625 	/*
1626 	 * Initialise function pointers
1627 	 */
1628 	prov->pc_close = pool_xml_close;
1629 	prov->pc_validate = pool_xml_validate;
1630 	prov->pc_commit = pool_xml_commit;
1631 	prov->pc_export = pool_xml_export;
1632 	prov->pc_rollback = pool_xml_rollback;
1633 	prov->pc_exec_query = pool_xml_exec_query;
1634 	prov->pc_elem_create = pool_xml_elem_create;
1635 	prov->pc_remove = pool_xml_remove;
1636 	prov->pc_res_xfer = pool_xml_res_transfer;
1637 	prov->pc_res_xxfer = pool_xml_res_xtransfer;
1638 	/*
1639 	 * End of common initialisation
1640 	 */
1641 	/*
1642 	 * Associate the provider to it's configuration
1643 	 */
1644 	conf->pc_prov = (pool_connection_t *)prov;
1645 	/*
1646 	 * At this point the configuration provider has been initialized,
1647 	 * mark the configuration as valid so that the various routines
1648 	 * which rely on a valid configuration will work correctly.
1649 	 */
1650 	conf->pc_state = POF_VALID;
1651 
1652 	if ((oflags & PO_CREAT) != 0) {
1653 		pool_conf_t *dyn;
1654 
1655 		if ((dyn = pool_conf_alloc()) == NULL)
1656 			return (PO_FAIL);
1657 
1658 		if (pool_conf_open(dyn, pool_dynamic_location(),
1659 		    PO_RDONLY) != PO_SUCCESS) {
1660 			pool_conf_free(dyn);
1661 			return (PO_FAIL);
1662 		}
1663 
1664 		if (pool_conf_export(dyn, conf->pc_location,
1665 		    POX_NATIVE) != PO_SUCCESS) {
1666 			(void) pool_conf_close(dyn);
1667 			pool_conf_free(dyn);
1668 			return (PO_FAIL);
1669 		}
1670 		(void) pool_conf_close(dyn);
1671 		pool_conf_free(dyn);
1672 	}
1673 
1674 	if (pool_xml_open_file(conf) == PO_FAIL) {
1675 		(void) pool_xml_close(conf);
1676 		return (PO_FAIL);
1677 	}
1678 
1679 	return (PO_SUCCESS);
1680 }
1681 
1682 /*
1683  * Free the resources for an XML data provider.
1684  */
1685 static void
1686 pool_xml_connection_free(pool_xml_connection_t *prov)
1687 {
1688 	free((void *)prov->pc_name);
1689 	free(prov);
1690 }
1691 
1692 /*
1693  * Allocate a result set. The Result Set stores the result of an XPath
1694  * query along with the parameters used to create the result set (for
1695  * debugging purposes).
1696  * Returns pool_xml_result_set_t pointer/NULL
1697  */
1698 static pool_xml_result_set_t *
1699 pool_xml_result_set_alloc(const pool_conf_t *conf)
1700 {
1701 	pool_xml_result_set_t *rs;
1702 
1703 	if ((rs = malloc(sizeof (pool_xml_result_set_t))) == NULL) {
1704 		pool_seterror(POE_SYSTEM);
1705 		return (NULL);
1706 	}
1707 	(void) memset(rs, 0, sizeof (pool_xml_result_set_t));
1708 	rs->prs_conf = conf;
1709 	rs->prs_index = -1;
1710 	rs->prs_active = PO_TRUE;
1711 	/* Fix up the result set accessor functions to the xml specfic ones */
1712 	rs->prs_next = pool_xml_rs_next;
1713 	rs->prs_prev = pool_xml_rs_prev;
1714 	rs->prs_first = pool_xml_rs_first;
1715 	rs->prs_last = pool_xml_rs_last;
1716 	rs->prs_get_index = pool_xml_rs_get_index;
1717 	rs->prs_set_index = pool_xml_rs_set_index;
1718 	rs->prs_close = pool_xml_rs_close;
1719 	rs->prs_count = pool_xml_rs_count;
1720 	return (rs);
1721 }
1722 
1723 /*
1724  * Free a result set. Ensure that the resources are all released at this point.
1725  */
1726 static void
1727 pool_xml_result_set_free(pool_xml_result_set_t *rs)
1728 {
1729 	if (rs->pxr_path != NULL)
1730 		xmlXPathFreeObject(rs->pxr_path);
1731 	if (rs->pxr_ctx != NULL)
1732 		xmlXPathFreeContext(rs->pxr_ctx);
1733 	free(rs);
1734 }
1735 
1736 /*
1737  * Transfer size from one resource to another.
1738  * Returns PO_SUCCESS/PO_FAIL
1739  */
1740 /* ARGSUSED */
1741 int
1742 pool_xml_res_transfer(pool_resource_t *src, pool_resource_t *tgt, uint64_t size)
1743 {
1744 	return (PO_SUCCESS);
1745 }
1746 
1747 /*
1748  * Transfer components rl from one resource to another.
1749  * Returns PO_SUCCESS/PO_FAIL
1750  */
1751 /* ARGSUSED */
1752 int
1753 pool_xml_res_xtransfer(pool_resource_t *src, pool_resource_t *tgt,
1754     pool_component_t **rl) {
1755 	int i;
1756 
1757 	/*
1758 	 * Walk the Result Set and move the resource components
1759 	 */
1760 	for (i = 0; rl[i] != NULL; i++) {
1761 		if (pool_set_container(TO_ELEM(tgt), TO_ELEM(rl[i])) ==
1762 		    PO_FAIL) {
1763 			return (PO_FAIL);
1764 		}
1765 	}
1766 	return (PO_SUCCESS);
1767 }
1768 
1769 /*
1770  * Return the next element in a result set.
1771  * Returns pool_elem_t pointer/NULL
1772  */
1773 static pool_elem_t *
1774 pool_xml_rs_next(pool_result_set_t *set)
1775 {
1776 	pool_elem_t *next;
1777 	/* Since I know this is an XML result set */
1778 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1779 
1780 	/* Update the context node */
1781 	if (xset->prs_index == xset->pxr_path->nodesetval->nodeNr - 1)
1782 		return (NULL);
1783 	next =
1784 	    xset->pxr_path->nodesetval->nodeTab[++xset->prs_index]->_private;
1785 	return (next);
1786 }
1787 
1788 /*
1789  * Return the previous element in a result set.
1790  * Returns pool_elem_t pointer/NULL
1791  */
1792 static pool_elem_t *
1793 pool_xml_rs_prev(pool_result_set_t *set)
1794 {
1795 	pool_elem_t *prev;
1796 	/* Since I know this is an XML result set */
1797 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1798 
1799 	/* Update the context node */
1800 	if (xset->prs_index < 0)
1801 		return (NULL);
1802 	prev =
1803 	    xset->pxr_path->nodesetval->nodeTab[xset->prs_index--]->_private;
1804 	return (prev);
1805 }
1806 
1807 /*
1808  * Sets the current index in a result set.
1809  * Returns PO_SUCCESS/PO_FAIL
1810  */
1811 static int
1812 pool_xml_rs_set_index(pool_result_set_t *set, int index)
1813 {
1814 	/* Since I know this is an XML result set */
1815 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1816 
1817 	if (index < 0 || index >= xset->pxr_path->nodesetval->nodeNr) {
1818 		pool_seterror(POE_BADPARAM);
1819 		return (PO_FAIL);
1820 	}
1821 	xset->prs_index = index;
1822 	return (PO_SUCCESS);
1823 }
1824 
1825 /*
1826  * Return the current index in a result set.
1827  * Returns current index
1828  */
1829 static int
1830 pool_xml_rs_get_index(pool_result_set_t *set)
1831 {
1832 	/* Since I know this is an XML result set */
1833 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1834 
1835 	return (xset->prs_index);
1836 }
1837 
1838 /*
1839  * Return the first element in a result set.
1840  * Returns pool_elem_t pointer/NULL
1841  */
1842 static pool_elem_t *
1843 pool_xml_rs_first(pool_result_set_t *set)
1844 {
1845 	/* Since I know this is an XML result set */
1846 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1847 
1848 	/* Update the context node */
1849 	return (xset->pxr_path->nodesetval->nodeTab[0]->_private);
1850 }
1851 
1852 /*
1853  * Return the last element in a result set.
1854  * Returns pool_elem_t pointer/NULL
1855  */
1856 static pool_elem_t *
1857 pool_xml_rs_last(pool_result_set_t *set)
1858 {
1859 	/* Since I know this is an XML result set */
1860 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1861 
1862 	/* Update the context node */
1863 	return (xset->pxr_path->nodesetval->
1864 	    nodeTab[xset->pxr_path->nodesetval->nodeNr-1]->_private);
1865 }
1866 
1867 /*
1868  * Return the number of results in a result set.
1869  * Returns result count
1870  */
1871 static int
1872 pool_xml_rs_count(pool_result_set_t *set)
1873 {
1874 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1875 
1876 	return (xset->pxr_path->nodesetval->nodeNr);
1877 }
1878 
1879 
1880 /*
1881  * Close a result set. Remove this result set from the list of results and
1882  * free the resources
1883  * Returns PO_SUCCESS/PO_FAIL
1884  */
1885 static int
1886 pool_xml_rs_close(pool_result_set_t *set)
1887 {
1888 	pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set;
1889 
1890 	pool_xml_result_set_free(xset);
1891 	return (PO_SUCCESS);
1892 }
1893 
1894 /*
1895  * Set the container for a node.
1896  * Returns PO_SUCCESS/PO_FAIL
1897  */
1898 static int
1899 pool_xml_set_container(pool_elem_t *pp, pool_elem_t *pc)
1900 {
1901 	pool_xml_elem_t *pxp;
1902 	pool_xml_elem_t *pxc;
1903 	xmlNodePtr parent;
1904 
1905 	pxp = (pool_xml_elem_t *)pp;
1906 	pxc = (pool_xml_elem_t *)pc;
1907 	parent = pxc->pxe_node->parent;
1908 
1909 	xmlUnlinkNode(pxc->pxe_node);
1910 	if (xmlAddChild(pxp->pxe_node, pxc->pxe_node) == NULL) {
1911 		xmlAddChild(parent, pxc->pxe_node); /* Try to move back */
1912 		pool_seterror(POE_INVALID_CONF);
1913 		return (PO_FAIL);
1914 	}
1915 	pc->pe_conf = pp->pe_conf;
1916 	return (PO_SUCCESS);
1917 }
1918 /*
1919  * Get the container for a node.
1920  * Returns Container/NULL
1921  */
1922 static pool_elem_t *
1923 pool_xml_get_container(const pool_elem_t *pc)
1924 {
1925 	pool_xml_elem_t *pxc = (pool_xml_elem_t *)pc;
1926 
1927 	return ((pool_elem_t *)pxc->pxe_node->parent->_private);
1928 }
1929 
1930 /*
1931  * Note: This function is resource specific, needs extending for other
1932  * resource types.
1933  */
1934 int
1935 pool_xml_resource_is_system(const pool_resource_t *pr)
1936 {
1937 	switch (pool_resource_elem_class(TO_ELEM(pr))) {
1938 	case PREC_PSET:
1939 		return (PSID_IS_SYSSET(
1940 		    elem_get_sysid(TO_ELEM(pr))));
1941 	default:
1942 		return (PO_FALSE);
1943 	}
1944 }
1945 
1946 /*
1947  * Note: This function is resource specific, needs extending for other
1948  * resource types.
1949  */
1950 int
1951 pool_xml_resource_can_associate(const pool_resource_t *pr)
1952 {
1953 	switch (pool_resource_elem_class(TO_ELEM(pr))) {
1954 	case PREC_PSET:
1955 		return (PO_TRUE);
1956 	default:
1957 		return (PO_FALSE);
1958 	}
1959 }
1960 
1961 /*
1962  * Note: This function is resource specific. It must be extended to support
1963  * multiple resource types.
1964  */
1965 int
1966 pool_xml_pool_associate(pool_t *pool, const pool_resource_t *pr)
1967 {
1968 	pool_value_t val = POOL_VALUE_INITIALIZER;
1969 
1970 	if (pool_xml_get_property(TO_ELEM(pr),
1971 	    "pset.ref_id", &val) != POC_STRING)
1972 		return (PO_FAIL);
1973 	if (pool_xml_put_property(TO_ELEM(pool), "pool.res", &val) !=
1974 	    PO_SUCCESS)
1975 		return (PO_FAIL);
1976 	return (PO_SUCCESS);
1977 }
1978 
1979 /*
1980  * pool_xml_pool_dissociate() simply finds the default resource for
1981  * the type of resource being dissociated and then calls
1982  * pool_xml_pool_associate() to associate to the default resource.
1983  */
1984 int
1985 pool_xml_pool_dissociate(pool_t *pool, const pool_resource_t *pr)
1986 {
1987 	const pool_resource_t *default_res;
1988 
1989 	if ((default_res = get_default_resource(pr)) == NULL)
1990 		return (PO_FAIL);
1991 	if (default_res == pr)
1992 		return (PO_SUCCESS);
1993 	return (pool_xml_pool_associate(pool, default_res));
1994 }
1995 
1996 /*
1997  * pool_xml_open_file() opens a file for a configuration. This establishes
1998  * the locks required to ensure data integrity when manipulating a
1999  * configuration.
2000  * Returns PO_SUCCESS/PO_FAIL
2001  */
2002 static int
2003 pool_xml_open_file(pool_conf_t *conf)
2004 {
2005 	struct flock lock;
2006 	struct stat s;
2007 
2008 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2009 
2010 	/*
2011 	 * Always close the pxc_file in case there was a previously failed open
2012 	 */
2013 	if (prov->pxc_file != NULL) {
2014 		(void) fclose(prov->pxc_file);
2015 		prov->pxc_file = NULL;
2016 	}
2017 
2018 	/*
2019 	 * Check that the DTD required for this operation is present.
2020 	 * If it isn't fail
2021 	 */
2022 	if (dtd_exists(dtd_location) == PO_FALSE) {
2023 		pool_seterror(POE_DATASTORE);
2024 		return (PO_FAIL);
2025 	}
2026 
2027 	if ((prov->pc_oflags & PO_RDWR) != 0)
2028 		prov->pxc_file = fopen(conf->pc_location, "r+F");
2029 	else /* Assume opening PO_RDONLY */
2030 		prov->pxc_file = fopen(conf->pc_location, "rF");
2031 
2032 	if (prov->pxc_file == NULL) {
2033 		pool_seterror(POE_SYSTEM);
2034 		return (PO_FAIL);
2035 	}
2036 
2037 	/*
2038 	 * Setup the lock for the file
2039 	 */
2040 	lock.l_type = (prov->pc_oflags & PO_RDWR) ? F_WRLCK : F_RDLCK;
2041 	lock.l_whence = SEEK_SET;
2042 	lock.l_start = 0;
2043 	lock.l_len = 0;
2044 	if (fcntl(fileno(prov->pxc_file), F_SETLKW, &lock) == -1) {
2045 		pool_seterror(POE_SYSTEM);
2046 		return (PO_FAIL);
2047 	}
2048 	/*
2049 	 * Check to see if the document was removed whilst waiting for
2050 	 * the lock. If it was return an error.
2051 	 */
2052 	if (stat(conf->pc_location, &s) == -1) {
2053 		(void) fclose(prov->pxc_file);
2054 		prov->pxc_file = NULL;
2055 		pool_seterror(POE_SYSTEM);
2056 		return (PO_FAIL);
2057 	}
2058 	/* Parse the document */
2059 	if (pool_xml_parse_document(conf) != PO_SUCCESS)
2060 		return (PO_FAIL);
2061 	return (PO_SUCCESS);
2062 }
2063 
2064 /*
2065  * Try to work out if an element contains an attribute of the supplied name.
2066  * Search the internal subset first and then the external subset.
2067  * Return PO_TRUE if there is an attribute of that name declared for that
2068  * element.
2069  */
2070 int
2071 pool_is_xml_attr(xmlDocPtr doc, const char *elem, const char *attr)
2072 {
2073 	xmlDtdPtr internal = xmlGetIntSubset(doc);
2074 	xmlDtdPtr external = doc->extSubset;
2075 
2076 	if (xmlGetDtdAttrDesc(internal, BAD_CAST elem, BAD_CAST attr) == NULL)
2077 		if (xmlGetDtdAttrDesc(external,
2078 		    BAD_CAST elem, BAD_CAST attr) == NULL)
2079 			return (PO_FALSE);
2080 	return (PO_TRUE);
2081 }
2082 
2083 /*
2084  * Execute the specified query using XPath. This complex function relies on
2085  * a couple of helpers to build up an XPath query, pool_build_xpath_buf in
2086  * particular.
2087  * conf - the pool configuration being manipulated
2088  * src - the root of the search, if NULL that means whole document
2089  * src_attr - if supplied means an IDREF(S) search on this attribute
2090  * classes - target classes
2091  * props - target properties
2092  * Returns pool_result_set_t pointer/NULL
2093  */
2094 pool_result_set_t *
2095 pool_xml_exec_query(const pool_conf_t *conf, const pool_elem_t *src,
2096     const char *src_attr, pool_elem_class_t classes, pool_value_t **props)
2097 {
2098 	char *buf = NULL;
2099 	char_buf_t *cb = NULL;
2100 	pool_xml_result_set_t *rs;
2101 	pool_xml_elem_t *pxe = (pool_xml_elem_t *)src;
2102 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2103 
2104 	if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
2105 		return (NULL);
2106 
2107 	/*
2108 	 * Prior to building up the complex XPath query, check to see if
2109 	 * src_attr is an IDREF(S). If it is use the IDREF(S) information
2110 	 * to generate the query rather than the other data
2111 	 */
2112 	if (src_attr != NULL) {
2113 		char *tok;
2114 		char *lasts;
2115 		char *or = "";
2116 		xmlChar *id;
2117 
2118 		/*
2119 		 * Check the arguments for consistency
2120 		 */
2121 		if (pool_is_xml_attr(prov->pxc_doc,
2122 		    element_class_tags[src->pe_class], src_attr) != PO_TRUE) {
2123 			free_char_buf(cb);
2124 			pool_seterror(POE_BADPARAM);
2125 			return (NULL);
2126 		}
2127 
2128 		if ((id = xmlGetProp(pxe->pxe_node, BAD_CAST src_attr))
2129 		    == NULL) {
2130 			free_char_buf(cb);
2131 			pool_seterror(POE_DATASTORE);
2132 			return (NULL);
2133 		}
2134 		for (tok = strtok_r((char *)id, "	 ", &lasts);
2135 		    tok != NULL; tok = strtok_r(NULL, "	 ", &lasts)) {
2136 			(void) append_char_buf(cb, "%s//*[@ref_id=\"%s\"]",
2137 			    or, tok);
2138 			or = " | ";
2139 			if ((classes & PEC_QRY_SYSTEM) != 0) {
2140 				if (pool_build_xpath_buf(prov, src, PEC_SYSTEM,
2141 				    props, cb, PO_TRUE) == PO_FAIL) {
2142 					free_char_buf(cb);
2143 					return (NULL);
2144 				}
2145 			}
2146 			if ((classes & PEC_QRY_POOL) != 0) {
2147 				if (pool_build_xpath_buf(prov, src, PEC_POOL,
2148 				    props, cb, PO_TRUE) == PO_FAIL) {
2149 					free_char_buf(cb);
2150 					return (NULL);
2151 				}
2152 			}
2153 			if ((classes & PEC_QRY_RES_COMP) != 0) {
2154 				if (pool_build_xpath_buf(prov, src,
2155 				    PEC_RES_COMP, props, cb, PO_TRUE)
2156 				    == PO_FAIL) {
2157 					free_char_buf(cb);
2158 					return (NULL);
2159 				}
2160 			} else if ((classes & PEC_QRY_RES_AGG) != 0) {
2161 				if (pool_build_xpath_buf(prov, src,
2162 				    PEC_RES_AGG, props, cb, PO_TRUE)
2163 				    == PO_FAIL) {
2164 					free_char_buf(cb);
2165 					return (NULL);
2166 				}
2167 			}
2168 		}
2169 		xmlFree(id);
2170 	} else {
2171 		/*
2172 		 * Build up an XPath query using the supplied parameters.
2173 		 * The basic logic is to:
2174 		 * - Identify which classes are the targets of the query
2175 		 * - For each class work out if the props are attributes or not
2176 		 * - Build up a piece of XPath for each class
2177 		 * - Combine the results into one large XPath query.
2178 		 * - Execute the query.
2179 		 */
2180 		if ((classes & PEC_QRY_SYSTEM) != 0) {
2181 			if (pool_build_xpath_buf(prov, src, PEC_SYSTEM, props,
2182 			    cb, PO_FALSE) == PO_FAIL) {
2183 				free_char_buf(cb);
2184 				return (NULL);
2185 			}
2186 		}
2187 		if ((classes & PEC_QRY_POOL) != 0) {
2188 			if (pool_build_xpath_buf(prov, src, PEC_POOL, props,
2189 			    cb, PO_FALSE) == PO_FAIL) {
2190 				free_char_buf(cb);
2191 				return (NULL);
2192 			}
2193 		}
2194 		if ((classes & PEC_QRY_RES_COMP) != 0) {
2195 			if (pool_build_xpath_buf(prov, src, PEC_RES_COMP, props,
2196 			    cb, PO_FALSE) == PO_FAIL) {
2197 				free_char_buf(cb);
2198 				return (NULL);
2199 			}
2200 		}
2201 		if ((classes & PEC_QRY_RES_AGG) != 0) {
2202 			if (pool_build_xpath_buf(prov, src, PEC_RES_AGG, props,
2203 			    cb, PO_FALSE) == PO_FAIL) {
2204 				free_char_buf(cb);
2205 				return (NULL);
2206 			}
2207 		}
2208 		if ((classes & PEC_QRY_COMP) != 0) {
2209 			if (pool_build_xpath_buf(prov, src, PEC_COMP, props,
2210 			    cb, PO_FALSE) == PO_FAIL) {
2211 				free_char_buf(cb);
2212 				return (NULL);
2213 			}
2214 		}
2215 	}
2216 	buf = strdup(cb->cb_buf);
2217 	free_char_buf(cb);
2218 	/*
2219 	 * Have a buffer at this point, that we can use
2220 	 */
2221 	if ((rs = pool_xml_result_set_alloc(conf)) == NULL) {
2222 		free(buf);
2223 		return (NULL);
2224 	}
2225 	/*
2226 	 * Set up the XPath Query
2227 	 */
2228 	if ((rs->pxr_ctx = xmlXPathNewContext(
2229 	    ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) {
2230 		free(buf);
2231 		(void) pool_xml_rs_close((pool_result_set_t *)rs);
2232 		pool_seterror(POE_DATASTORE);
2233 		return (NULL);
2234 	}
2235 	if (src == NULL)
2236 		rs->pxr_ctx->node = xmlDocGetRootElement
2237 		    (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc);
2238 	else
2239 		rs->pxr_ctx->node = pxe->pxe_node;
2240 	/*
2241 	 * Select
2242 	 */
2243 	rs->pxr_path = xmlXPathEval(BAD_CAST buf, rs->pxr_ctx);
2244 	free(buf);
2245 	/*
2246 	 * Generate the result set and wrap the results as pool_elem_t
2247 	 */
2248 	if (rs->pxr_path->nodesetval->nodeNr == 0)
2249 		pool_seterror(POE_INVALID_SEARCH);
2250 	return ((pool_result_set_t *)rs);
2251 }
2252 
2253 /*
2254  * Build an XPath query buffer. This is complex and a little fragile, but
2255  * I'm trying to accomplish something complex with as little code as possible.
2256  * I wait the implementation of XMLQuery with baited breath...
2257  * Returns PO_SUCCESS/PO_FAIL
2258  */
2259 static int
2260 pool_build_xpath_buf(pool_xml_connection_t *prov, const pool_elem_t *src,
2261     pool_elem_class_t class, pool_value_t *props[], char_buf_t *cb, int is_ref)
2262 {
2263 	int i;
2264 	const char *ATTR_FMTS[] = {
2265 		"[ @%s=\"%llu\" ]",	/* POC_UINT */
2266 		"[ @%s=\"%lld\" ]",	/* POC_INT */
2267 		"[ @%s=\"%f\" ]",	/* POC_DOUBLE */
2268 		"[ @%s=\"%s\" ]",	/* POC_BOOL */
2269 		"[ @%s=\"%s\" ]",	/* POC_STRING */
2270 	};
2271 	const char *PROP_FMTS[] = {
2272 		"[ property[@name=\"%s\"][text()=\"%llu\"] ]",	/* POC_UINT */
2273 		"[ property[@name=\"%s\"][text()=\"%lld\"] ]",	/* POC_INT */
2274 		"[ property[@name=\"%s\"][text()=\"%f\"] ]",	/* POC_DOUBLE */
2275 		"[ property[@name=\"%s\"][text()=\"%s\"] ]",	/* POC_BOOL */
2276 		"[ property[@name=\"%s\"][text()=\"%s\"] ]"	/* POC_STRING */
2277 	};
2278 	const char **fmts;
2279 	int nprop;
2280 	const char *last_prop_name = NULL;
2281 	char *type_prefix = NULL;
2282 	int has_type = PO_FALSE;
2283 
2284 	if (is_ref == PO_FALSE) {
2285 		if (cb->cb_buf != NULL && strlen(cb->cb_buf) > 0)
2286 			(void) append_char_buf(cb, " |");
2287 		if (src != NULL)
2288 			(void) append_char_buf(cb, " ./");
2289 		else
2290 			(void) append_char_buf(cb, "//");
2291 		(void) append_char_buf(cb, element_class_tags[class]);
2292 	}
2293 	if (props == NULL || props[0] == NULL)
2294 		return (PO_SUCCESS);
2295 	for (nprop = 0; props[nprop] != NULL; nprop++)
2296 		/* Count properties */;
2297 	/*
2298 	 * Sort the attributes and properties by name.
2299 	 */
2300 	qsort(props, nprop, sizeof (pool_value_t *), prop_sort);
2301 	for (i = 0; i < nprop; i++) {
2302 		int is_attr = 0;
2303 		const char *prefix;
2304 		const char *prop_name;
2305 		uint64_t uval;
2306 		int64_t ival;
2307 		double dval;
2308 		uchar_t bval;
2309 		const char *sval;
2310 		pool_value_class_t pvc;
2311 
2312 		prop_name = pool_value_get_name(props[i]);
2313 		if ((prefix = is_a_known_prefix(class, prop_name)) != NULL) {
2314 			const char *attr_name;
2315 			/*
2316 			 * Possibly an attribute. Strip off the prefix.
2317 			 */
2318 			if (strcmp(prop_name, c_type) == 0) {
2319 				has_type = PO_TRUE;
2320 				attr_name = prop_name;
2321 			} else
2322 				attr_name = prop_name + strlen(prefix) + 1;
2323 			if (pool_is_xml_attr(prov->pxc_doc,
2324 			    element_class_tags[class], attr_name)) {
2325 				is_attr = 1;
2326 				prop_name = attr_name;
2327 				if (class == PEC_RES_COMP ||
2328 				    class == PEC_RES_AGG ||
2329 				class == PEC_COMP) {
2330 					if (type_prefix != NULL)
2331 						free(type_prefix);
2332 					type_prefix = strdup(prefix);
2333 				}
2334 			}
2335 		}
2336 		if (is_attr)  {
2337 			fmts = ATTR_FMTS;
2338 		} else {
2339 			fmts = PROP_FMTS;
2340 		}
2341 		/*
2342 		 * Add attributes/properties to the search buffer
2343 		 */
2344 		switch ((pvc = pool_value_get_type(props[i]))) {
2345 		case POC_UINT:
2346 			(void) pool_value_get_uint64(props[i], &uval);
2347 			if (append_char_buf(cb, fmts[pvc], prop_name, uval)
2348 			    == PO_FAIL) {
2349 				free(type_prefix);
2350 				return (PO_FAIL);
2351 			}
2352 			break;
2353 		case POC_INT:
2354 			(void) pool_value_get_int64(props[i], &ival);
2355 			if (append_char_buf(cb, fmts[pvc], prop_name, ival)
2356 			    == PO_FAIL) {
2357 				free(type_prefix);
2358 				return (PO_FAIL);
2359 			}
2360 			break;
2361 		case POC_DOUBLE:
2362 			(void) pool_value_get_double(props[i], &dval);
2363 			if (append_char_buf(cb, fmts[pvc], prop_name, dval)
2364 			    == PO_FAIL) {
2365 				free(type_prefix);
2366 				return (PO_FAIL);
2367 			}
2368 			break;
2369 		case POC_BOOL:
2370 			(void) pool_value_get_bool(props[i], &bval);
2371 			if (append_char_buf(cb, fmts[pvc], prop_name,
2372 			    bval ? "true" : "false") == PO_FAIL) {
2373 				free(type_prefix);
2374 				return (PO_FAIL);
2375 			}
2376 			break;
2377 		case POC_STRING:
2378 			(void) pool_value_get_string(props[i], &sval);
2379 			if (append_char_buf(cb, fmts[pvc], prop_name, sval)
2380 			    == PO_FAIL) {
2381 				free(type_prefix);
2382 				return (PO_FAIL);
2383 			}
2384 			break;
2385 		default:
2386 			free(type_prefix);
2387 			pool_seterror(POE_INVALID_SEARCH);
2388 			return (PO_FAIL);
2389 		}
2390 		if (last_prop_name != NULL) {
2391 			const char *suffix1, *suffix2;
2392 			/*
2393 			 * Extra fiddling for namespaces
2394 			 */
2395 			suffix1 = strrchr(prop_name, '.');
2396 			suffix2 = strrchr(last_prop_name, '.');
2397 
2398 			if (suffix1 != NULL || suffix2 != NULL) {
2399 				if (suffix1 == NULL)
2400 					suffix1 = prop_name;
2401 				else
2402 					suffix1++;
2403 				if (suffix2 == NULL)
2404 					suffix2 = last_prop_name;
2405 				else
2406 					suffix2++;
2407 			} else {
2408 				suffix1 = prop_name;
2409 				suffix2 = last_prop_name;
2410 			}
2411 			if (strcmp(suffix1, suffix2) == 0) {
2412 				char *where = strrchr(cb->cb_buf, '[');
2413 				if (is_attr != PO_TRUE) {
2414 					/* repeat */
2415 					while (*--where != '[');
2416 					while (*--where != '[');
2417 				}
2418 				*(where - 1) = 'o';
2419 				*where = 'r';
2420 			}
2421 		}
2422 		last_prop_name = prop_name;
2423 	}
2424 	if (has_type == PO_FALSE) {
2425 		if (type_prefix) {
2426 			if (append_char_buf(cb, ATTR_FMTS[POC_STRING],
2427 			    c_type, type_prefix) == PO_FAIL) {
2428 				free(type_prefix);
2429 				return (PO_FAIL);
2430 			}
2431 		}
2432 	}
2433 	free(type_prefix);
2434 	return (PO_SUCCESS);
2435 }
2436 
2437 /*
2438  * Utility routine for use by quicksort. Assumes that the supplied data
2439  * are pool values and compares the names of the two pool values.
2440  * Returns an integer greater than, equal to, or less than 0.
2441  */
2442 static int
2443 prop_sort(const void *a, const void *b)
2444 {
2445 	pool_value_t **prop_a = (pool_value_t **)a;
2446 	pool_value_t **prop_b = (pool_value_t **)b;
2447 	const char *str_a;
2448 	const char *str_b;
2449 	const char *suffix1, *suffix2;
2450 
2451 	str_a = pool_value_get_name(*prop_a);
2452 	str_b = pool_value_get_name(*prop_b);
2453 	/*
2454 	 * Extra fiddling for namespaces
2455 	 */
2456 	suffix1 = strrchr(str_a, '.');
2457 	suffix2 = strrchr(str_b, '.');
2458 
2459 	if (suffix1 != NULL || suffix2 != NULL) {
2460 		if (suffix1 == NULL)
2461 			suffix1 = str_a;
2462 		else
2463 			suffix1++;
2464 		if (suffix2 == NULL)
2465 			suffix2 = str_b;
2466 		else
2467 			suffix2++;
2468 	} else {
2469 		suffix1 = str_a;
2470 		suffix2 = str_b;
2471 	}
2472 	return (strcmp(suffix1, suffix2));
2473 }
2474 
2475 /*
2476  * Order the elements by (ref_id)
2477  */
2478 
2479 /*
2480  * Returns PO_TRUE/PO_FALSE to indicate whether the supplied path exists on the
2481  * system. It is assumed that the supplied path is in URL format and represents
2482  * a file and so file:// is stripped from the start of the search.
2483  */
2484 static int
2485 dtd_exists(const char *path)
2486 {
2487 	struct stat buf;
2488 
2489 	if (strstr(path, "file://") != path)
2490 		return (PO_FALSE);
2491 
2492 	if (path[7] == 0)
2493 		return (PO_FALSE);
2494 
2495 	if (stat(&path[7], &buf) == 0)
2496 		return (PO_TRUE);
2497 	return (PO_FALSE);
2498 }
2499 
2500 /*
2501  * Build the dtype structures to accelerate data type lookup operations. The
2502  * purpose is to avoid expensive XML manipulations on data which will not
2503  * change over the life of a library invocation. It is designed to be invoked
2504  * once from the library init function.
2505  */
2506 static void
2507 build_dtype_accelerator(void)
2508 {
2509 	xmlDtdPtr dtd;
2510 	const xmlChar *elem_list[ELEM_TYPE_COUNT] = {
2511 		BAD_CAST "res_comp",
2512 		BAD_CAST "res_agg",
2513 		BAD_CAST "comp",
2514 		BAD_CAST "pool",
2515 		BAD_CAST "property",
2516 		BAD_CAST "system" };
2517 	int i;
2518 
2519 	if (_libpool_xml_initialised == PO_TRUE)
2520 		return;
2521 
2522 	/* Load up the d-type data for each element */
2523 	/*
2524 	 * Store data type information in nested lists
2525 	 * Top level list contains attribute declaration pointers which
2526 	 * can be used to match with supplied nodes.
2527 	 * Second level list contains attribute type information for each
2528 	 * element declaration
2529 	 */
2530 	/*
2531 	 * Unfortunately, there's no easy way to get a list of all DTD
2532 	 * element descriptions as there is no libxml API to do this (they
2533 	 * are stored in a hash, which I guess is why). Explicitly seek
2534 	 * for descriptions for elements that are known to hold an a-dtype
2535 	 * attribute and build accelerators for those elements.
2536 	 * If the DTD changes, the library may have to change as well now,
2537 	 * since this code makes explicit assumptions about which elements
2538 	 * contain a-dtype information.
2539 	 */
2540 
2541 	if ((dtd = xmlParseDTD(BAD_CAST "-//Sun Microsystems Inc//DTD Resource"
2542 	    " Management All//EN", BAD_CAST dtd_location)) == NULL)
2543 		return;
2544 	for (i = 0; i < ELEM_TYPE_COUNT; i++) {
2545 		xmlElementPtr elem;
2546 		xmlAttributePtr attr;
2547 
2548 		if ((elem = xmlGetDtdElementDesc(dtd, elem_list[i])) == NULL)
2549 			return;
2550 		elem_tbl[i].ett_elem = xmlStrdup(elem->name);
2551 		/* Walk the list of attributes looking for a-dtype */
2552 		for (attr = elem->attributes; attr != NULL;
2553 		    attr = attr->nexth) {
2554 			if (strcmp((const char *)attr->name, c_a_dtype) == 0) {
2555 				/*
2556 				 * Allocate a dtype_tbl_t
2557 				 */
2558 				elem_tbl[i].ett_dtype =
2559 				    build_dtype_tbl(attr->defaultValue);
2560 				/* This could have returned NULL */
2561 			}
2562 		}
2563 	}
2564 	xmlFreeDtd(dtd);
2565 }
2566 
2567 /*
2568  * build_dtype_tbl() parses the supplied data and returns an array (max size
2569  * of 10, increase if required) of dtype_tbl_t structures holding data type
2570  * information for an element. The supplied data is assumed to be in "a-dtype"
2571  * format. The dtype_tbl_t array is NULL terminated, which is why space for
2572  * 11 members is allocated.
2573  */
2574 static dtype_tbl_t
2575 (*build_dtype_tbl(const xmlChar *rawdata))[]
2576 {
2577 	char *tok;
2578 	char *lasts;
2579 	dtype_tbl_t (*tbl)[];
2580 	int j = 0;
2581 	xmlChar *data;
2582 	const int max_attr = 11; /* Not more than 10 types per element */
2583 
2584 	/*
2585 	 * Parse the supplied data, assumed to be in a-dtype format, and
2586 	 * generate a lookup table which is indexed by the name and contains
2587 	 * the data type
2588 	 */
2589 	if (rawdata == NULL)
2590 	return (NULL);
2591 	if ((data = xmlStrdup(rawdata)) == NULL)
2592 	return (NULL);
2593 	if ((tbl = calloc(max_attr, sizeof (dtype_tbl_t))) == NULL) {
2594 		xmlFree(data);
2595 		return (NULL);
2596 	}
2597 	for (tok = strtok_r((char *)data, "	 ", &lasts); tok != NULL;
2598 	    tok = strtok_r(NULL, "	 ", &lasts)) {
2599 		    int i;
2600 		    (*tbl)[j].dt_name  = xmlStrdup(BAD_CAST tok);
2601 		    if ((tok = strtok_r(NULL, "	 ", &lasts)) == NULL) {
2602 			    int k = j;
2603 			    for (j = 0; j < k; j++)
2604 				    free((*tbl)[j].dt_name);
2605 			    pool_seterror(POE_DATASTORE);
2606 			    xmlFree(data);
2607 			    free(tbl);
2608 			    return (NULL);
2609 		    }
2610 		    for (i = 0; i < (sizeof (data_type_tags) /
2611 			sizeof (data_type_tags[0])); i++) {
2612 				if (strcmp(tok, data_type_tags[i]) == 0)
2613 				(*tbl)[j++].dt_type = i;
2614 			}
2615 		    if (j == max_attr) { /* too many attributes, bail out */
2616 			    for (j = 0; j < max_attr; j++)
2617 			    free((*tbl)[j].dt_name);
2618 			    free(tbl);
2619 			    xmlFree(data);
2620 			    return (NULL);
2621 		    }
2622 	    }
2623 	(*tbl)[j].dt_name = NULL; /* Terminate the table */
2624 	xmlFree(data);
2625 	return (tbl);
2626 }
2627 
2628 /*
2629  * get_fast_dtype() finds the data type for a supplied attribute name on a
2630  * supplied node. This is called get_fast_dtype() because it uses the cached
2631  * data type information created at library initialisation.
2632  */
2633 static int
2634 get_fast_dtype(xmlNodePtr node, xmlChar *name)
2635 {
2636 	int i;
2637 	xmlElementPtr elem;
2638 
2639 	if ((elem = xmlGetDtdElementDesc(node->doc->extSubset, node->name))
2640 	    == NULL) {
2641 		pool_seterror(POE_BADPARAM);
2642 		return (POC_INVAL);
2643 	}
2644 
2645 	for (i = 0; i < ELEM_TYPE_COUNT; i++) {
2646 		if (xmlStrcmp(elem_tbl[i].ett_elem, elem->name) == 0) {
2647 			dtype_tbl_t (*tbl)[] = elem_tbl[i].ett_dtype;
2648 			int j = 0;
2649 
2650 			if (tbl == NULL)
2651 				break;
2652 			for (j = 0; (*tbl)[j].dt_name != NULL; j++)
2653 				if (xmlStrcmp(name, (*tbl)[j].dt_name) == 0)
2654 					return ((*tbl)[j].dt_type); /* found */
2655 			break; /* if we didn't find it in the elem, break */
2656 		}
2657 	}
2658 	/* If we can't find it, say it's a string */
2659 	return (POC_STRING);
2660 }
2661 
2662 /*
2663  * pool_xml_parse_document() parses the file associated with a supplied
2664  * configuration to regenerate the runtime representation. The supplied
2665  * configuration must reference an already opened file and this is used
2666  * to generate the XML representation via the configuration provider's
2667  * pxc_doc member.
2668  * size must be >=4 in order for "content encoding detection" to work.
2669  */
2670 static int
2671 pool_xml_parse_document(pool_conf_t *conf)
2672 {
2673 	int res;
2674 	char chars[PAGE_READ_SIZE];
2675 	struct stat f_stat;
2676 	xmlParserCtxtPtr ctxt;
2677 	size_t size;
2678 	pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov;
2679 	xmlNodePtr root;
2680 	pool_resource_t **rsl;
2681 	uint_t nelem;
2682 	int i;
2683 
2684 	if (fstat(fileno(prov->pxc_file), &f_stat) == -1) {
2685 		pool_seterror(POE_SYSTEM);
2686 		return (PO_FAIL);
2687 	}
2688 
2689 	if (f_stat.st_size == 0) {
2690 		pool_seterror(POE_INVALID_CONF);
2691 		return (PO_FAIL);
2692 	} else
2693 		size = f_stat.st_size < 4 ? 4 : PAGE_READ_SIZE;
2694 
2695 	res = fread(chars, 1, size, prov->pxc_file);
2696 
2697 	if (res >= 4) {
2698 		xmlValidCtxtPtr cvp;
2699 
2700 		if ((ctxt = xmlCreatePushParserCtxt(NULL, NULL,
2701 		    chars, res, conf->pc_location)) == NULL) {
2702 			pool_seterror(POE_INVALID_CONF);
2703 			return (PO_FAIL);
2704 		}
2705 
2706 		while ((res = fread(chars, 1, size, prov->pxc_file)) > 0) {
2707 			if (xmlParseChunk(ctxt, chars, res, 0) != 0) {
2708 				xmlFreeParserCtxt(ctxt);
2709 				pool_seterror(POE_INVALID_CONF);
2710 				return (PO_FAIL);
2711 			}
2712 		}
2713 		if (xmlParseChunk(ctxt, chars, 0, 1) != 0) {
2714 			xmlFreeParserCtxt(ctxt);
2715 			pool_seterror(POE_INVALID_CONF);
2716 			return (PO_FAIL);
2717 		}
2718 
2719 		if ((cvp = xmlNewValidCtxt()) == NULL) {
2720 			pool_seterror(POE_INVALID_CONF);
2721 			return (PO_FAIL);
2722 		}
2723 		cvp->error    = pool_error_func;
2724 		cvp->warning  = pool_error_func;
2725 
2726 		if (xmlValidateDocument(cvp, ctxt->myDoc) == 0) {
2727 			xmlFreeValidCtxt(cvp);
2728 			xmlFreeParserCtxt(ctxt);
2729 			pool_seterror(POE_INVALID_CONF);
2730 			return (PO_FAIL);
2731 		}
2732 		prov->pxc_doc = ctxt->myDoc;
2733 		xmlFreeValidCtxt(cvp);
2734 		xmlFreeParserCtxt(ctxt);
2735 	}
2736 	if (prov->pxc_doc == NULL) {
2737 		pool_seterror(POE_INVALID_CONF);
2738 		return (PO_FAIL);
2739 	}
2740 	prov->pxc_doc->_private = conf;
2741 
2742 	/* Get the root element */
2743 	if ((root = xmlDocGetRootElement(prov->pxc_doc)) == NULL) {
2744 		pool_seterror(POE_INVALID_CONF);
2745 		return (PO_FAIL);
2746 	}
2747 	/*
2748 	 * Ensure that the parsed tree has been contained within
2749 	 * our shadow tree.
2750 	 */
2751 	if (create_shadow(root) != PO_SUCCESS) {
2752 		pool_seterror(POE_INVALID_CONF);
2753 		return (PO_FAIL);
2754 	}
2755 
2756 	if (pool_xml_validate(conf, POV_STRICT) != PO_SUCCESS) {
2757 		return (PO_FAIL);
2758 	}
2759 	/*
2760 	 * For backwards compatibility with S9, make sure that all
2761 	 * resources have a size and that it is correct.
2762 	 */
2763 	if ((rsl = pool_query_resources(conf, &nelem, NULL)) != NULL) {
2764 		pool_value_t val = POOL_VALUE_INITIALIZER;
2765 		for (i = 0; i < nelem; i++) {
2766 			if (pool_get_ns_property(TO_ELEM(rsl[i]), c_size_prop,
2767 			    &val) != POC_UINT) {
2768 				pool_component_t **cs;
2769 				uint_t size;
2770 				if ((cs = pool_query_resource_components(conf,
2771 				    rsl[i], &size, NULL)) != NULL) {
2772 					free(cs);
2773 					pool_value_set_uint64(&val, size);
2774 				} else
2775 					pool_value_set_uint64(&val, 0);
2776 				if (pool_put_any_ns_property(TO_ELEM(rsl[i]),
2777 				    c_size_prop, &val)  != PO_SUCCESS) {
2778 					free(rsl);
2779 					return (PO_FAIL);
2780 				}
2781 			}
2782 		}
2783 		free(rsl);
2784 	}
2785 	return (PO_SUCCESS);
2786 }
2787