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