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