xref: /illumos-gate/usr/src/cmd/boot/bootadm/bootadm_loader.c (revision 4e065a9f6a4471f1001853cd10a093bc5beb58a5)
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2012 Milan Jurik. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28  * Copyright 2016 Toomas Soome <tsoome@me.com>
29  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
30  * Copyright 2020 2020 Data Direct Networks.
31  */
32 
33 /*
34  * Loader menu management.
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <wchar.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <alloca.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/queue.h>
48 #include <libbe.h>
49 #include <ficl.h>
50 #include <ficlplatform/emu.h>
51 #include <ofmt.h>
52 
53 #include "bootadm.h"
54 
55 extern int bam_rootlen;
56 extern int bam_alt_root;
57 extern char *rootbuf;
58 extern char *bam_root;
59 
60 #define	BOOT_DIR	"/boot"
61 #define	CONF_DIR	BOOT_DIR "/conf.d"
62 #define	MENU		BOOT_DIR "/menu.lst"
63 #define	TRANSIENT	BOOT_DIR "/transient.conf"
64 #define	XEN_CONFIG	CONF_DIR "/xen"
65 
66 typedef struct menu_entry {
67 	int me_idx;
68 	boolean_t me_active;
69 	char *me_title;
70 	char *me_type;
71 	char *me_bootfs;
72 	STAILQ_ENTRY(menu_entry) me_next;
73 } menu_entry_t;
74 STAILQ_HEAD(menu_lst, menu_entry);
75 
76 static error_t set_option(struct menu_lst *, char *, char *);
77 static error_t list_entry(struct menu_lst *, char *, char *);
78 static error_t update_entry(struct menu_lst *, char *, char *);
79 static error_t update_temp(struct menu_lst *, char *, char *);
80 static error_t list_setting(struct menu_lst *menu, char *, char *);
81 static error_t disable_hyper(struct menu_lst *, char *, char *);
82 static error_t enable_hyper(struct menu_lst *, char *, char *);
83 
84 /* Menu related sub commands */
85 static subcmd_defn_t menu_subcmds[] = {
86 	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
87 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
88 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
89 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
90 	"list_setting",		OPT_OPTIONAL,	list_setting, 1, /* menu */
91 	"disable_hypervisor",	OPT_ABSENT,	disable_hyper, 0, /* menu */
92 	"enable_hypervisor",	OPT_ABSENT,	enable_hyper, 0, /* menu */
93 	NULL,			0,		NULL, 0 /* must be last */
94 };
95 
96 #define	NUM_COLS	(5)
97 
98 static boolean_t
99 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
100 {
101 	menu_entry_t *entry = ofarg->ofmt_cbarg;
102 
103 	switch (ofarg->ofmt_id) {
104 	case 0:
105 		(void) snprintf(buf, bufsize, "%d", entry->me_idx);
106 		break;
107 	case 1:
108 		(void) snprintf(buf, bufsize, "%s", entry->me_title);
109 		break;
110 	case 2:
111 		(void) snprintf(buf, bufsize, "%s", entry->me_bootfs);
112 		break;
113 	case 3:
114 		(void) snprintf(buf, bufsize, "%s", entry->me_type);
115 		break;
116 	case 4:
117 		if (entry->me_active == B_TRUE)
118 			(void) snprintf(buf, bufsize, "   *");
119 		else
120 			(void) snprintf(buf, bufsize, "   -");
121 		break;
122 	default:
123 		return (B_FALSE);
124 	}
125 	return (B_TRUE);
126 }
127 
128 static void
129 init_hdr_cols(ofmt_field_t *hdr)
130 {
131 	uint_t i;
132 
133 	for (i = 0; i < NUM_COLS; i++) {
134 		char *name = NULL;
135 
136 		switch (i) {
137 		case 0:
138 			name = _("INDEX");
139 			break;
140 		case 1:
141 			name = _("NAME");
142 			break;
143 		case 2:
144 			name = _("DEVICE");
145 			break;
146 		case 3:
147 			name = _("TYPE");
148 			break;
149 		case 4:
150 			name = _("DEFAULT");
151 			break;
152 		}
153 
154 		hdr[i].of_name = name;
155 		hdr[i].of_id = i;
156 		hdr[i].of_cb = print_menu_cb;
157 
158 		if (name != NULL) {
159 			wchar_t wname[128];
160 			size_t sz = mbstowcs(wname, name, sizeof (wname) /
161 			    sizeof (wchar_t));
162 			if (sz > 0) {
163 				int wcsw = wcswidth(wname, sz);
164 				if (wcsw > 0)
165 					hdr[i].of_width = wcsw;
166 				else
167 					hdr[i].of_width = sz;
168 			} else {
169 				hdr[i].of_width = strlen(name);
170 			}
171 		}
172 	}
173 }
174 
175 static void
176 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu)
177 {
178 	size_t len[NUM_COLS];
179 	menu_entry_t *entry;
180 	int i;
181 
182 	for (i = 0; i < NUM_COLS; i++)
183 		len[i] = hdr[i].of_width + 1;
184 
185 	STAILQ_FOREACH(entry, menu, me_next) {
186 		size_t entry_len;
187 
188 		entry_len = strlen(entry->me_title) + 1;
189 		if (entry_len > len[1])
190 			len[1] = entry_len;
191 
192 		entry_len = strlen(entry->me_bootfs) + 1;
193 		if (entry_len > len[2])
194 			len[2] = entry_len;
195 
196 		entry_len = strlen(entry->me_type) + 1;
197 		if (entry_len > len[3])
198 			len[3] = entry_len;
199 	}
200 
201 	for (i = 0; i < NUM_COLS; i++)
202 		hdr[i].of_width = len[i];
203 }
204 
205 static ofmt_field_t *
206 init_menu_template(struct menu_lst *menu)
207 {
208 	ofmt_field_t *temp;
209 
210 	if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL)
211 		return (temp);
212 
213 	init_hdr_cols(temp);
214 	menu_update_widths(temp, menu);
215 	return (temp);
216 }
217 
218 static void
219 print_nodes(boolean_t parsable, struct menu_lst *menu)
220 {
221 	ofmt_status_t oferr;
222 	ofmt_handle_t ofmt;
223 	uint_t ofmtflags = 0;
224 	ofmt_field_t *menu_template;
225 	menu_entry_t  *entry;
226 
227 	if (parsable == B_TRUE)
228 		ofmtflags = OFMT_PARSABLE;
229 
230 	menu_template = init_menu_template(menu);
231 	oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt);
232 
233 	if (oferr != OFMT_SUCCESS) {
234 		char buf[OFMT_BUFSIZE];
235 
236 		(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
237 		(void) printf("bootadm: %s\n", buf);
238 		free(menu_template);
239 		return;
240 	}
241 
242 	STAILQ_FOREACH(entry, menu, me_next)
243 		ofmt_print(ofmt, entry);
244 
245 	ofmt_close(ofmt);
246 	free(menu_template);
247 }
248 
249 /*
250  * Get the be_active_on_boot for bootfs.
251  */
252 static boolean_t
253 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
254 {
255 	be_node_list_t *be_node;
256 	boolean_t rv = B_FALSE;
257 
258 	for (be_node = be_nodes; be_node != NULL;
259 	    be_node = be_node->be_next_node) {
260 		if (strcmp(be_node->be_root_ds, bootfs) == 0) {
261 			rv = be_node->be_active_on_boot;
262 			break;
263 		}
264 	}
265 
266 	return (rv);
267 }
268 
269 error_t
270 menu_read(struct menu_lst *menu, char *menu_path)
271 {
272 	FILE *fp;
273 	be_node_list_t *be_nodes;
274 	menu_entry_t *mp;
275 	char buf[PATH_MAX];
276 	char *title;
277 	char *bootfs;
278 	char *type;
279 	char *key, *value;
280 	int i = 0;
281 	int ret = BAM_SUCCESS;
282 
283 	fp = fopen(menu_path, "r");
284 	if (fp == NULL)
285 		return (BAM_ERROR);
286 
287 	if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS)
288 		be_nodes = NULL;
289 
290 	/*
291 	 * menu.lst entry is on two lines, one for title, one for bootfs
292 	 * so we process both lines in succession.
293 	 */
294 	title = NULL;
295 	type = NULL;
296 	bootfs = NULL;
297 	do {
298 		if (fgets(buf, PATH_MAX, fp) == NULL) {
299 			if (!feof(fp))
300 				ret = BAM_ERROR;
301 			goto done;
302 		}
303 		key = strtok(buf, " \n");
304 		if (strcmp(key, "title") != 0) {
305 			ret = BAM_ERROR;
306 			goto done;
307 		}
308 		value = strtok(NULL, " \n");
309 		if ((title = strdup(value)) == NULL) {
310 			ret = BAM_ERROR;
311 			goto done;
312 		}
313 
314 		if (fgets(buf, PATH_MAX, fp) == NULL) {
315 			ret = BAM_ERROR;
316 			goto done;
317 		}
318 
319 		key = strtok(buf, " \n");
320 		if ((type = strdup(key)) == NULL) {
321 			ret = BAM_ERROR;
322 			goto done;
323 		}
324 		value = strtok(NULL, " \n");
325 		if ((bootfs = strdup(value)) == NULL) {
326 			ret = BAM_ERROR;
327 			goto done;
328 		}
329 		if ((mp = malloc(sizeof (menu_entry_t))) == NULL) {
330 			ret = BAM_ERROR;
331 			goto done;
332 		}
333 		mp->me_idx = i++;
334 		mp->me_title = title;
335 		mp->me_type = type;
336 		mp->me_bootfs = bootfs;
337 		mp->me_active = menu_active_on_boot(be_nodes, bootfs);
338 		STAILQ_INSERT_TAIL(menu, mp, me_next);
339 
340 		title = NULL;
341 		type = NULL;
342 		bootfs = NULL;
343 	} while (feof(fp) == 0);
344 
345 done:
346 	free(title);
347 	free(type);
348 	free(bootfs);
349 	(void) fclose(fp);
350 	be_free_list(be_nodes);
351 	return (ret);
352 }
353 
354 void
355 menu_free(struct menu_lst *menu)
356 {
357 	menu_entry_t *entry;
358 
359 	while (!STAILQ_EMPTY(menu)) {
360 		entry = STAILQ_FIRST(menu);
361 		STAILQ_REMOVE_HEAD(menu, me_next);
362 		free(entry->me_title);
363 		free(entry->me_type);
364 		free(entry->me_bootfs);
365 		free(entry);
366 	}
367 }
368 
369 error_t
370 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
371 {
372 	error_t		ret;
373 	char		menu_path[PATH_MAX];
374 	char		clean_menu_root[PATH_MAX];
375 	char		menu_root[PATH_MAX];
376 	struct stat	sb;
377 	error_t		(*f)(struct menu_lst *, char *, char *);
378 	char		*special;
379 	char		*pool = NULL;
380 	zfs_mnted_t	zmnted;
381 	char		*zmntpt;
382 	char		*osdev;
383 	char		*osroot;
384 	const char	*fcn = "bam_loader_menu()";
385 	struct menu_lst	menu = {0};
386 
387 	STAILQ_INIT(&menu);
388 
389 	/*
390 	 * Check arguments
391 	 */
392 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
393 	if (ret == BAM_ERROR) {
394 		return (BAM_ERROR);
395 	}
396 
397 	assert(bam_root);
398 
399 	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
400 	osdev = osroot = NULL;
401 
402 	if (strcmp(subcmd, "update_entry") == 0) {
403 		assert(opt);
404 
405 		osdev = strtok(opt, ",");
406 		assert(osdev);
407 		osroot = strtok(NULL, ",");
408 		if (osroot) {
409 			/* fixup bam_root so that it points at osroot */
410 			if (realpath(osroot, rootbuf) == NULL) {
411 				bam_error(_("cannot resolve path %s: %s\n"),
412 				    osroot, strerror(errno));
413 				return (BAM_ERROR);
414 			}
415 			bam_alt_root = 1;
416 			bam_root  = rootbuf;
417 			bam_rootlen = strlen(rootbuf);
418 		}
419 	}
420 
421 	if (stat(menu_root, &sb) == -1) {
422 		bam_error(_("cannot find menu\n"));
423 		return (BAM_ERROR);
424 	}
425 
426 	if (!is_zfs(menu_root)) {
427 		bam_error(_("only ZFS root is supported\n"));
428 		return (BAM_ERROR);
429 	}
430 
431 	assert(strcmp(menu_root, bam_root) == 0);
432 	special = get_special(menu_root);
433 	INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
434 	if (special == NULL) {
435 		bam_error(_("cant find special file for mount-point %s\n"),
436 		    menu_root);
437 		return (BAM_ERROR);
438 	}
439 	pool = strtok(special, "/");
440 	INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
441 	if (pool == NULL) {
442 		free(special);
443 		bam_error(_("cant find pool for mount-point %s\n"), menu_root);
444 		return (BAM_ERROR);
445 	}
446 	BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
447 
448 	zmntpt = mount_top_dataset(pool, &zmnted);
449 	INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
450 	if (zmntpt == NULL) {
451 		bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
452 		free(special);
453 		return (BAM_ERROR);
454 	}
455 	BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
456 
457 	(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
458 	BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
459 
460 	elide_trailing_slash(menu_root, clean_menu_root,
461 	    sizeof (clean_menu_root));
462 
463 	BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
464 
465 	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
466 	(void) strlcat(menu_path, MENU, sizeof (menu_path));
467 
468 	BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
469 
470 	/*
471 	 * update_entry is special case, its used by installer
472 	 * and needs to create menu.lst file for loader
473 	 */
474 	if (menu_read(&menu, menu_path) == BAM_ERROR &&
475 	    strcmp(subcmd, "update_entry") != 0) {
476 		bam_error(_("cannot find menu file: %s\n"), menu_path);
477 		if (special != NULL)
478 			free(special);
479 		return (BAM_ERROR);
480 	}
481 
482 	/*
483 	 * If listing the menu, display the menu location
484 	 */
485 	if (strcmp(subcmd, "list_entry") == 0)
486 		bam_print(_("the location for the active menu is: %s\n"),
487 		    menu_path);
488 
489 	/*
490 	 * We already checked the following case in
491 	 * check_subcmd_and_suboptions() above. Complete the
492 	 * final step now.
493 	 */
494 	if (strcmp(subcmd, "set_option") == 0) {
495 		assert(largc == 1 && largv[0] && largv[1] == NULL);
496 		opt = largv[0];
497 	} else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
498 	    (strcmp(subcmd, "list_setting") != 0)) {
499 		assert(largc == 0 && largv == NULL);
500 	}
501 
502 	/*
503 	 * Once the sub-cmd handler has run
504 	 * only the line field is guaranteed to have valid values
505 	 */
506 	if (strcmp(subcmd, "update_entry") == 0) {
507 		ret = f(&menu, menu_root, osdev);
508 	} else if (strcmp(subcmd, "upgrade") == 0) {
509 		ret = f(&menu, bam_root, menu_root);
510 	} else if (strcmp(subcmd, "list_entry") == 0) {
511 		ret = f(&menu, menu_path, opt);
512 	} else if (strcmp(subcmd, "list_setting") == 0) {
513 		ret = f(&menu, ((largc > 0) ? largv[0] : ""),
514 		    ((largc > 1) ? largv[1] : ""));
515 	} else if (strcmp(subcmd, "disable_hypervisor") == 0) {
516 		if (is_sparc()) {
517 			bam_error(_("%s operation unsupported on SPARC "
518 			    "machines\n"), subcmd);
519 			ret = BAM_ERROR;
520 		} else {
521 			ret = f(&menu, bam_root, NULL);
522 		}
523 	} else if (strcmp(subcmd, "enable_hypervisor") == 0) {
524 		if (is_sparc()) {
525 			bam_error(_("%s operation unsupported on SPARC "
526 			    "machines\n"), subcmd);
527 			ret = BAM_ERROR;
528 		} else {
529 			char *extra_args = NULL;
530 
531 			/*
532 			 * Compress all arguments passed in the largv[] array
533 			 * into one string that can then be appended to the
534 			 * end of the kernel$ string the routine to enable the
535 			 * hypervisor will build.
536 			 *
537 			 * This allows the caller to supply arbitrary unparsed
538 			 * arguments, such as dom0 memory settings or APIC
539 			 * options.
540 			 *
541 			 * This concatenation will be done without ANY syntax
542 			 * checking whatsoever, so it's the responsibility of
543 			 * the caller to make sure the arguments are valid and
544 			 * do not duplicate arguments the conversion routines
545 			 * may create.
546 			 */
547 			if (largc > 0) {
548 				int extra_len, i;
549 
550 				for (extra_len = 0, i = 0; i < largc; i++)
551 					extra_len += strlen(largv[i]);
552 
553 				/*
554 				 * Allocate space for argument strings,
555 				 * intervening spaces and terminating NULL.
556 				 */
557 				extra_args = alloca(extra_len + largc);
558 
559 				(void) strcpy(extra_args, largv[0]);
560 
561 				for (i = 1; i < largc; i++) {
562 					(void) strcat(extra_args, " ");
563 					(void) strcat(extra_args, largv[i]);
564 				}
565 			}
566 
567 			ret = f(&menu, bam_root, extra_args);
568 		}
569 	} else
570 		ret = f(&menu, NULL, opt);
571 
572 	if (ret == BAM_WRITE) {
573 		BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
574 		    fcn, clean_menu_root));
575 		/* ret = menu_write(clean_menu_root, menu); */
576 	}
577 
578 	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
579 	assert((is_zfs(menu_root)) ^ (pool == NULL));
580 	if (pool) {
581 		(void) umount_top_dataset(pool, zmnted, zmntpt);
582 		free(special);
583 	}
584 
585 	menu_free(&menu);
586 	return (ret);
587 }
588 
589 /*
590  * To suppress output from ficl. We do not want to see messages
591  * from interpreting loader config.
592  */
593 
594 /*ARGSUSED*/
595 static void
596 ficlTextOutSilent(ficlCallback *cb, char *text)
597 {
598 }
599 
600 /*ARGSUSED*/
601 static error_t
602 set_option(struct menu_lst *menu, char *dummy, char *opt)
603 {
604 	char path[PATH_MAX];
605 	char *val;
606 	char *rest;
607 	int optval;
608 	menu_entry_t *entry;
609 	nvlist_t *be_attrs;
610 	FILE *fp;
611 	int rv, ret = BAM_SUCCESS;
612 
613 	assert(menu);
614 	assert(opt);
615 	assert(dummy == NULL);
616 
617 	val = strchr(opt, '=');
618 	if (val != NULL) {
619 		*val++ = '\0';
620 	} else {
621 		bam_error(_("missing value in key=value\n"));
622 		return (BAM_ERROR);
623 	}
624 
625 	if (strcmp(opt, "default") == 0) {
626 		errno = 0;
627 		optval = strtol(val, &rest, 10);
628 		if (errno != 0 || *rest != '\0') {
629 			bam_error(_("invalid boot entry number: %s\n"), val);
630 			return (BAM_ERROR);
631 		}
632 		STAILQ_FOREACH(entry, menu, me_next) {
633 			if (entry->me_idx == optval)
634 				break;
635 		}
636 		if (entry == NULL) {
637 			bam_error(_("invalid boot entry number: %s\n"), val);
638 			return (BAM_ERROR);
639 		}
640 		if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
641 			bam_error(_("out of memory\n"));
642 			return (BAM_ERROR);
643 		}
644 		if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
645 		    entry->me_title) != 0) {
646 			bam_error(_("out of memory\n"));
647 			nvlist_free(be_attrs);
648 			return (BAM_ERROR);
649 		}
650 		ret = be_activate(be_attrs);
651 		nvlist_free(be_attrs);
652 		if (ret != 0)
653 			ret = BAM_ERROR;
654 		return (ret);
655 	} else if (strcmp(opt, "timeout") == 0) {
656 		errno = 0;
657 		optval = strtol(val, &rest, 10);
658 		if (errno != 0 || *rest != '\0') {
659 			bam_error(_("invalid timeout: %s\n"), val);
660 			return (BAM_ERROR);
661 		}
662 
663 		(void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
664 		    bam_root);
665 
666 		fp = fopen(path, "w");
667 		if (fp == NULL) {
668 			bam_error(_("failed to open file: %s: %s\n"),
669 			    path, strerror(errno));
670 			return (BAM_ERROR);
671 		}
672 		/*
673 		 * timeout=-1 is to disable auto boot in illumos, but
674 		 * loader needs "NO" to disable auto boot.
675 		 */
676 		if (optval == -1)
677 			rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
678 		else
679 			rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
680 
681 		if (rv < 0) {
682 			bam_error(_("write to file failed: %s: %s\n"),
683 			    path, strerror(errno));
684 			(void) fclose(fp);
685 			ret = BAM_ERROR;
686 		} else
687 			rv = fclose(fp);
688 
689 		if (rv < 0) {
690 			bam_error(_("failed to close file: %s: %s\n"),
691 			    path, strerror(errno));
692 			ret = BAM_ERROR;
693 		}
694 		if (ret == BAM_ERROR)
695 			(void) unlink(path);
696 
697 		return (BAM_SUCCESS);
698 	}
699 
700 	bam_error(_("invalid option: %s\n"), opt);
701 	return (BAM_ERROR);
702 }
703 
704 static int
705 bam_mount_be(menu_entry_t *entry, char **dir)
706 {
707 	nvlist_t *be_attrs = NULL;
708 	const char *tmpdir = getenv("TMPDIR");
709 	const char *tmpname = "bam.XXXXXX";
710 	be_node_list_t *be_node, *be_nodes = NULL;
711 	int ret;
712 
713 	*dir = NULL;
714 	if (tmpdir == NULL)
715 		tmpdir = "/tmp";
716 
717 	ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
718 	if (ret < 0) {
719 		return (BE_ERR_NOMEM);
720 	}
721 	*dir = mkdtemp(*dir);
722 
723 	if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
724 		ret = BE_ERR_NOMEM;
725 		goto out;
726 	}
727 
728 	ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
729 	if (ret != BE_SUCCESS) {
730 		goto out;
731 	}
732 
733 	for (be_node = be_nodes; be_node;
734 	    be_node = be_node->be_next_node)
735 		if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0)
736 			break;
737 
738 	if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
739 	    be_node->be_node_name) != 0) {
740 		ret = BE_ERR_NOMEM;
741 		goto out;
742 	}
743 
744 	if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
745 		ret = BE_ERR_NOMEM;
746 		goto out;
747 	}
748 
749 	ret = be_mount(be_attrs);
750 	if (ret == BE_ERR_MOUNTED) {
751 		/*
752 		 * if BE is mounted, dir does not point to correct directory
753 		 */
754 		(void) rmdir(*dir);
755 		free(*dir);
756 		*dir = NULL;
757 	}
758 out:
759 	if (be_nodes != NULL)
760 		be_free_list(be_nodes);
761 	nvlist_free(be_attrs);
762 	return (ret);
763 }
764 
765 static int
766 bam_umount_be(char *dir)
767 {
768 	nvlist_t *be_attrs;
769 	int ret;
770 
771 	if (dir == NULL)		/* nothing to do */
772 		return (BE_SUCCESS);
773 
774 	if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
775 		return (BE_ERR_NOMEM);
776 
777 	if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
778 		ret = BE_ERR_NOMEM;
779 		goto out;
780 	}
781 
782 	ret = be_unmount(be_attrs);
783 out:
784 	nvlist_free(be_attrs);
785 	return (ret);
786 }
787 
788 /*
789  * display details of menu entry or single property
790  */
791 static error_t
792 list_menu_entry(menu_entry_t *entry, char *setting)
793 {
794 	int ret = BAM_SUCCESS;
795 	char *ptr, *dir;
796 	char buf[MAX_INPUT];
797 	ficlVm *vm;
798 	int mounted;
799 
800 	ptr = strrchr(entry->me_bootfs, ':');
801 	if (strcmp(entry->me_type, "bootfs") != 0 ||
802 	    (ptr != NULL && ptr[1] == '\0')) {
803 		(void) printf("\nTitle:       %s\n", entry->me_title);
804 		(void) printf("Type:        %s\n", entry->me_type);
805 		(void) printf("Device:      %s\n", entry->me_bootfs);
806 		return (ret);
807 	}
808 
809 	mounted = bam_mount_be(entry, &dir);
810 	if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
811 		if (dir != NULL) {
812 			(void) rmdir(dir);
813 			free(dir);
814 		}
815 		bam_error(_("%s is not mounted\n"), entry->me_title);
816 		return (BAM_ERROR);
817 	}
818 
819 	vm = bf_init("", ficlTextOutSilent);
820 	if (vm == NULL) {
821 		bam_error(_("error setting up forth interpreter\n"));
822 		ret = BAM_ERROR;
823 		goto done;
824 	}
825 
826 	/* should only get FICL_VM_STATUS_OUT_OF_TEXT */
827 	(void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
828 	    entry->me_bootfs);
829 	ret = ficlVmEvaluate(vm, buf);
830 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
831 		bam_error(_("error interpreting boot config\n"));
832 		ret = BAM_ERROR;
833 		goto done;
834 	}
835 	(void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
836 	ret = ficlVmEvaluate(vm, buf);
837 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
838 		bam_error(_("error interpreting boot config\n"));
839 		ret = BAM_ERROR;
840 		goto done;
841 	}
842 	(void) snprintf(buf, MAX_INPUT, "start");
843 	ret = ficlVmEvaluate(vm, buf);
844 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
845 		bam_error(_("error interpreting boot config\n"));
846 		ret = BAM_ERROR;
847 		goto done;
848 	}
849 	(void) snprintf(buf, MAX_INPUT, "boot");
850 	ret = ficlVmEvaluate(vm, buf);
851 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
852 		bam_error(_("error interpreting boot config\n"));
853 		ret = BAM_ERROR;
854 		goto done;
855 	}
856 
857 	ret = BAM_SUCCESS;
858 	if (*setting == '\0')
859 		(void) printf("\nTitle:       %s\n", entry->me_title);
860 	else if (strcasecmp(setting, "title") == 0) {
861 		(void) printf("%s\n", entry->me_title);
862 		goto done;
863 	}
864 
865 	ptr = getenv("autoboot_delay");
866 	if (ptr != NULL) {
867 		char *timeout = "-1";
868 
869 		if (strcasecmp(ptr, "NO") != 0)
870 			timeout = ptr;
871 
872 		if (*setting == '\0')
873 			(void) printf("Timeout:     %s\n", timeout);
874 		else if (strcasecmp(setting, "timeout") == 0) {
875 			(void) printf("%s\n", timeout);
876 			goto done;
877 		}
878 
879 	}
880 	ptr = getenv("console");
881 	if (ptr != NULL) {
882 		if (*setting == '\0')
883 			(void) printf("Console:     %s\n", ptr);
884 		else if (strcasecmp(setting, "console") == 0) {
885 			(void) printf("%s\n", ptr);
886 			goto done;
887 		}
888 	}
889 
890 	if (*setting == '\0')
891 		(void) printf("Bootfs:      %s\n", entry->me_bootfs);
892 	else if (strcasecmp(setting, "bootfs") == 0) {
893 		(void) printf("%s\n", entry->me_bootfs);
894 		goto done;
895 	}
896 
897 	ptr = getenv("xen_kernel");
898 	if (ptr != NULL) {
899 			if (*setting == '\0') {
900 				(void) printf("Xen kernel:  %s\n", ptr);
901 			} else if (strcasecmp(setting, "xen_kernel") == 0) {
902 				(void) printf("%s\n", ptr);
903 				goto done;
904 			}
905 
906 			if (*setting == '\0') {
907 				(void) printf("Xen args:    \"%s\"\n",
908 				    getenv("xen_cmdline"));
909 			} else if (strcasecmp(setting, "xen_cmdline") == 0) {
910 				(void) printf("%s\n", getenv("xen_cmdline"));
911 				goto done;
912 			}
913 
914 			if (*setting == '\0') {
915 				(void) printf("Kernel:      %s\n",
916 				    getenv("bootfile"));
917 			} else if (strcasecmp(setting, "kernel") == 0) {
918 				(void) printf("%s\n", getenv("bootfile"));
919 				goto done;
920 			}
921 	} else {
922 		ptr = getenv("kernelname");
923 		if (ptr != NULL) {
924 			if (*setting == '\0') {
925 				(void) printf("Kernel:      %s\n", ptr);
926 			} else if (strcasecmp(setting, "kernel") == 0) {
927 				(void) printf("%s\n", ptr);
928 				goto done;
929 			}
930 		}
931 	}
932 
933 	ptr = getenv("boot-args");
934 	if (ptr != NULL) {
935 		if (*setting == '\0') {
936 			(void) printf("Boot-args:   \"%s\"\n", ptr);
937 		} else if (strcasecmp(setting, "boot-args") == 0) {
938 			(void) printf("%s\n", ptr);
939 			goto done;
940 		}
941 	}
942 
943 	if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
944 		(void) printf("\nModules:\n");
945 		ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
946 		(void) snprintf(buf, MAX_INPUT, "show-module-options");
947 		ret = ficlVmEvaluate(vm, buf);
948 		if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
949 			bam_error(_("error interpreting boot config\n"));
950 			ret = BAM_ERROR;
951 			goto done;
952 		}
953 		ret = BAM_SUCCESS;
954 		goto done;
955 	}
956 
957 	/* if we got here with setting string, its unknown property */
958 	if (*setting != '\0') {
959 		bam_error(_("unknown property: %s\n"), setting);
960 		ret = BAM_ERROR;
961 	} else
962 		ret = BAM_SUCCESS;
963 done:
964 	bf_fini();
965 	if (mounted != BE_ERR_MOUNTED) {
966 		(void) bam_umount_be(dir);
967 	}
968 
969 	if (dir != NULL) {
970 		(void) rmdir(dir);
971 		free(dir);
972 	}
973 
974 	return (ret);
975 }
976 
977 /*ARGSUSED*/
978 static error_t
979 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
980 {
981 	error_t ret = BAM_SUCCESS;
982 	menu_entry_t *entry;
983 	char *ptr, *title = NULL;
984 	int i, e = -1;
985 
986 	if (opt == NULL) {
987 		print_nodes(B_FALSE, menu);
988 		return (ret);
989 	}
990 
991 	if ((ptr = strchr(opt, '=')) == NULL) {
992 		bam_error(_("invalid option: %s\n"), opt);
993 		return (BAM_ERROR);
994 	}
995 
996 	i = ptr - opt;
997 	if (strncmp(opt, "entry", i) == 0) {
998 		e = atoi(ptr+1);
999 	} else if (strncmp(opt, "title", i) == 0) {
1000 		title = ptr+1;
1001 	} else {
1002 		bam_error(_("invalid option: %s\n"), opt);
1003 		return (BAM_ERROR);
1004 	}
1005 
1006 	STAILQ_FOREACH(entry, menu, me_next) {
1007 		if (title != NULL) {
1008 			if (strcmp(title, entry->me_title) == 0)
1009 				break;
1010 		} else if (entry->me_idx == e)
1011 			break;
1012 	}
1013 
1014 	if (entry == NULL) {
1015 		bam_error(_("no matching entry found\n"));
1016 		return (BAM_ERROR);
1017 	}
1018 
1019 	return (list_menu_entry(entry, ""));
1020 }
1021 
1022 /*
1023  * For now this is just stub entry to support grub interface, the
1024  * known consumer is installer ict.py code, calling as:
1025  * bootadm update-menu -R /a -Z -o rdisk
1026  * Later this can be converted to do something useful.
1027  */
1028 /*ARGSUSED*/
1029 static error_t
1030 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
1031 {
1032 	char path[PATH_MAX];
1033 	char *pool = menu_root + 1;
1034 	be_node_list_t *be_nodes, *be_node;
1035 	int rv;
1036 	FILE *fp;
1037 
1038 	(void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
1039 	rv = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1040 
1041 	if (rv != BE_SUCCESS)
1042 		return (BAM_ERROR);
1043 
1044 	fp = fopen(path, "w");
1045 	if (fp == NULL) {
1046 		be_free_list(be_nodes);
1047 		return (BAM_ERROR);
1048 	}
1049 
1050 	for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1051 		if (strcmp(be_node->be_rpool, pool) == 0) {
1052 			(void) fprintf(fp, "title %s\n", be_node->be_node_name);
1053 			(void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1054 		}
1055 	}
1056 
1057 	be_free_list(be_nodes);
1058 	(void) fclose(fp);
1059 	return (BAM_SUCCESS);
1060 }
1061 
1062 /*ARGSUSED*/
1063 static error_t
1064 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1065 {
1066 	error_t ret = BAM_ERROR;
1067 	char path[PATH_MAX];
1068 	char buf[MAX_INPUT];
1069 	struct mnttab mpref = { 0 };
1070 	struct mnttab mp = { 0 };
1071 	ficlVm *vm;
1072 	char *env, *o;
1073 	FILE *fp;
1074 
1075 	(void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1076 	/*
1077 	 * if opt == NULL, remove transient config
1078 	 */
1079 	if (opt == NULL) {
1080 		(void) unlink(path);
1081 		return (BAM_SUCCESS);
1082 	}
1083 
1084 	fp = fopen(MNTTAB, "r");
1085 	if (fp == NULL)
1086 		return (BAM_ERROR);
1087 
1088 	mpref.mnt_mountp = "/";
1089 	if (getmntany(fp, &mp, &mpref) != 0) {
1090 		(void) fclose(fp);
1091 		return (BAM_ERROR);
1092 	}
1093 	(void) fclose(fp);
1094 
1095 	vm = bf_init("", ficlTextOutSilent);
1096 	if (vm == NULL) {
1097 		bam_error(_("Error setting up forth interpreter\n"));
1098 		return (ret);
1099 	}
1100 
1101 	/*
1102 	 * need to check current boot config, so fire up the ficl
1103 	 * if its xen setup, we add option to boot-args list, not replacing it.
1104 	 */
1105 	(void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1106 	ret = ficlVmEvaluate(vm, buf);
1107 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1108 		bam_error(_("Error interpreting boot config\n"));
1109 		bf_fini();
1110 		return (BAM_ERROR);
1111 	}
1112 	(void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1113 	ret = ficlVmEvaluate(vm, buf);
1114 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1115 		bam_error(_("Error interpreting boot config\n"));
1116 		bf_fini();
1117 		return (BAM_ERROR);
1118 	}
1119 	(void) snprintf(buf, MAX_INPUT, "start");
1120 	ret = ficlVmEvaluate(vm, buf);
1121 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1122 		bam_error(_("Error interpreting boot config\n"));
1123 		bf_fini();
1124 		return (BAM_ERROR);
1125 	}
1126 	(void) snprintf(buf, MAX_INPUT, "boot");
1127 	ret = ficlVmEvaluate(vm, buf);
1128 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1129 		bam_error(_("Error interpreting boot config\n"));
1130 		bf_fini();
1131 		return (BAM_ERROR);
1132 	}
1133 	bf_fini();
1134 
1135 	if (opt[0] == '-') {
1136 		env = getenv("xen_kernel");
1137 		fp = fopen(path, "w");
1138 		if (fp == NULL)
1139 			return (BAM_ERROR);
1140 
1141 		if (env != NULL) {
1142 			env = getenv("boot-args");
1143 			(void) fprintf(fp, "boot-args='%s %s'\n", env, opt);
1144 		} else
1145 			(void) fprintf(fp, "boot-args='%s'\n", opt);
1146 		(void) fclose(fp);
1147 		return (BAM_SUCCESS);
1148 	}
1149 
1150 	/*
1151 	 * it should be the case with "kernel args"
1152 	 * so, we split the opt at first space
1153 	 * and store bootfile= and boot-args=
1154 	 */
1155 	env = getenv("xen_kernel");
1156 
1157 	o = strchr(opt, ' ');
1158 	if (o == NULL) {
1159 		fp = fopen(path, "w");
1160 		if (fp == NULL)
1161 			return (BAM_ERROR);
1162 		(void) fprintf(fp, "bootfile='%s;unix'\n", opt);
1163 		(void) fclose(fp);
1164 		return (BAM_SUCCESS);
1165 	}
1166 	*o++ = '\0';
1167 	fp = fopen(path, "w");
1168 	if (fp == NULL)
1169 		return (BAM_ERROR);
1170 	(void) fprintf(fp, "bootfile='%s;unix'\n", opt);
1171 
1172 	if (env != NULL) {
1173 		env = getenv("boot-args");
1174 		(void) fprintf(fp, "boot-args='%s %s'\n", env, o);
1175 	} else
1176 		(void) fprintf(fp, "boot-args='%s'\n", o);
1177 
1178 	(void) fflush(fp);
1179 	(void) fclose(fp);
1180 	return (ret);
1181 }
1182 
1183 static error_t
1184 list_setting(struct menu_lst *menu, char *which, char *setting)
1185 {
1186 	int entry = -1;
1187 	menu_entry_t *m;
1188 	be_node_list_t *be_nodes, *be_node = NULL;
1189 	int ret;
1190 
1191 	assert(which);
1192 	assert(setting);
1193 
1194 	/*
1195 	 * which can be:
1196 	 * "" - list default entry
1197 	 * number - use for entry number
1198 	 * property name
1199 	 */
1200 	if (*which != '\0') {
1201 		if (isdigit(*which)) {
1202 			char *rest;
1203 			errno = 0;
1204 			entry = strtol(which, &rest, 10);
1205 			if (errno != 0 || *rest != '\0') {
1206 				bam_error(_("invalid boot entry number: %s\n"),
1207 				    which);
1208 				return (BAM_ERROR);
1209 			}
1210 		} else
1211 			setting = which;
1212 	}
1213 
1214 	/* find default entry */
1215 	if (entry == -1) {
1216 		ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1217 		if (ret != BE_SUCCESS) {
1218 			bam_error(_("No BE's found\n"));
1219 			return (BAM_ERROR);
1220 		}
1221 		STAILQ_FOREACH(m, menu, me_next) {
1222 			entry++;
1223 			for (be_node = be_nodes; be_node;
1224 			    be_node = be_node->be_next_node) {
1225 				if (strcmp(be_node->be_root_ds,
1226 				    m->me_bootfs) == 0)
1227 					break;
1228 			}
1229 			if (be_node != NULL &&
1230 			    be_node->be_active_on_boot == B_TRUE)
1231 				break; /* found active node */
1232 		}
1233 		be_free_list(be_nodes);
1234 		if (be_node == NULL) {
1235 			bam_error(_("None of BE nodes is marked active\n"));
1236 			return (BAM_ERROR);
1237 		}
1238 	} else {
1239 		STAILQ_FOREACH(m, menu, me_next)
1240 			if (m->me_idx == entry)
1241 				break;
1242 
1243 		if (m == NULL) {
1244 			bam_error(_("no matching entry found\n"));
1245 			return (BAM_ERROR);
1246 		}
1247 	}
1248 
1249 	return (list_menu_entry(m, setting));
1250 }
1251 
1252 /*ARGSUSED*/
1253 static error_t
1254 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1255 {
1256 	char path[PATH_MAX];
1257 
1258 	(void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1259 	(void) unlink(path);
1260 	return (BAM_SUCCESS);
1261 }
1262 
1263 /*ARGSUSED*/
1264 static error_t
1265 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1266 {
1267 	ficlVm *vm;
1268 	char path[PATH_MAX];
1269 	char buf[MAX_INPUT];
1270 	char *env;
1271 	FILE *fp;
1272 	struct mnttab mpref = { 0 };
1273 	struct mnttab mp = { 0 };
1274 	int ret;
1275 
1276 	fp = fopen(MNTTAB, "r");
1277 	if (fp == NULL)
1278 		return (BAM_ERROR);
1279 
1280 	mpref.mnt_mountp = "/";
1281 	if (getmntany(fp, &mp, &mpref) != 0) {
1282 		(void) fclose(fp);
1283 		return (BAM_ERROR);
1284 	}
1285 	(void) fclose(fp);
1286 
1287 	vm = bf_init("", ficlTextOutSilent);
1288 	if (vm == NULL) {
1289 		bam_error(_("Error setting up forth interpreter\n"));
1290 		return (BAM_ERROR);
1291 	}
1292 
1293 	/*
1294 	 * need to check current boot config, so fire up the ficl
1295 	 * if its xen setup, we add option to boot-args list, not replacing it.
1296 	 */
1297 	(void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1298 	ret = ficlVmEvaluate(vm, buf);
1299 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1300 		bam_error(_("Error interpreting boot config\n"));
1301 		bf_fini();
1302 		return (BAM_ERROR);
1303 	}
1304 	(void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1305 	ret = ficlVmEvaluate(vm, buf);
1306 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1307 		bam_error(_("Error interpreting boot config\n"));
1308 		bf_fini();
1309 		return (BAM_ERROR);
1310 	}
1311 	(void) snprintf(buf, MAX_INPUT, "start");
1312 	ret = ficlVmEvaluate(vm, buf);
1313 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1314 		bam_error(_("Error interpreting boot config\n"));
1315 		bf_fini();
1316 		return (BAM_ERROR);
1317 	}
1318 	(void) snprintf(buf, MAX_INPUT, "boot");
1319 	ret = ficlVmEvaluate(vm, buf);
1320 	if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1321 		bam_error(_("Error interpreting boot config\n"));
1322 		bf_fini();
1323 		return (BAM_ERROR);
1324 	}
1325 	bf_fini();
1326 
1327 	(void) mkdir(CONF_DIR, 0755);
1328 	(void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1329 	fp = fopen(path, "w");
1330 	if (fp == NULL) {
1331 		return (BAM_ERROR);	/* error, cant write config */
1332 	}
1333 
1334 	errno = 0;
1335 	/*
1336 	 * on write error, remove file to ensure we have bootable config.
1337 	 * note we dont mind if config exists, it will get updated
1338 	 */
1339 	(void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1340 	if (errno != 0)
1341 		goto error;
1342 
1343 	/*
1344 	 * really simple and stupid console conversion.
1345 	 * it really has to be gone, it belongs to milestone/xvm properties.
1346 	 */
1347 	env = getenv("console");
1348 	if (env != NULL) {
1349 		if (strcmp(env, "ttya") == 0)
1350 			(void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1351 			    opt);
1352 		else if (strcmp(env, "ttyb") == 0)
1353 			(void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1354 			    opt);
1355 		else
1356 			(void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1357 			    opt);
1358 	} else
1359 		(void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1360 	if (errno != 0)
1361 		goto error;
1362 
1363 	(void) fprintf(fp,
1364 	    "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1365 	if (errno != 0)
1366 		goto error;
1367 
1368 	(void) fprintf(fp,
1369 	    "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1370 	if (errno != 0)
1371 		goto error;
1372 
1373 	(void) fclose(fp);
1374 	if (errno != 0) {
1375 		(void) unlink(path);
1376 		return (BAM_ERROR);
1377 	}
1378 	return (BAM_SUCCESS);
1379 error:
1380 	(void) fclose(fp);
1381 	(void) unlink(path);
1382 	return (BAM_ERROR);
1383 }
1384