xref: /illumos-gate/usr/src/lib/libbrand/common/libbrand.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2015, Joyent, Inc.
25  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26  */
27 
28 #include <assert.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <fnmatch.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <strings.h>
36 #include <synch.h>
37 #include <sys/brand.h>
38 #include <sys/fcntl.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/systeminfo.h>
42 #include <sys/types.h>
43 #include <thread.h>
44 #include <zone.h>
45 
46 #include <libbrand_impl.h>
47 #include <libbrand.h>
48 
49 #define	DTD_ELEM_ATTACH		((const xmlChar *) "attach")
50 #define	DTD_ELEM_BOOT		((const xmlChar *) "boot")
51 #define	DTD_ELEM_BRAND		((const xmlChar *) "brand")
52 #define	DTD_ELEM_CLONE		((const xmlChar *) "clone")
53 #define	DTD_ELEM_COMMENT	((const xmlChar *) "comment")
54 #define	DTD_ELEM_DETACH		((const xmlChar *) "detach")
55 #define	DTD_ELEM_DEVICE		((const xmlChar *) "device")
56 #define	DTD_ELEM_GLOBAL_MOUNT	((const xmlChar *) "global_mount")
57 #define	DTD_ELEM_HALT		((const xmlChar *) "halt")
58 #define	DTD_ELEM_INITNAME	((const xmlChar *) "initname")
59 #define	DTD_ELEM_INSTALL	((const xmlChar *) "install")
60 #define	DTD_ELEM_INSTALLOPTS	((const xmlChar *) "installopts")
61 #define	DTD_ELEM_LOGIN_CMD	((const xmlChar *) "login_cmd")
62 #define	DTD_ELEM_FORCELOGIN_CMD	((const xmlChar *) "forcedlogin_cmd")
63 #define	DTD_ELEM_MODNAME	((const xmlChar *) "modname")
64 #define	DTD_ELEM_MOUNT		((const xmlChar *) "mount")
65 #define	DTD_ELEM_RESTARTINIT	((const xmlChar *) "restartinit")
66 #define	DTD_ELEM_POSTATTACH	((const xmlChar *) "postattach")
67 #define	DTD_ELEM_POSTCLONE	((const xmlChar *) "postclone")
68 #define	DTD_ELEM_POSTINSTALL	((const xmlChar *) "postinstall")
69 #define	DTD_ELEM_POSTSNAP	((const xmlChar *) "postsnap")
70 #define	DTD_ELEM_POSTSTATECHG	((const xmlChar *) "poststatechange")
71 #define	DTD_ELEM_PREDETACH	((const xmlChar *) "predetach")
72 #define	DTD_ELEM_PRESNAP	((const xmlChar *) "presnap")
73 #define	DTD_ELEM_PRESTATECHG	((const xmlChar *) "prestatechange")
74 #define	DTD_ELEM_PREUNINSTALL	((const xmlChar *) "preuninstall")
75 #define	DTD_ELEM_PRIVILEGE	((const xmlChar *) "privilege")
76 #define	DTD_ELEM_QUERY		((const xmlChar *) "query")
77 #define	DTD_ELEM_SHUTDOWN	((const xmlChar *) "shutdown")
78 #define	DTD_ELEM_SYMLINK	((const xmlChar *) "symlink")
79 #define	DTD_ELEM_SYSBOOT	((const xmlChar *) "sysboot")
80 #define	DTD_ELEM_UNINSTALL	((const xmlChar *) "uninstall")
81 #define	DTD_ELEM_USER_CMD	((const xmlChar *) "user_cmd")
82 #define	DTD_ELEM_VALIDSNAP	((const xmlChar *) "validatesnap")
83 #define	DTD_ELEM_VERIFY_CFG	((const xmlChar *) "verify_cfg")
84 #define	DTD_ELEM_VERIFY_ADM	((const xmlChar *) "verify_adm")
85 
86 #define	DTD_ATTR_ALLOWEXCL	((const xmlChar *) "allow-exclusive-ip")
87 #define	DTD_ATTR_ARCH		((const xmlChar *) "arch")
88 #define	DTD_ATTR_DIRECTORY	((const xmlChar *) "directory")
89 #define	DTD_ATTR_IPTYPE		((const xmlChar *) "ip-type")
90 #define	DTD_ATTR_MATCH		((const xmlChar *) "match")
91 #define	DTD_ATTR_MODE		((const xmlChar *) "mode")
92 #define	DTD_ATTR_NAME		((const xmlChar *) "name")
93 #define	DTD_ATTR_OPT		((const xmlChar *) "opt")
94 #define	DTD_ATTR_PATH		((const xmlChar *) "path")
95 #define	DTD_ATTR_SET		((const xmlChar *) "set")
96 #define	DTD_ATTR_SOURCE		((const xmlChar *) "source")
97 #define	DTD_ATTR_SPECIAL	((const xmlChar *) "special")
98 #define	DTD_ATTR_TARGET		((const xmlChar *) "target")
99 #define	DTD_ATTR_TYPE		((const xmlChar *) "type")
100 
101 #define	DTD_ENTITY_TRUE		"true"
102 
103 static volatile boolean_t	libbrand_initialized = B_FALSE;
104 static char			i_curr_arch[MAXNAMELEN];
105 static char			i_curr_zone[ZONENAME_MAX];
106 
107 /*ARGSUSED*/
108 static void
109 brand_error_func(void *ctx, const char *msg, ...)
110 {
111 	/*
112 	 * Ignore error messages from libxml
113 	 */
114 }
115 
116 static boolean_t
117 libbrand_initialize()
118 {
119 	static mutex_t initialize_lock = DEFAULTMUTEX;
120 
121 	(void) mutex_lock(&initialize_lock);
122 
123 	if (libbrand_initialized) {
124 		(void) mutex_unlock(&initialize_lock);
125 		return (B_TRUE);
126 	}
127 
128 	if (sysinfo(SI_ARCHITECTURE, i_curr_arch, sizeof (i_curr_arch)) < 0) {
129 		(void) mutex_unlock(&initialize_lock);
130 		return (B_FALSE);
131 	}
132 
133 	if (getzonenamebyid(getzoneid(), i_curr_zone,
134 	    sizeof (i_curr_zone)) < 0) {
135 		(void) mutex_unlock(&initialize_lock);
136 		return (B_FALSE);
137 	}
138 
139 	/*
140 	 * Note that here we're initializing per-process libxml2
141 	 * state.  By doing so we're implicitly assuming that
142 	 * no other code in this process is also trying to
143 	 * use libxml2.  But in most case we know this not to
144 	 * be true since we're almost always used in conjunction
145 	 * with libzonecfg, which also uses libxml2.  Lucky for
146 	 * us, libzonecfg initializes libxml2 to essentially
147 	 * the same defaults as we're using below.
148 	 */
149 	(void) xmlLineNumbersDefault(1);
150 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
151 	xmlDoValidityCheckingDefaultValue = 1;
152 	(void) xmlKeepBlanksDefault(0);
153 	xmlGetWarningsDefaultValue = 0;
154 	xmlSetGenericErrorFunc(NULL, brand_error_func);
155 
156 	libbrand_initialized = B_TRUE;
157 	(void) mutex_unlock(&initialize_lock);
158 	return (B_TRUE);
159 }
160 
161 static const char *
162 get_curr_arch(void)
163 {
164 	if (!libbrand_initialize())
165 		return (NULL);
166 
167 	return (i_curr_arch);
168 }
169 
170 static const char *
171 get_curr_zone(void)
172 {
173 	if (!libbrand_initialize())
174 		return (NULL);
175 
176 	return (i_curr_zone);
177 }
178 
179 /*
180  * Internal function to open an XML file
181  *
182  * Returns the XML doc pointer, or NULL on failure.  It will validate the
183  * document, as well as removing any comments from the document structure.
184  */
185 static xmlDocPtr
186 open_xml_file(const char *file)
187 {
188 	xmlDocPtr doc;
189 	xmlValidCtxtPtr cvp;
190 	int valid;
191 
192 	if (!libbrand_initialize())
193 		return (NULL);
194 
195 	/*
196 	 * Parse the file
197 	 */
198 	if ((doc = xmlParseFile(file)) == NULL)
199 		return (NULL);
200 
201 	/*
202 	 * Validate the file
203 	 */
204 	if ((cvp = xmlNewValidCtxt()) == NULL) {
205 		xmlFreeDoc(doc);
206 		return (NULL);
207 	}
208 	cvp->error = brand_error_func;
209 	cvp->warning = brand_error_func;
210 	valid = xmlValidateDocument(cvp, doc);
211 	xmlFreeValidCtxt(cvp);
212 	if (valid == 0) {
213 		xmlFreeDoc(doc);
214 		return (NULL);
215 	}
216 
217 	return (doc);
218 }
219 /*
220  * Open a handle to the named brand.
221  *
222  * Returns a handle to the named brand, which is used for all subsequent brand
223  * interaction, or NULL if unable to open or initialize the brand.
224  */
225 brand_handle_t
226 brand_open(const char *name)
227 {
228 	struct brand_handle *bhp;
229 	char path[MAXPATHLEN];
230 	xmlNodePtr node;
231 	xmlChar *property;
232 	struct stat statbuf;
233 
234 	/*
235 	 * Make sure brand name isn't too long
236 	 */
237 	if (strlen(name) >= MAXNAMELEN)
238 		return (NULL);
239 
240 	/*
241 	 * Check that the brand exists
242 	 */
243 	(void) snprintf(path, sizeof (path), "%s/%s", BRAND_DIR, name);
244 
245 	if (stat(path, &statbuf) != 0)
246 		return (NULL);
247 
248 	/*
249 	 * Allocate brand handle
250 	 */
251 	if ((bhp = malloc(sizeof (struct brand_handle))) == NULL)
252 		return (NULL);
253 	bzero(bhp, sizeof (struct brand_handle));
254 
255 	(void) strcpy(bhp->bh_name, name);
256 
257 	/*
258 	 * Open the configuration file
259 	 */
260 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
261 	    BRAND_CONFIG);
262 	if ((bhp->bh_config = open_xml_file(path)) == NULL) {
263 		brand_close((brand_handle_t)bhp);
264 		return (NULL);
265 	}
266 
267 	/*
268 	 * Verify that the name of the brand matches the directory in which it
269 	 * is installed.
270 	 */
271 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL) {
272 		brand_close((brand_handle_t)bhp);
273 		return (NULL);
274 	}
275 
276 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0) {
277 		brand_close((brand_handle_t)bhp);
278 		return (NULL);
279 	}
280 
281 	if ((property = xmlGetProp(node, DTD_ATTR_NAME)) == NULL) {
282 		brand_close((brand_handle_t)bhp);
283 		return (NULL);
284 	}
285 
286 	if (strcmp((char *)property, name) != 0) {
287 		xmlFree(property);
288 		brand_close((brand_handle_t)bhp);
289 		return (NULL);
290 	}
291 	xmlFree(property);
292 
293 	/*
294 	 * Open handle to platform configuration file.
295 	 */
296 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
297 	    BRAND_PLATFORM);
298 	if ((bhp->bh_platform = open_xml_file(path)) == NULL) {
299 		brand_close((brand_handle_t)bhp);
300 		return (NULL);
301 	}
302 
303 	return ((brand_handle_t)bhp);
304 }
305 
306 /*
307  * Closes the given brand handle
308  */
309 void
310 brand_close(brand_handle_t bh)
311 {
312 	struct brand_handle *bhp = (struct brand_handle *)bh;
313 	if (bhp->bh_platform != NULL)
314 		xmlFreeDoc(bhp->bh_platform);
315 	if (bhp->bh_config != NULL)
316 		xmlFreeDoc(bhp->bh_config);
317 	free(bhp);
318 }
319 
320 static int
321 i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
322     const char *zonename, const char *zonepath, const char *username,
323     const char *curr_zone)
324 {
325 	int dst, src;
326 
327 	/*
328 	 * Walk through the characters, substituting values as needed.
329 	 */
330 	dbuf[0] = '\0';
331 	dst = 0;
332 	for (src = 0; src < strlen((char *)sbuf) && dst < dbuf_size; src++) {
333 		if (sbuf[src] != '%') {
334 			dbuf[dst++] = sbuf[src];
335 			continue;
336 		}
337 
338 		switch (sbuf[++src]) {
339 		case '%':
340 			dst += strlcpy(dbuf + dst, "%", dbuf_size - dst);
341 			break;
342 		case 'R':
343 			if (zonepath == NULL)
344 				break;
345 			dst += strlcpy(dbuf + dst, zonepath, dbuf_size - dst);
346 			break;
347 		case 'u':
348 			if (username == NULL)
349 				break;
350 			dst += strlcpy(dbuf + dst, username, dbuf_size - dst);
351 			break;
352 		case 'Z':
353 			if (curr_zone == NULL)
354 				break;
355 			/* name of the zone we're running in */
356 			dst += strlcpy(dbuf + dst, curr_zone, dbuf_size - dst);
357 			break;
358 		case 'z':
359 			/* name of the zone we're operating on */
360 			if (zonename == NULL)
361 				break;
362 			dst += strlcpy(dbuf + dst, zonename, dbuf_size - dst);
363 			break;
364 		}
365 	}
366 
367 	if (dst >= dbuf_size)
368 		return (-1);
369 
370 	dbuf[dst] = '\0';
371 	return (0);
372 }
373 
374 /*
375  * Retrieve the given tag from the brand.
376  * Perform the following substitutions as necessary:
377  *
378  *	%%	%
379  *	%u	Username
380  *	%z	Name of target zone
381  *	%Z	Name of current zone
382  *	%R	Zonepath of zone
383  *
384  * Returns 0 on success, -1 on failure.
385  */
386 static int
387 brand_get_value(struct brand_handle *bhp, const char *zonename,
388     const char *zonepath, const char *username, const char *curr_zone,
389     char *buf, size_t len, const xmlChar *tagname,
390     boolean_t substitute, boolean_t optional)
391 {
392 	xmlNodePtr node;
393 	xmlChar *content;
394 	int err = 0;
395 
396 	/*
397 	 * Retrieve the specified value from the XML doc
398 	 */
399 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
400 		return (-1);
401 
402 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0)
403 		return (-1);
404 
405 	for (node = node->xmlChildrenNode; node != NULL;
406 	    node = node->next) {
407 		if (xmlStrcmp(node->name, tagname) == 0)
408 			break;
409 	}
410 
411 	if (node == NULL) {
412 		if (optional) {
413 			buf[0] = '\0';
414 			return (0);
415 		} else {
416 			return (-1);
417 		}
418 	}
419 
420 	if ((content = xmlNodeGetContent(node)) == NULL)
421 		return (-1);
422 
423 	if (strlen((char *)content) == 0) {
424 		/*
425 		 * If the entry in the config file is empty, check to see
426 		 * whether this is an optional field.  If so, we return the
427 		 * empty buffer.  If not, we return an error.
428 		 */
429 		if (optional) {
430 			buf[0] = '\0';
431 		} else {
432 			err = -1;
433 		}
434 	} else {
435 		/* Substitute token values as needed. */
436 		if (substitute) {
437 			if (i_substitute_tokens((char *)content, buf, len,
438 			    zonename, zonepath, username, curr_zone) != 0)
439 				err = -1;
440 		} else {
441 			if (strlcpy(buf, (char *)content, len) >= len)
442 				err = -1;
443 		}
444 	}
445 
446 	xmlFree(content);
447 
448 	return (err);
449 }
450 
451 int
452 brand_get_attach(brand_handle_t bh, const char *zonename,
453     const char *zonepath, char *buf, size_t len)
454 {
455 	struct brand_handle *bhp = (struct brand_handle *)bh;
456 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
457 	    buf, len, DTD_ELEM_ATTACH, B_TRUE, B_TRUE));
458 }
459 
460 int
461 brand_get_boot(brand_handle_t bh, const char *zonename,
462     const char *zonepath, char *buf, size_t len)
463 {
464 	struct brand_handle *bhp = (struct brand_handle *)bh;
465 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
466 	    buf, len, DTD_ELEM_BOOT, B_TRUE, B_TRUE));
467 }
468 
469 int
470 brand_get_brandname(brand_handle_t bh, char *buf, size_t len)
471 {
472 	struct brand_handle *bhp = (struct brand_handle *)bh;
473 	if (len <= strlen(bhp->bh_name))
474 		return (-1);
475 
476 	(void) strcpy(buf, bhp->bh_name);
477 
478 	return (0);
479 }
480 
481 int
482 brand_get_clone(brand_handle_t bh, const char *zonename,
483     const char *zonepath, char *buf, size_t len)
484 {
485 	struct brand_handle *bhp = (struct brand_handle *)bh;
486 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
487 	    buf, len, DTD_ELEM_CLONE, B_TRUE, B_TRUE));
488 }
489 
490 int
491 brand_get_detach(brand_handle_t bh, const char *zonename,
492     const char *zonepath, char *buf, size_t len)
493 {
494 	struct brand_handle *bhp = (struct brand_handle *)bh;
495 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
496 	    buf, len, DTD_ELEM_DETACH, B_TRUE, B_TRUE));
497 }
498 
499 int
500 brand_get_halt(brand_handle_t bh, const char *zonename,
501     const char *zonepath, char *buf, size_t len)
502 {
503 	struct brand_handle *bhp = (struct brand_handle *)bh;
504 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
505 	    buf, len, DTD_ELEM_HALT, B_TRUE, B_TRUE));
506 }
507 
508 int
509 brand_get_shutdown(brand_handle_t bh, const char *zonename,
510     const char *zonepath, char *buf, size_t len)
511 {
512 	struct brand_handle *bhp = (struct brand_handle *)bh;
513 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
514 	    buf, len, DTD_ELEM_SHUTDOWN, B_TRUE, B_TRUE));
515 }
516 
517 int
518 brand_get_initname(brand_handle_t bh, char *buf, size_t len)
519 {
520 	struct brand_handle *bhp = (struct brand_handle *)bh;
521 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
522 	    buf, len, DTD_ELEM_INITNAME, B_FALSE, B_FALSE));
523 }
524 
525 boolean_t
526 brand_restartinit(brand_handle_t bh)
527 {
528 	struct brand_handle *bhp = (struct brand_handle *)bh;
529 	char val[80];
530 
531 	if (brand_get_value(bhp, NULL, NULL, NULL, NULL,
532 	    val, sizeof (val), DTD_ELEM_RESTARTINIT, B_FALSE, B_FALSE) != 0)
533 		return (B_TRUE);
534 
535 	if (strcmp(val, "false") == 0)
536 		return (B_FALSE);
537 	return (B_TRUE);
538 }
539 
540 int
541 brand_get_login_cmd(brand_handle_t bh, const char *username,
542     char *buf, size_t len)
543 {
544 	struct brand_handle *bhp = (struct brand_handle *)bh;
545 	const char *curr_zone = get_curr_zone();
546 	return (brand_get_value(bhp, NULL, NULL, username, curr_zone,
547 	    buf, len, DTD_ELEM_LOGIN_CMD, B_TRUE, B_FALSE));
548 }
549 
550 int
551 brand_get_forcedlogin_cmd(brand_handle_t bh, const char *username,
552     char *buf, size_t len)
553 {
554 	struct brand_handle *bhp = (struct brand_handle *)bh;
555 	const char *curr_zone = get_curr_zone();
556 	return (brand_get_value(bhp, NULL, NULL, username, curr_zone,
557 	    buf, len, DTD_ELEM_FORCELOGIN_CMD, B_TRUE, B_FALSE));
558 }
559 
560 int
561 brand_get_user_cmd(brand_handle_t bh, const char *username,
562     char *buf, size_t len)
563 {
564 	struct brand_handle *bhp = (struct brand_handle *)bh;
565 
566 	return (brand_get_value(bhp, NULL, NULL, username, NULL,
567 	    buf, len, DTD_ELEM_USER_CMD, B_TRUE, B_FALSE));
568 }
569 
570 int
571 brand_get_install(brand_handle_t bh, const char *zonename,
572     const char *zonepath, char *buf, size_t len)
573 {
574 	struct brand_handle *bhp = (struct brand_handle *)bh;
575 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
576 	    buf, len, DTD_ELEM_INSTALL, B_TRUE, B_FALSE));
577 }
578 
579 int
580 brand_get_installopts(brand_handle_t bh, char *buf, size_t len)
581 {
582 	struct brand_handle *bhp = (struct brand_handle *)bh;
583 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
584 	    buf, len, DTD_ELEM_INSTALLOPTS, B_FALSE, B_TRUE));
585 }
586 
587 int
588 brand_get_modname(brand_handle_t bh, char *buf, size_t len)
589 {
590 	struct brand_handle *bhp = (struct brand_handle *)bh;
591 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
592 	    buf, len, DTD_ELEM_MODNAME, B_FALSE, B_TRUE));
593 }
594 
595 int
596 brand_get_postattach(brand_handle_t bh, const char *zonename,
597     const char *zonepath, char *buf, size_t len)
598 {
599 	struct brand_handle *bhp = (struct brand_handle *)bh;
600 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
601 	    buf, len, DTD_ELEM_POSTATTACH, B_TRUE, B_TRUE));
602 }
603 
604 int
605 brand_get_postclone(brand_handle_t bh, const char *zonename,
606     const char *zonepath, char *buf, size_t len)
607 {
608 	struct brand_handle *bhp = (struct brand_handle *)bh;
609 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
610 	    buf, len, DTD_ELEM_POSTCLONE, B_TRUE, B_TRUE));
611 }
612 
613 int
614 brand_get_postinstall(brand_handle_t bh, const char *zonename,
615     const char *zonepath, char *buf, size_t len)
616 {
617 	struct brand_handle *bhp = (struct brand_handle *)bh;
618 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
619 	    buf, len, DTD_ELEM_POSTINSTALL, B_TRUE, B_TRUE));
620 }
621 
622 int
623 brand_get_postsnap(brand_handle_t bh, const char *zonename,
624     const char *zonepath, char *buf, size_t len)
625 {
626 	struct brand_handle *bhp = (struct brand_handle *)bh;
627 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
628 	    buf, len, DTD_ELEM_POSTSNAP, B_TRUE, B_TRUE));
629 }
630 
631 int
632 brand_get_poststatechange(brand_handle_t bh, const char *zonename,
633     const char *zonepath, char *buf, size_t len)
634 {
635 	struct brand_handle *bhp = (struct brand_handle *)bh;
636 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
637 	    buf, len, DTD_ELEM_POSTSTATECHG, B_TRUE, B_TRUE));
638 }
639 
640 int
641 brand_get_predetach(brand_handle_t bh, const char *zonename,
642     const char *zonepath, char *buf, size_t len)
643 {
644 	struct brand_handle *bhp = (struct brand_handle *)bh;
645 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
646 	    buf, len, DTD_ELEM_PREDETACH, B_TRUE, B_TRUE));
647 }
648 
649 int
650 brand_get_presnap(brand_handle_t bh, const char *zonename,
651     const char *zonepath, char *buf, size_t len)
652 {
653 	struct brand_handle *bhp = (struct brand_handle *)bh;
654 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
655 	    buf, len, DTD_ELEM_PRESNAP, B_TRUE, B_TRUE));
656 }
657 
658 int
659 brand_get_prestatechange(brand_handle_t bh, const char *zonename,
660     const char *zonepath, char *buf, size_t len)
661 {
662 	struct brand_handle *bhp = (struct brand_handle *)bh;
663 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
664 	    buf, len, DTD_ELEM_PRESTATECHG, B_TRUE, B_TRUE));
665 }
666 
667 int
668 brand_get_preuninstall(brand_handle_t bh, const char *zonename,
669     const char *zonepath, char *buf, size_t len)
670 {
671 	struct brand_handle *bhp = (struct brand_handle *)bh;
672 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
673 	    buf, len, DTD_ELEM_PREUNINSTALL, B_TRUE, B_TRUE));
674 }
675 
676 int
677 brand_get_query(brand_handle_t bh, const char *zonename,
678     const char *zonepath, char *buf, size_t len)
679 {
680 	struct brand_handle *bhp = (struct brand_handle *)bh;
681 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
682 	    buf, len, DTD_ELEM_QUERY, B_TRUE, B_TRUE));
683 }
684 
685 int
686 brand_get_uninstall(brand_handle_t bh, const char *zonename,
687     const char *zonepath, char *buf, size_t len)
688 {
689 	struct brand_handle *bhp = (struct brand_handle *)bh;
690 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
691 	    buf, len, DTD_ELEM_UNINSTALL, B_TRUE, B_TRUE));
692 }
693 
694 int
695 brand_get_validatesnap(brand_handle_t bh, const char *zonename,
696     const char *zonepath, char *buf, size_t len)
697 {
698 	struct brand_handle *bhp = (struct brand_handle *)bh;
699 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
700 	    buf, len, DTD_ELEM_VALIDSNAP, B_TRUE, B_TRUE));
701 }
702 
703 int
704 brand_get_verify_cfg(brand_handle_t bh, char *buf, size_t len)
705 {
706 	struct brand_handle *bhp = (struct brand_handle *)bh;
707 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
708 	    buf, len, DTD_ELEM_VERIFY_CFG, B_FALSE, B_TRUE));
709 }
710 
711 int
712 brand_get_verify_adm(brand_handle_t bh, const char *zonename,
713     const char *zonepath, char *buf, size_t len)
714 {
715 	struct brand_handle *bhp = (struct brand_handle *)bh;
716 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
717 	    buf, len, DTD_ELEM_VERIFY_ADM, B_TRUE, B_TRUE));
718 }
719 
720 int
721 brand_get_sysboot(brand_handle_t bh, const char *zonename,
722     const char *zonepath, char *buf, size_t len)
723 {
724 	struct brand_handle *bhp = (struct brand_handle *)bh;
725 	return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
726 	    buf, len, DTD_ELEM_SYSBOOT, B_TRUE, B_TRUE));
727 }
728 
729 boolean_t
730 brand_allow_exclusive_ip(brand_handle_t bh)
731 {
732 	struct brand_handle	*bhp = (struct brand_handle *)bh;
733 	xmlNodePtr		node;
734 	xmlChar			*allow_excl;
735 	boolean_t		ret;
736 
737 	assert(bhp != NULL);
738 
739 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
740 		return (B_FALSE);
741 
742 	allow_excl = xmlGetProp(node, DTD_ATTR_ALLOWEXCL);
743 	if (allow_excl == NULL)
744 		return (B_FALSE);
745 
746 	/* Note: only return B_TRUE if it's "true" */
747 	if (strcmp((char *)allow_excl, DTD_ENTITY_TRUE) == 0)
748 		ret = B_TRUE;
749 	else
750 		ret = B_FALSE;
751 
752 	xmlFree(allow_excl);
753 
754 	return (ret);
755 }
756 
757 /*
758  * Iterate over brand privileges
759  *
760  * Walks the brand config, searching for <privilege> elements, calling the
761  * specified callback for each.  Returns 0 on success, or -1 on failure.
762  */
763 int
764 brand_config_iter_privilege(brand_handle_t bh,
765     int (*func)(void *, priv_iter_t *), void *data)
766 {
767 	struct brand_handle	*bhp = (struct brand_handle *)bh;
768 	xmlNodePtr		node;
769 	xmlChar			*name, *set, *iptype;
770 	priv_iter_t		priv_iter;
771 	int			ret;
772 
773 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
774 		return (-1);
775 
776 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
777 
778 		if (xmlStrcmp(node->name, DTD_ELEM_PRIVILEGE) != 0)
779 			continue;
780 
781 		name = xmlGetProp(node, DTD_ATTR_NAME);
782 		set = xmlGetProp(node, DTD_ATTR_SET);
783 		iptype = xmlGetProp(node, DTD_ATTR_IPTYPE);
784 
785 		if (name == NULL || set == NULL || iptype == NULL) {
786 			if (name != NULL)
787 				xmlFree(name);
788 			if (set != NULL)
789 				xmlFree(set);
790 			if (iptype != NULL)
791 				xmlFree(iptype);
792 			return (-1);
793 		}
794 
795 		priv_iter.pi_name = (char *)name;
796 		priv_iter.pi_set = (char *)set;
797 		priv_iter.pi_iptype = (char *)iptype;
798 
799 		ret = func(data, &priv_iter);
800 
801 		xmlFree(name);
802 		xmlFree(set);
803 		xmlFree(iptype);
804 
805 		if (ret != 0)
806 			return (-1);
807 	}
808 
809 	return (0);
810 }
811 
812 static int
813 i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename,
814     const char *zonepath, int (*func)(void *, const char *, const char *,
815     const char *, const char *), void *data, const xmlChar *mount_type)
816 {
817 	xmlNodePtr node;
818 	xmlChar *special, *dir, *type, *opt;
819 	char special_exp[MAXPATHLEN];
820 	char opt_exp[MAXPATHLEN];
821 	int ret;
822 
823 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
824 		return (-1);
825 
826 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
827 
828 		if (xmlStrcmp(node->name, mount_type) != 0)
829 			continue;
830 
831 		special = xmlGetProp(node, DTD_ATTR_SPECIAL);
832 		dir = xmlGetProp(node, DTD_ATTR_DIRECTORY);
833 		type = xmlGetProp(node, DTD_ATTR_TYPE);
834 		opt = xmlGetProp(node, DTD_ATTR_OPT);
835 		if ((special == NULL) || (dir == NULL) || (type == NULL) ||
836 		    (opt == NULL)) {
837 			ret = -1;
838 			goto next;
839 		}
840 
841 		/* Substitute token values as needed. */
842 		if ((ret = i_substitute_tokens((char *)special,
843 		    special_exp, sizeof (special_exp),
844 		    zonename, zonepath, NULL, NULL)) != 0)
845 			goto next;
846 
847 		/* opt might not be defined */
848 		if (strlen((const char *)opt) == 0) {
849 			xmlFree(opt);
850 			opt = NULL;
851 		} else {
852 			if ((ret = i_substitute_tokens((char *)opt,
853 			    opt_exp, sizeof (opt_exp),
854 			    zonename, zonepath, NULL, NULL)) != 0)
855 				goto next;
856 		}
857 
858 		ret = func(data, (char *)special_exp, (char *)dir,
859 		    (char *)type, ((opt != NULL) ? opt_exp : NULL));
860 
861 next:
862 		if (special != NULL)
863 			xmlFree(special);
864 		if (dir != NULL)
865 			xmlFree(dir);
866 		if (type != NULL)
867 			xmlFree(type);
868 		if (opt != NULL)
869 			xmlFree(opt);
870 		if (ret != 0)
871 			return (-1);
872 	}
873 	return (0);
874 }
875 
876 
877 /*
878  * Iterate over global platform filesystems
879  *
880  * Walks the platform, searching for <global_mount> elements, calling the
881  * specified callback for each.  Returns 0 on success, or -1 on failure.
882  *
883  * Perform the following substitutions as necessary:
884  *
885  *	%R	Zonepath of zone
886  */
887 int
888 brand_platform_iter_gmounts(brand_handle_t bh, const char *zonename,
889     const char *zonepath, int (*func)(void *, const char *, const char *,
890     const char *, const char *), void *data)
891 {
892 	struct brand_handle *bhp = (struct brand_handle *)bh;
893 	return (i_brand_platform_iter_mounts(bhp, zonename, zonepath, func,
894 	    data, DTD_ELEM_GLOBAL_MOUNT));
895 }
896 
897 /*
898  * Iterate over non-global zone platform filesystems
899  *
900  * Walks the platform, searching for <mount> elements, calling the
901  * specified callback for each.  Returns 0 on success, or -1 on failure.
902  */
903 int
904 brand_platform_iter_mounts(brand_handle_t bh, int (*func)(void *,
905     const char *, const char *, const char *, const char *), void *data)
906 {
907 	struct brand_handle *bhp = (struct brand_handle *)bh;
908 	return (i_brand_platform_iter_mounts(bhp, NULL, NULL, func, data,
909 	    DTD_ELEM_MOUNT));
910 }
911 
912 /*
913  * Iterate over platform symlinks
914  *
915  * Walks the platform, searching for <symlink> elements, calling the
916  * specified callback for each.  Returns 0 on success, or -1 on failure.
917  */
918 int
919 brand_platform_iter_link(brand_handle_t bh,
920     int (*func)(void *, const char *, const char *), void *data)
921 {
922 	struct brand_handle *bhp = (struct brand_handle *)bh;
923 	xmlNodePtr node;
924 	xmlChar *source, *target;
925 	int ret;
926 
927 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
928 		return (-1);
929 
930 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
931 
932 		if (xmlStrcmp(node->name, DTD_ELEM_SYMLINK) != 0)
933 			continue;
934 
935 		source = xmlGetProp(node, DTD_ATTR_SOURCE);
936 		target = xmlGetProp(node, DTD_ATTR_TARGET);
937 
938 		if (source == NULL || target == NULL) {
939 			if (source != NULL)
940 				xmlFree(source);
941 			if (target != NULL)
942 				xmlFree(target);
943 			return (-1);
944 		}
945 
946 		ret = func(data, (char *)source, (char *)target);
947 
948 		xmlFree(source);
949 		xmlFree(target);
950 
951 		if (ret != 0)
952 			return (-1);
953 	}
954 
955 	return (0);
956 }
957 
958 /*
959  * Iterate over platform devices
960  *
961  * Walks the platform, searching for <device> elements, calling the
962  * specified callback for each.  Returns 0 on success, or -1 on failure.
963  */
964 int
965 brand_platform_iter_devices(brand_handle_t bh, const char *zonename,
966     int (*func)(void *, const char *, const char *), void *data,
967     const char *curr_iptype)
968 {
969 	struct brand_handle	*bhp = (struct brand_handle *)bh;
970 	const char		*curr_arch = get_curr_arch();
971 	xmlNodePtr		node;
972 	xmlChar			*match, *name, *arch, *iptype;
973 	char			match_exp[MAXPATHLEN];
974 	boolean_t		err = B_FALSE;
975 	int			ret = 0;
976 
977 
978 	assert(bhp != NULL);
979 	assert(zonename != NULL);
980 	assert(func != NULL);
981 	assert(curr_iptype != NULL);
982 
983 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
984 		return (-1);
985 
986 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
987 
988 		if (xmlStrcmp(node->name, DTD_ELEM_DEVICE) != 0)
989 			continue;
990 
991 		match = xmlGetProp(node, DTD_ATTR_MATCH);
992 		name = xmlGetProp(node, DTD_ATTR_NAME);
993 		arch = xmlGetProp(node, DTD_ATTR_ARCH);
994 		iptype = xmlGetProp(node, DTD_ATTR_IPTYPE);
995 		if ((match == NULL) || (name == NULL) || (arch == NULL) ||
996 		    (iptype == NULL)) {
997 			err = B_TRUE;
998 			goto next;
999 		}
1000 
1001 		/* check if the arch matches */
1002 		if ((strcmp((char *)arch, "all") != 0) &&
1003 		    (strcmp((char *)arch, curr_arch) != 0))
1004 			goto next;
1005 
1006 		/* check if the iptype matches */
1007 		if ((strcmp((char *)iptype, "all") != 0) &&
1008 		    (strcmp((char *)iptype, curr_iptype) != 0))
1009 			goto next;
1010 
1011 		/* Substitute token values as needed. */
1012 		if ((ret = i_substitute_tokens((char *)match,
1013 		    match_exp, sizeof (match_exp),
1014 		    zonename, NULL, NULL, NULL)) != 0) {
1015 			err = B_TRUE;
1016 			goto next;
1017 		}
1018 
1019 		/* name might not be defined */
1020 		if (strlen((const char *)name) == 0) {
1021 			xmlFree(name);
1022 			name = NULL;
1023 		}
1024 
1025 		/* invoke the callback */
1026 		ret = func(data, (const char *)match_exp, (const char *)name);
1027 
1028 next:
1029 		if (match != NULL)
1030 			xmlFree(match);
1031 		if (name != NULL)
1032 			xmlFree(name);
1033 		if (arch != NULL)
1034 			xmlFree(arch);
1035 		if (iptype != NULL)
1036 			xmlFree(iptype);
1037 		if (err)
1038 			return (-1);
1039 		if (ret != 0)
1040 			return (-1);
1041 	}
1042 
1043 	return (0);
1044 }
1045