xref: /titanic_51/usr/src/lib/libbrand/common/libbrand.c (revision 264a6e7478846334593be7663fb6b1a8f37784a0)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <fnmatch.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <strings.h>
37 #include <synch.h>
38 #include <sys/brand.h>
39 #include <sys/fcntl.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/systeminfo.h>
43 #include <sys/types.h>
44 #include <thread.h>
45 #include <zone.h>
46 
47 #include <libbrand_impl.h>
48 #include <libbrand.h>
49 
50 #define	DTD_ELEM_BOOT		((const xmlChar *) "boot")
51 #define	DTD_ELEM_BRAND		((const xmlChar *) "brand")
52 #define	DTD_ELEM_COMMENT	((const xmlChar *) "comment")
53 #define	DTD_ELEM_DEVICE		((const xmlChar *) "device")
54 #define	DTD_ELEM_GLOBAL_MOUNT	((const xmlChar *) "global_mount")
55 #define	DTD_ELEM_HALT		((const xmlChar *) "halt")
56 #define	DTD_ELEM_INITNAME	((const xmlChar *) "initname")
57 #define	DTD_ELEM_INSTALL	((const xmlChar *) "install")
58 #define	DTD_ELEM_INSTALLOPTS	((const xmlChar *) "installopts")
59 #define	DTD_ELEM_LOGIN_CMD	((const xmlChar *) "login_cmd")
60 #define	DTD_ELEM_MODNAME	((const xmlChar *) "modname")
61 #define	DTD_ELEM_MOUNT		((const xmlChar *) "mount")
62 #define	DTD_ELEM_POSTCLONE	((const xmlChar *) "postclone")
63 #define	DTD_ELEM_PRIVILEGE	((const xmlChar *) "privilege")
64 #define	DTD_ELEM_SYMLINK	((const xmlChar *) "symlink")
65 #define	DTD_ELEM_VERIFY_CFG	((const xmlChar *) "verify_cfg")
66 #define	DTD_ELEM_VERIFY_ADM	((const xmlChar *) "verify_adm")
67 
68 #define	DTD_ATTR_ARCH		((const xmlChar *) "arch")
69 #define	DTD_ATTR_DIRECTORY	((const xmlChar *) "directory")
70 #define	DTD_ATTR_MATCH		((const xmlChar *) "match")
71 #define	DTD_ATTR_MODE		((const xmlChar *) "mode")
72 #define	DTD_ATTR_NAME		((const xmlChar *) "name")
73 #define	DTD_ATTR_OPT		((const xmlChar *) "opt")
74 #define	DTD_ATTR_PATH		((const xmlChar *) "path")
75 #define	DTD_ATTR_SET		((const xmlChar *) "set")
76 #define	DTD_ATTR_SOURCE		((const xmlChar *) "source")
77 #define	DTD_ATTR_SPECIAL	((const xmlChar *) "special")
78 #define	DTD_ATTR_TARGET		((const xmlChar *) "target")
79 #define	DTD_ATTR_TYPE		((const xmlChar *) "type")
80 
81 static volatile boolean_t	libbrand_initialized = B_FALSE;
82 static char			i_curr_arch[MAXNAMELEN];
83 static char			i_curr_zone[ZONENAME_MAX];
84 
85 /*ARGSUSED*/
86 static void
87 brand_error_func(void *ctx, const char *msg, ...)
88 {
89 	/*
90 	 * Ignore error messages from libxml
91 	 */
92 }
93 
94 static boolean_t
95 libbrand_initialize()
96 {
97 	static mutex_t initialize_lock = DEFAULTMUTEX;
98 
99 	(void) mutex_lock(&initialize_lock);
100 
101 	if (libbrand_initialized) {
102 		(void) mutex_unlock(&initialize_lock);
103 		return (B_TRUE);
104 	}
105 
106 	if (sysinfo(SI_ARCHITECTURE, i_curr_arch, sizeof (i_curr_arch)) < 0) {
107 		(void) mutex_unlock(&initialize_lock);
108 		return (B_FALSE);
109 	}
110 
111 	if (getzonenamebyid(getzoneid(), i_curr_zone,
112 	    sizeof (i_curr_zone)) < 0) {
113 		(void) mutex_unlock(&initialize_lock);
114 		return (B_FALSE);
115 	}
116 
117 	/*
118 	 * Note that here we're initializing per-process libxml2
119 	 * state.  By doing so we're implicitly assuming that
120 	 * no other code in this process is also trying to
121 	 * use libxml2.  But in most case we know this not to
122 	 * be true since we're almost always used in conjunction
123 	 * with libzonecfg, which also uses libxml2.  Lucky for
124 	 * us, libzonecfg initializes libxml2 to essentially
125 	 * the same defaults as we're using below.
126 	 */
127 	xmlLineNumbersDefault(1);
128 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
129 	xmlDoValidityCheckingDefaultValue = 1;
130 	(void) xmlKeepBlanksDefault(0);
131 	xmlGetWarningsDefaultValue = 0;
132 	xmlSetGenericErrorFunc(NULL, brand_error_func);
133 
134 	libbrand_initialized = B_TRUE;
135 	(void) mutex_unlock(&initialize_lock);
136 	return (B_TRUE);
137 }
138 
139 static const char *
140 get_curr_arch(void)
141 {
142 	if (!libbrand_initialize())
143 		return (NULL);
144 
145 	return (i_curr_arch);
146 }
147 
148 static const char *
149 get_curr_zone(void)
150 {
151 	if (!libbrand_initialize())
152 		return (NULL);
153 
154 	return (i_curr_zone);
155 }
156 
157 /*
158  * Internal function to open an XML file
159  *
160  * Returns the XML doc pointer, or NULL on failure.  It will validate the
161  * document, as well as removing any comments from the document structure.
162  */
163 static xmlDocPtr
164 open_xml_file(const char *file)
165 {
166 	xmlDocPtr doc;
167 	xmlValidCtxtPtr cvp;
168 	int valid;
169 
170 	if (!libbrand_initialize())
171 		return (NULL);
172 
173 	/*
174 	 * Parse the file
175 	 */
176 	if ((doc = xmlParseFile(file)) == NULL)
177 		return (NULL);
178 
179 	/*
180 	 * Validate the file
181 	 */
182 	if ((cvp = xmlNewValidCtxt()) == NULL) {
183 		xmlFreeDoc(doc);
184 		return (NULL);
185 	}
186 	cvp->error = brand_error_func;
187 	cvp->warning = brand_error_func;
188 	valid = xmlValidateDocument(cvp, doc);
189 	xmlFreeValidCtxt(cvp);
190 	if (valid == 0) {
191 		xmlFreeDoc(doc);
192 		return (NULL);
193 	}
194 
195 	return (doc);
196 }
197 /*
198  * Open a handle to the named brand.
199  *
200  * Returns a handle to the named brand, which is used for all subsequent brand
201  * interaction, or NULL if unable to open or initialize the brand.
202  */
203 brand_handle_t *
204 brand_open(const char *name)
205 {
206 	brand_handle_t *bhp;
207 	char path[MAXPATHLEN];
208 	xmlNodePtr node;
209 	xmlChar *property;
210 	struct stat statbuf;
211 
212 	/*
213 	 * Make sure brand name isn't too long
214 	 */
215 	if (strlen(name) >= MAXNAMELEN)
216 		return (NULL);
217 
218 	/*
219 	 * Check that the brand exists
220 	 */
221 	(void) snprintf(path, sizeof (path), "%s/%s", BRAND_DIR, name);
222 
223 	if (stat(path, &statbuf) != 0)
224 		return (NULL);
225 
226 	/*
227 	 * Allocate brand handle
228 	 */
229 	if ((bhp = malloc(sizeof (brand_handle_t))) == NULL)
230 		return (NULL);
231 	bzero(bhp, sizeof (brand_handle_t));
232 
233 	(void) strcpy(bhp->bh_name, name);
234 
235 	/*
236 	 * Open the configuration file
237 	 */
238 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
239 	    BRAND_CONFIG);
240 	if ((bhp->bh_config = open_xml_file(path)) == NULL) {
241 		brand_close(bhp);
242 		return (NULL);
243 	}
244 
245 	/*
246 	 * Verify that the name of the brand matches the directory in which it
247 	 * is installed.
248 	 */
249 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL) {
250 		brand_close(bhp);
251 		return (NULL);
252 	}
253 
254 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0) {
255 		brand_close(bhp);
256 		return (NULL);
257 	}
258 
259 	if ((property = xmlGetProp(node, DTD_ATTR_NAME)) == NULL) {
260 		brand_close(bhp);
261 		return (NULL);
262 	}
263 
264 	if (strcmp((char *)property, name) != 0) {
265 		xmlFree(property);
266 		brand_close(bhp);
267 		return (NULL);
268 	}
269 	xmlFree(property);
270 
271 	/*
272 	 * Open handle to platform configuration file.
273 	 */
274 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
275 	    BRAND_PLATFORM);
276 	if ((bhp->bh_platform = open_xml_file(path)) == NULL) {
277 		brand_close(bhp);
278 		return (NULL);
279 	}
280 
281 	return (bhp);
282 }
283 
284 /*
285  * Closes the given brand handle
286  */
287 void
288 brand_close(brand_handle_t *bhp)
289 {
290 	if (bhp->bh_platform != NULL)
291 		xmlFreeDoc(bhp->bh_platform);
292 	if (bhp->bh_config != NULL)
293 		xmlFreeDoc(bhp->bh_config);
294 	free(bhp);
295 }
296 
297 static int
298 i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
299     const char *zonename, const char *zoneroot, const char *username,
300     const char *curr_zone, int argc, char **argv)
301 {
302 	int dst, src, i;
303 
304 	assert(argc >= 0);
305 	assert((argc == 0) || (argv != NULL));
306 
307 	/*
308 	 * Walk through the characters, substituting values as needed.
309 	 */
310 	dbuf[0] = '\0';
311 	dst = 0;
312 	for (src = 0; src < strlen((char *)sbuf) && dst < dbuf_size; src++) {
313 		if (sbuf[src] != '%') {
314 			dbuf[dst++] = sbuf[src];
315 			continue;
316 		}
317 
318 		switch (sbuf[++src]) {
319 		case '%':
320 			dst += strlcpy(dbuf + dst, "%", dbuf_size - dst);
321 			break;
322 		case 'R':
323 			if (zoneroot == NULL)
324 				break;
325 			dst += strlcpy(dbuf + dst, zoneroot, dbuf_size - dst);
326 			break;
327 		case 'u':
328 			if (username == NULL)
329 				break;
330 			dst += strlcpy(dbuf + dst, username, dbuf_size - dst);
331 			break;
332 		case 'Z':
333 			if (curr_zone == NULL)
334 				break;
335 			/* name of the zone we're running in */
336 			dst += strlcpy(dbuf + dst, curr_zone, dbuf_size - dst);
337 			break;
338 		case 'z':
339 			/* name of the zone we're operating on */
340 			if (zonename == NULL)
341 				break;
342 			dst += strlcpy(dbuf + dst, zonename, dbuf_size - dst);
343 			break;
344 		case '*':
345 			if (argv == NULL)
346 				break;
347 			for (i = 0; i < argc; i++)
348 				dst += snprintf(dbuf + dst, dbuf_size - dst,
349 				    " \"%s\"", argv[i]);
350 			break;
351 		}
352 	}
353 
354 	if (dst >= dbuf_size)
355 		return (-1);
356 
357 	dbuf[dst] = '\0';
358 	return (0);
359 }
360 
361 /*
362  * Retrieve the given tag from the brand.
363  * Perform the following substitutions as necessary:
364  *
365  *	%%	%
366  *	%u	Username
367  *	%z	Name of target zone
368  *	%Z	Name of current zone
369  *	%R	Root of zone
370  *	%*	Additional arguments (argc, argv)
371  *
372  * Returns 0 on success, -1 on failure.
373  */
374 static int
375 brand_get_value(brand_handle_t *bhp, const char *zonename,
376     const char *zoneroot, const char *username, const char *curr_zone,
377     char *buf, size_t len, int argc, char **argv, const xmlChar *tagname,
378     boolean_t substitute, boolean_t optional)
379 {
380 	xmlNodePtr node;
381 	xmlChar *content;
382 	int err = 0;
383 
384 	/*
385 	 * Retrieve the specified value from the XML doc
386 	 */
387 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
388 		return (-1);
389 
390 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0)
391 		return (-1);
392 
393 	for (node = node->xmlChildrenNode; node != NULL;
394 	    node = node->next) {
395 		if (xmlStrcmp(node->name, tagname) == 0)
396 			break;
397 	}
398 
399 	if (node == NULL)
400 		return (-1);
401 
402 	if ((content = xmlNodeGetContent(node)) == NULL)
403 		return (-1);
404 
405 	if (strlen((char *)content) == 0) {
406 		/*
407 		 * If the entry in the config file is empty, check to see
408 		 * whether this is an optional field.  If so, we return the
409 		 * empty buffer.  If not, we return an error.
410 		 */
411 		if (optional) {
412 			buf[0] = '\0';
413 		} else {
414 			err = -1;
415 		}
416 	} else {
417 		/* Substitute token values as needed. */
418 		if (substitute) {
419 			if (i_substitute_tokens((char *)content, buf, len,
420 			    zonename, zoneroot, username, curr_zone,
421 			    argc, argv) != 0)
422 				err = -1;
423 		} else {
424 			if (strlcpy(buf, (char *)content, len) >= len)
425 				err = -1;
426 		}
427 	}
428 
429 	xmlFree(content);
430 
431 	return (err);
432 }
433 
434 int
435 brand_get_boot(brand_handle_t *bhp, const char *zonename,
436     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
437 {
438 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
439 	    buf, len, argc, argv, DTD_ELEM_BOOT, B_TRUE, B_TRUE));
440 }
441 
442 int
443 brand_get_brandname(brand_handle_t *bhp, char *buf, size_t len)
444 {
445 	if (len <= strlen(bhp->bh_name))
446 		return (-1);
447 
448 	(void) strcpy(buf, bhp->bh_name);
449 
450 	return (0);
451 }
452 
453 int
454 brand_get_halt(brand_handle_t *bhp, const char *zonename,
455     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
456 {
457 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
458 	    buf, len, argc, argv, DTD_ELEM_HALT, B_TRUE, B_TRUE));
459 }
460 
461 int
462 brand_get_initname(brand_handle_t *bhp, char *buf, size_t len)
463 {
464 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
465 	    buf, len, 0, NULL, DTD_ELEM_INITNAME, B_FALSE, B_FALSE));
466 }
467 
468 int
469 brand_get_login_cmd(brand_handle_t *bhp, const char *username,
470     char *buf, size_t len)
471 {
472 	const char *curr_zone = get_curr_zone();
473 	return (brand_get_value(bhp, NULL, NULL, username, curr_zone,
474 	    buf, len, 0, NULL, DTD_ELEM_LOGIN_CMD, B_TRUE, B_FALSE));
475 }
476 
477 int
478 brand_get_install(brand_handle_t *bhp, const char *zonename,
479     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
480 {
481 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
482 	    buf, len, argc, argv, DTD_ELEM_INSTALL, B_TRUE, B_FALSE));
483 }
484 
485 int
486 brand_get_installopts(brand_handle_t *bhp, char *buf, size_t len)
487 {
488 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
489 	    buf, len, 0, NULL, DTD_ELEM_INSTALLOPTS, B_FALSE, B_TRUE));
490 }
491 
492 int
493 brand_get_modname(brand_handle_t *bhp, char *buf, size_t len)
494 {
495 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
496 	    buf, len, 0, NULL, DTD_ELEM_MODNAME, B_FALSE, B_TRUE));
497 }
498 
499 int
500 brand_get_postclone(brand_handle_t *bhp, const char *zonename,
501     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
502 {
503 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
504 	    buf, len, argc, argv, DTD_ELEM_POSTCLONE, B_TRUE, B_TRUE));
505 }
506 
507 int
508 brand_get_verify_cfg(brand_handle_t *bhp, char *buf, size_t len)
509 {
510 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
511 	    buf, len, 0, NULL, DTD_ELEM_VERIFY_CFG, B_FALSE, B_TRUE));
512 }
513 
514 int
515 brand_get_verify_adm(brand_handle_t *bhp, const char *zonename,
516     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
517 {
518 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
519 	    buf, len, argc, argv, DTD_ELEM_VERIFY_ADM, B_TRUE, B_TRUE));
520 }
521 
522 int
523 brand_is_native(brand_handle_t *bhp)
524 {
525 	return ((strcmp(bhp->bh_name, NATIVE_BRAND_NAME) == 0) ? 1 : 0);
526 }
527 
528 /*
529  * Iterate over brand privileges
530  *
531  * Walks the brand config, searching for <privilege> elements, calling the
532  * specified callback for each.  Returns 0 on success, or -1 on failure.
533  */
534 int
535 brand_config_iter_privilege(brand_handle_t *bhp, int (*func)(void *,
536     const char *, const char *), void *data)
537 {
538 	xmlNodePtr		node;
539 	xmlChar			*name, *set;
540 	int			ret;
541 
542 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
543 		return (-1);
544 
545 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
546 
547 		if (xmlStrcmp(node->name, DTD_ELEM_PRIVILEGE) != 0)
548 			continue;
549 
550 		name = xmlGetProp(node, DTD_ATTR_NAME);
551 		set = xmlGetProp(node, DTD_ATTR_SET);
552 
553 		if (name == NULL || set == NULL) {
554 			if (name != NULL)
555 				xmlFree(name);
556 			if (set != NULL)
557 				xmlFree(set);
558 			return (-1);
559 		}
560 
561 		ret = func(data, (const char *)name, (const char *)set);
562 
563 		xmlFree(name);
564 		xmlFree(set);
565 
566 		if (ret != 0)
567 			return (-1);
568 	}
569 
570 	return (0);
571 }
572 
573 static int
574 i_brand_platform_iter_mounts(brand_handle_t *bhp, const char *zoneroot,
575     int (*func)(void *, const char *, const char *, const char *,
576     const char *), void *data, const xmlChar *mount_type)
577 {
578 	xmlNodePtr node;
579 	xmlChar *special, *dir, *type, *opt;
580 	char special_exp[MAXPATHLEN];
581 	char opt_exp[MAXPATHLEN];
582 	int ret;
583 
584 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
585 		return (-1);
586 
587 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
588 
589 		if (xmlStrcmp(node->name, mount_type) != 0)
590 			continue;
591 
592 		special = xmlGetProp(node, DTD_ATTR_SPECIAL);
593 		dir = xmlGetProp(node, DTD_ATTR_DIRECTORY);
594 		type = xmlGetProp(node, DTD_ATTR_TYPE);
595 		opt = xmlGetProp(node, DTD_ATTR_OPT);
596 		if ((special == NULL) || (dir == NULL) || (type == NULL) ||
597 		    (opt == NULL)) {
598 			ret = -1;
599 			goto next;
600 		}
601 
602 		/* Substitute token values as needed. */
603 		if ((ret = i_substitute_tokens((char *)special,
604 		    special_exp, sizeof (special_exp),
605 		    NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
606 			goto next;
607 
608 		/* opt might not be defined */
609 		if (strlen((const char *)opt) == 0) {
610 			xmlFree(opt);
611 			opt = NULL;
612 		} else {
613 			if ((ret = i_substitute_tokens((char *)opt,
614 			    opt_exp, sizeof (opt_exp),
615 			    NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
616 				goto next;
617 		}
618 
619 		ret = func(data, (char *)special_exp, (char *)dir,
620 		    (char *)type, ((opt != NULL) ? opt_exp : NULL));
621 
622 next:
623 		if (special != NULL)
624 			xmlFree(special);
625 		if (dir != NULL)
626 			xmlFree(dir);
627 		if (type != NULL)
628 			xmlFree(type);
629 		if (opt != NULL)
630 			xmlFree(opt);
631 		if (ret != 0)
632 			return (-1);
633 	}
634 	return (0);
635 }
636 
637 
638 /*
639  * Iterate over global platform filesystems
640  *
641  * Walks the platform, searching for <global_mount> elements, calling the
642  * specified callback for each.  Returns 0 on success, or -1 on failure.
643  *
644  * Perform the following substitutions as necessary:
645  *
646  *	%R	Root of zone
647  */
648 int
649 brand_platform_iter_gmounts(brand_handle_t *bhp, const char *zoneroot,
650     int (*func)(void *, const char *, const char *, const char *,
651     const char *), void *data)
652 {
653 	return (i_brand_platform_iter_mounts(bhp, zoneroot, func, data,
654 	    DTD_ELEM_GLOBAL_MOUNT));
655 }
656 
657 /*
658  * Iterate over non-global zone platform filesystems
659  *
660  * Walks the platform, searching for <mount> elements, calling the
661  * specified callback for each.  Returns 0 on success, or -1 on failure.
662  */
663 int
664 brand_platform_iter_mounts(brand_handle_t *bhp, int (*func)(void *,
665     const char *, const char *, const char *, const char *), void *data)
666 {
667 	return (i_brand_platform_iter_mounts(bhp, NULL, func, data,
668 	    DTD_ELEM_MOUNT));
669 }
670 
671 /*
672  * Iterate over platform symlinks
673  *
674  * Walks the platform, searching for <symlink> elements, calling the
675  * specified callback for each.  Returns 0 on success, or -1 on failure.
676  */
677 int
678 brand_platform_iter_link(brand_handle_t *bhp,
679     int (*func)(void *, const char *, const char *), void *data)
680 {
681 	xmlNodePtr node;
682 	xmlChar *source, *target;
683 	int ret;
684 
685 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
686 		return (-1);
687 
688 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
689 
690 		if (xmlStrcmp(node->name, DTD_ELEM_SYMLINK) != 0)
691 			continue;
692 
693 		source = xmlGetProp(node, DTD_ATTR_SOURCE);
694 		target = xmlGetProp(node, DTD_ATTR_TARGET);
695 
696 		if (source == NULL || target == NULL) {
697 			if (source != NULL)
698 				xmlFree(source);
699 			if (target != NULL)
700 				xmlFree(target);
701 			return (-1);
702 		}
703 
704 		ret = func(data, (char *)source, (char *)target);
705 
706 		xmlFree(source);
707 		xmlFree(target);
708 
709 		if (ret != 0)
710 			return (-1);
711 	}
712 
713 	return (0);
714 }
715 
716 /*
717  * Iterate over platform devices
718  *
719  * Walks the platform, searching for <device> elements, calling the
720  * specified callback for each.  Returns 0 on success, or -1 on failure.
721  */
722 int
723 brand_platform_iter_devices(brand_handle_t *bhp, const char *zonename,
724     int (*func)(void *, const char *, const char *), void *data)
725 {
726 	const char	*curr_arch = get_curr_arch();
727 	xmlNodePtr	node;
728 	xmlChar		*match, *name, *arch;
729 	char		match_exp[MAXPATHLEN];
730 	boolean_t	err = B_FALSE;
731 	int		ret = 0;
732 
733 
734 	assert(bhp != NULL);
735 	assert(zonename != NULL);
736 	assert(func != NULL);
737 
738 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
739 		return (-1);
740 
741 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
742 
743 		if (xmlStrcmp(node->name, DTD_ELEM_DEVICE) != 0)
744 			continue;
745 
746 		match = xmlGetProp(node, DTD_ATTR_MATCH);
747 		name = xmlGetProp(node, DTD_ATTR_NAME);
748 		arch = xmlGetProp(node, DTD_ATTR_ARCH);
749 		if ((match == NULL) || (name == NULL) || (arch == NULL)) {
750 			err = B_TRUE;
751 			goto next;
752 		}
753 
754 		/* check if the arch matches */
755 		if ((strcmp((char *)arch, "all") != 0) &&
756 		    (strcmp((char *)arch, curr_arch) != 0))
757 			goto next;
758 
759 		/* Substitute token values as needed. */
760 		if ((ret = i_substitute_tokens((char *)match,
761 		    match_exp, sizeof (match_exp),
762 		    zonename, NULL, NULL, NULL, 0, NULL)) != 0) {
763 			err = B_TRUE;
764 			goto next;
765 		}
766 
767 		/* name might not be defined */
768 		if (strlen((const char *)name) == 0) {
769 			xmlFree(name);
770 			name = NULL;
771 		}
772 
773 		/* invoke the callback */
774 		ret = func(data, (const char *)match_exp, (const char *)name);
775 
776 next:
777 		if (match != NULL)
778 			xmlFree(match);
779 		if (name != NULL)
780 			xmlFree(name);
781 		if (arch != NULL)
782 			xmlFree(arch);
783 		if (err)
784 			return (-1);
785 		if (ret != 0)
786 			return (-1);
787 	}
788 
789 	return (0);
790 }
791