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