xref: /onnv-gate/usr/src/cmd/eeprom/i386/benv.c (revision 12124)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
22*12124SGangadhar.M@Sun.COM  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
233446Smrj  */
243446Smrj 
253446Smrj #include "benv.h"
263446Smrj #include "message.h"
273446Smrj #include <ctype.h>
283446Smrj #include <stdarg.h>
293446Smrj #include <sys/mman.h>
303446Smrj #include <unistd.h>
313446Smrj #include <signal.h>
323446Smrj #include <sys/wait.h>
333446Smrj 
343446Smrj /*
353446Smrj  * Usage:  % eeprom [-v] [-f prom_dev] [-]
363446Smrj  *	   % eeprom [-v] [-f prom_dev] field[=value] ...
373446Smrj  */
383446Smrj 
393446Smrj extern void get_kbenv(void);
403446Smrj extern void close_kbenv(void);
413446Smrj extern caddr_t get_propval(char *name, char *node);
423446Smrj extern void setprogname(char *prog);
4311906SGangadhar.M@Sun.COM extern char *getbootcmd(void);
443446Smrj 
453446Smrj char *boottree;
463446Smrj struct utsname uts_buf;
473446Smrj 
483446Smrj static int test;
493446Smrj int verbose;
503446Smrj 
513446Smrj /*
523446Smrj  * Concatenate a NULL terminated list of strings into
533446Smrj  * a single string.
543446Smrj  */
553446Smrj char *
563446Smrj strcats(char *s, ...)
573446Smrj {
583446Smrj 	char *cp, *ret;
593446Smrj 	size_t len;
603446Smrj 	va_list ap;
613446Smrj 
623446Smrj 	va_start(ap, s);
633446Smrj 	for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) {
643446Smrj 		if (ret == NULL) {
653446Smrj 			ret = strdup(s);
663446Smrj 			len = strlen(ret) + 1;
673446Smrj 		} else {
683446Smrj 			len += strlen(cp);
693446Smrj 			ret = realloc(ret, len);
703446Smrj 			(void) strcat(ret, cp);
713446Smrj 		}
723446Smrj 	}
733446Smrj 	va_end(ap);
743446Smrj 
753446Smrj 	return (ret);
763446Smrj }
773446Smrj 
783446Smrj eplist_t *
793446Smrj new_list(void)
803446Smrj {
813446Smrj 	eplist_t *list;
823446Smrj 
833446Smrj 	list = (eplist_t *)malloc(sizeof (eplist_t));
843446Smrj 	(void) memset(list, 0, sizeof (eplist_t));
853446Smrj 
863446Smrj 	list->next = list;
873446Smrj 	list->prev = list;
883446Smrj 	list->item = NULL;
893446Smrj 
903446Smrj 	return (list);
913446Smrj }
923446Smrj 
933446Smrj void
943446Smrj add_item(void *item, eplist_t *list)
953446Smrj {
963446Smrj 	eplist_t *entry;
973446Smrj 
983446Smrj 	entry = (eplist_t *)malloc(sizeof (eplist_t));
993446Smrj 	(void) memset(entry, 0, sizeof (eplist_t));
1003446Smrj 	entry->item = item;
1013446Smrj 
1023446Smrj 	entry->next = list;
1033446Smrj 	entry->prev = list->prev;
1043446Smrj 	list->prev->next = entry;
1053446Smrj 	list->prev = entry;
1063446Smrj }
1073446Smrj 
1083446Smrj typedef struct benv_ent {
1093446Smrj 	char *cmd;
1103446Smrj 	char *name;
1113446Smrj 	char *val;
1123446Smrj } benv_ent_t;
1133446Smrj 
1143446Smrj typedef struct benv_des {
1153446Smrj 	char *name;
1163446Smrj 	int fd;
1173446Smrj 	caddr_t adr;
1183446Smrj 	size_t len;
1193446Smrj 	eplist_t *elist;
1203446Smrj } benv_des_t;
1213446Smrj 
1223446Smrj static benv_des_t *
1233446Smrj new_bd(void)
1243446Smrj {
1253446Smrj 
1263446Smrj 	benv_des_t *bd;
1273446Smrj 
1283446Smrj 	bd = (benv_des_t *)malloc(sizeof (benv_des_t));
1293446Smrj 	(void) memset(bd, 0, sizeof (benv_des_t));
1303446Smrj 
1313446Smrj 	bd->elist = new_list();
1323446Smrj 
1333446Smrj 	return (bd);
1343446Smrj }
1353446Smrj 
1363446Smrj /*
1373446Smrj  * Create a new entry.  Comment entries have NULL names.
1383446Smrj  */
1393446Smrj static benv_ent_t *
1403446Smrj new_bent(char *comm, char *cmd, char *name, char *val)
1413446Smrj {
1423446Smrj 	benv_ent_t *bent;
1433446Smrj 
1443446Smrj 	bent = (benv_ent_t *)malloc(sizeof (benv_ent_t));
1453446Smrj 	(void) memset(bent, 0, sizeof (benv_ent_t));
1463446Smrj 
1473446Smrj 	if (comm) {
1483446Smrj 		bent->cmd = strdup(comm);
1493446Smrj 		comm = NULL;
1503446Smrj 	} else {
1513446Smrj 		bent->cmd = strdup(cmd);
1523446Smrj 		bent->name = strdup(name);
1533446Smrj 		if (val)
1543446Smrj 			bent->val = strdup(val);
1553446Smrj 	}
1563446Smrj 
1573446Smrj 	return (bent);
1583446Smrj }
1593446Smrj 
1603446Smrj /*
1613446Smrj  * Add a new entry to the benv entry list.  Entries can be
1623446Smrj  * comments or commands.
1633446Smrj  */
1643446Smrj static void
1653446Smrj add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val)
1663446Smrj {
1673446Smrj 	benv_ent_t *bent;
1683446Smrj 
1693446Smrj 	bent = new_bent(comm, cmd, name, val);
1703446Smrj 	add_item((void *)bent, list);
1713446Smrj }
1723446Smrj 
1733446Smrj static benv_ent_t *
1743446Smrj get_var(char *name, eplist_t *list)
1753446Smrj {
1763446Smrj 	eplist_t *e;
1773446Smrj 	benv_ent_t *p;
1783446Smrj 
1793446Smrj 	for (e = list->next; e != list; e = e->next) {
1803446Smrj 		p = (benv_ent_t *)e->item;
1813446Smrj 		if (p->name != NULL && strcmp(p->name, name) == 0)
1823446Smrj 			return (p);
1833446Smrj 	}
1843446Smrj 
1853446Smrj 	return (NULL);
1863446Smrj }
1873446Smrj 
1883446Smrj /*PRINTFLIKE1*/
1893446Smrj static void
1903446Smrj eeprom_error(const char *format, ...)
1913446Smrj {
1923446Smrj 	va_list ap;
1933446Smrj 
1943446Smrj 	va_start(ap, format);
1953446Smrj 	(void) fprintf(stderr, "eeprom: ");
1963446Smrj 	(void) vfprintf(stderr, format, ap);
1973446Smrj 	va_end(ap);
1983446Smrj }
1993446Smrj 
2003446Smrj static int
2013446Smrj exec_cmd(char *cmdline, char *output, int64_t osize)
2023446Smrj {
2033446Smrj 	char buf[BUFSIZ];
2043446Smrj 	int ret;
2053446Smrj 	size_t len;
2063446Smrj 	FILE *ptr;
2073446Smrj 	sigset_t set;
2083446Smrj 	void (*disp)(int);
2093446Smrj 
2103446Smrj 	if (output)
2113446Smrj 		output[0] = '\0';
2123446Smrj 
2133446Smrj 	/*
2143446Smrj 	 * For security
2153446Smrj 	 * - only absolute paths are allowed
2163446Smrj 	 * - set IFS to space and tab
2173446Smrj 	 */
2183446Smrj 	if (*cmdline != '/') {
2193446Smrj 		eeprom_error(ABS_PATH_REQ, cmdline);
2203446Smrj 		return (-1);
2213446Smrj 	}
2223446Smrj 	(void) putenv("IFS= \t");
2233446Smrj 
2243446Smrj 	/*
2253446Smrj 	 * We may have been exec'ed with SIGCHLD blocked
2263446Smrj 	 * unblock it here
2273446Smrj 	 */
2283446Smrj 	(void) sigemptyset(&set);
2293446Smrj 	(void) sigaddset(&set, SIGCHLD);
2303446Smrj 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
2313446Smrj 		eeprom_error(FAILED_SIG, strerror(errno));
2323446Smrj 		return (-1);
2333446Smrj 	}
2343446Smrj 
2353446Smrj 	/*
2363446Smrj 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
2373446Smrj 	 */
2383446Smrj 	disp = sigset(SIGCHLD, SIG_DFL);
2393446Smrj 	if (disp == SIG_ERR) {
2403446Smrj 		eeprom_error(FAILED_SIG, strerror(errno));
2413446Smrj 		return (-1);
2423446Smrj 	}
2433446Smrj 	if (disp == SIG_HOLD) {
2443446Smrj 		eeprom_error(BLOCKED_SIG, cmdline);
2453446Smrj 		return (-1);
2463446Smrj 	}
2473446Smrj 
2483446Smrj 	ptr = popen(cmdline, "r");
2493446Smrj 	if (ptr == NULL) {
2503446Smrj 		eeprom_error(POPEN_FAIL, cmdline, strerror(errno));
2513446Smrj 		return (-1);
2523446Smrj 	}
2533446Smrj 
2543446Smrj 	/*
2553446Smrj 	 * If we simply do a pclose() following a popen(), pclose()
2563446Smrj 	 * will close the reader end of the pipe immediately even
2573446Smrj 	 * if the child process has not started/exited. pclose()
2583446Smrj 	 * does wait for cmd to terminate before returning though.
2593446Smrj 	 * When the executed command writes its output to the pipe
2603446Smrj 	 * there is no reader process and the command dies with
2613446Smrj 	 * SIGPIPE. To avoid this we read repeatedly until read
2623446Smrj 	 * terminates with EOF. This indicates that the command
2633446Smrj 	 * (writer) has closed the pipe and we can safely do a
2643446Smrj 	 * pclose().
2653446Smrj 	 *
2663446Smrj 	 * Since pclose() does wait for the command to exit,
2673446Smrj 	 * we can safely reap the exit status of the command
2683446Smrj 	 * from the value returned by pclose()
2693446Smrj 	 */
2703446Smrj 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
2713446Smrj 		if (output && osize > 0) {
2723446Smrj 			(void) snprintf(output, osize, "%s", buf);
2733446Smrj 			len = strlen(buf);
2743446Smrj 			output += len;
2753446Smrj 			osize -= len;
2763446Smrj 		}
2773446Smrj 	}
2783446Smrj 
2793446Smrj 	/*
2803446Smrj 	 * If there's a "\n" at the end, we want to chop it off
2813446Smrj 	 */
2823446Smrj 	if (output) {
2833446Smrj 		len = strlen(output) - 1;
2843446Smrj 		if (output[len] == '\n')
2853446Smrj 			output[len] = '\0';
2863446Smrj 	}
2873446Smrj 
2883446Smrj 	ret = pclose(ptr);
2893446Smrj 	if (ret == -1) {
2903446Smrj 		eeprom_error(PCLOSE_FAIL, cmdline, strerror(errno));
2913446Smrj 		return (-1);
2923446Smrj 	}
2933446Smrj 
2943446Smrj 	if (WIFEXITED(ret)) {
2953446Smrj 		return (WEXITSTATUS(ret));
2963446Smrj 	} else {
2973446Smrj 		eeprom_error(EXEC_FAIL, cmdline, ret);
2983446Smrj 		return (-1);
2993446Smrj 	}
3003446Smrj }
3013446Smrj 
3023446Smrj #define	BOOTADM_STR	"bootadm: "
3033446Smrj 
3043446Smrj /*
3053446Smrj  * bootadm starts all error messages with "bootadm: ".
3063446Smrj  * Add a note so users don't get confused on how they ran bootadm.
3073446Smrj  */
3083446Smrj static void
3093446Smrj output_error_msg(const char *msg)
3103446Smrj {
3113446Smrj 	size_t len = sizeof (BOOTADM_STR) - 1;
3123446Smrj 
3133446Smrj 	if (strncmp(msg, BOOTADM_STR, len) == 0) {
3143446Smrj 		eeprom_error("error returned from %s\n", msg);
3153446Smrj 	} else if (msg[0] != '\0') {
3163446Smrj 		eeprom_error("%s\n", msg);
3173446Smrj 	}
3183446Smrj }
3193446Smrj 
3203446Smrj static char *
3213481Srscott get_bootadm_value(char *name, const int quiet)
3223446Smrj {
3233446Smrj 	char *ptr, *ret_str, *end_ptr, *orig_ptr;
3243446Smrj 	char output[BUFSIZ];
3253446Smrj 	int is_console, is_kernel = 0;
3263446Smrj 	size_t len;
3273446Smrj 
3283446Smrj 	is_console = (strcmp(name, "console") == 0);
3293446Smrj 
3303446Smrj 	if (strcmp(name, "boot-file") == 0) {
3313446Smrj 		is_kernel = 1;
3323446Smrj 		ptr = "/sbin/bootadm set-menu kernel 2>&1";
3333446Smrj 	} else if (is_console || (strcmp(name, "boot-args") == 0)) {
3343446Smrj 		ptr = "/sbin/bootadm set-menu args 2>&1";
3353446Smrj 	} else {
3363446Smrj 		eeprom_error("Unknown value in get_bootadm_value: %s\n", name);
3373446Smrj 		return (NULL);
3383446Smrj 	}
3393446Smrj 
3403446Smrj 	if (exec_cmd(ptr, output, BUFSIZ) != 0) {
3413481Srscott 		if (quiet == 0) {
3423481Srscott 			output_error_msg(output);
3433481Srscott 		}
3443446Smrj 		return (NULL);
3453446Smrj 	}
3463446Smrj 
3473446Smrj 	if (is_console) {
3483446Smrj 		if ((ptr = strstr(output, "console=")) == NULL) {
3493446Smrj 			return (NULL);
3503446Smrj 		}
3513446Smrj 		ptr += strlen("console=");
3523446Smrj 
3533446Smrj 		/*
3543446Smrj 		 * -B may have comma-separated values.  It may also be
3553446Smrj 		 * followed by other flags.
3563446Smrj 		 */
3573446Smrj 		len = strcspn(ptr, " \t,");
3583446Smrj 		ret_str = calloc(len + 1, 1);
3593446Smrj 		if (ret_str == NULL) {
3603446Smrj 			eeprom_error(NO_MEM, len + 1);
3613446Smrj 			return (NULL);
3623446Smrj 		}
3633446Smrj 		(void) strncpy(ret_str, ptr, len);
3643446Smrj 		return (ret_str);
3653446Smrj 	} else if (is_kernel) {
3663446Smrj 		ret_str = strdup(output);
3673446Smrj 		if (ret_str == NULL)
3683446Smrj 			eeprom_error(NO_MEM, strlen(output) + 1);
3693446Smrj 		return (ret_str);
3703446Smrj 	} else {
3713446Smrj 		/* If there's no console setting, we can return */
3723446Smrj 		if ((orig_ptr = strstr(output, "console=")) == NULL) {
3733446Smrj 			return (strdup(output));
3743446Smrj 		}
3753446Smrj 		len = strcspn(orig_ptr, " \t,");
3763446Smrj 		ptr = orig_ptr;
3773446Smrj 		end_ptr = orig_ptr + len + 1;
3783446Smrj 
3793446Smrj 		/* Eat up any white space */
3803446Smrj 		while ((*end_ptr == ' ') || (*end_ptr == '\t'))
3813446Smrj 			end_ptr++;
3823446Smrj 
3833446Smrj 		/*
3843446Smrj 		 * If there's data following the console string, copy it.
3853446Smrj 		 * If not, cut off the new string.
3863446Smrj 		 */
3873446Smrj 		if (*end_ptr == '\0')
3883446Smrj 			*ptr = '\0';
3893446Smrj 
3903446Smrj 		while (*end_ptr != '\0') {
3913446Smrj 			*ptr = *end_ptr;
3923446Smrj 			ptr++;
3933446Smrj 			end_ptr++;
3943446Smrj 		}
3953446Smrj 		*ptr = '\0';
3963446Smrj 		if ((strchr(output, '=') == NULL) &&
3973446Smrj 		    (strncmp(output, "-B ", 3) == 0)) {
3983446Smrj 			/*
3993446Smrj 			 * Since we removed the console setting, we no
4003446Smrj 			 * longer need the initial "-B "
4013446Smrj 			 */
4023446Smrj 			orig_ptr = output + 3;
4033446Smrj 		} else {
4043446Smrj 			orig_ptr = output;
4053446Smrj 		}
4063446Smrj 
4073446Smrj 		ret_str = strdup(orig_ptr);
4083446Smrj 		if (ret_str == NULL)
4093446Smrj 			eeprom_error(NO_MEM, strlen(orig_ptr) + 1);
4103446Smrj 		return (ret_str);
4113446Smrj 	}
4123446Smrj }
4133446Smrj 
4143446Smrj /*
4153446Smrj  * If quiet is 1, print nothing if there is no value.  If quiet is 0, print
4164088Srscott  * a message.  Return 1 if the value is printed, 0 otherwise.
4173446Smrj  */
4184088Srscott static int
4193481Srscott print_bootadm_value(char *name, const int quiet)
4203446Smrj {
4214088Srscott 	int rv = 0;
4223481Srscott 	char *value = get_bootadm_value(name, quiet);
4233446Smrj 
4243446Smrj 	if ((value != NULL) && (value[0] != '\0')) {
4253446Smrj 		(void) printf("%s=%s\n", name, value);
4264088Srscott 		rv = 1;
4273446Smrj 	} else if (quiet == 0) {
4283446Smrj 		(void) printf("%s: data not available.\n", name);
4293446Smrj 	}
4303446Smrj 
4313446Smrj 	if (value != NULL)
4323446Smrj 		free(value);
4334088Srscott 	return (rv);
4343446Smrj }
4353446Smrj 
4363446Smrj static void
4373446Smrj print_var(char *name, eplist_t *list)
4383446Smrj {
4393446Smrj 	benv_ent_t *p;
440*12124SGangadhar.M@Sun.COM 	char *bootcmd;
4413446Smrj 
4424088Srscott 	/*
4434088Srscott 	 * The console property is kept in both menu.lst and bootenv.rc.  The
4444088Srscott 	 * menu.lst value takes precedence.
4454088Srscott 	 */
4464088Srscott 	if (strcmp(name, "console") == 0) {
4474088Srscott 		if (print_bootadm_value(name, 1) == 0) {
4484088Srscott 			if ((p = get_var(name, list)) != NULL) {
4494088Srscott 				(void) printf("%s=%s\n", name, p->val ?
4504088Srscott 				    p->val : "");
4514088Srscott 			} else {
4524088Srscott 				(void) printf("%s: data not available.\n",
4534088Srscott 				    name);
4544088Srscott 			}
4554088Srscott 		}
456*12124SGangadhar.M@Sun.COM 	} else if (strcmp(name, "bootcmd") == 0) {
457*12124SGangadhar.M@Sun.COM 		bootcmd = getbootcmd();
458*12124SGangadhar.M@Sun.COM 		(void) printf("%s=%s\n", name, bootcmd ? bootcmd : "");
4594088Srscott 	} else if ((strcmp(name, "boot-file") == 0) ||
4604088Srscott 	    (strcmp(name, "boot-args") == 0)) {
4614088Srscott 		(void) print_bootadm_value(name, 0);
462*12124SGangadhar.M@Sun.COM 	} else if ((p = get_var(name, list)) == NULL) {
4633446Smrj 		(void) printf("%s: data not available.\n", name);
464*12124SGangadhar.M@Sun.COM 	} else {
4653446Smrj 		(void) printf("%s=%s\n", name, p->val ? p->val : "");
466*12124SGangadhar.M@Sun.COM 	}
4673446Smrj }
4683446Smrj 
4693446Smrj static void
4703446Smrj print_vars(eplist_t *list)
4713446Smrj {
4723446Smrj 	eplist_t *e;
4733446Smrj 	benv_ent_t *p;
4744088Srscott 	int console_printed = 0;
4754088Srscott 
4764088Srscott 	/*
4774088Srscott 	 * The console property is kept both in menu.lst and bootenv.rc.
4784088Srscott 	 * The menu.lst value takes precedence, so try printing that one
4794088Srscott 	 * first.
4804088Srscott 	 */
4814088Srscott 	console_printed = print_bootadm_value("console", 1);
4823446Smrj 
4833446Smrj 	for (e = list->next; e != list; e = e->next) {
4843446Smrj 		p = (benv_ent_t *)e->item;
4853446Smrj 		if (p->name != NULL) {
4864088Srscott 			if (((strcmp(p->name, "console") == 0) &&
4874088Srscott 			    (console_printed == 1)) ||
4884088Srscott 			    ((strcmp(p->name, "boot-file") == 0) ||
4894088Srscott 			    (strcmp(p->name, "boot-args") == 0))) {
4903446Smrj 				/* handle these separately */
4913446Smrj 				continue;
4923446Smrj 			}
4933446Smrj 			(void) printf("%s=%s\n", p->name, p->val ? p->val : "");
4943446Smrj 		}
4953446Smrj 	}
4964088Srscott 	(void) print_bootadm_value("boot-file", 1);
4974088Srscott 	(void) print_bootadm_value("boot-args", 1);
4983446Smrj }
4993446Smrj 
5003446Smrj /*
5013446Smrj  * Write a string to a file, quoted appropriately.  We use single
5023446Smrj  * quotes to prevent any variable expansion.  Of course, we backslash-quote
5033446Smrj  * any single quotes or backslashes.
5043446Smrj  */
5053446Smrj static void
5063446Smrj put_quoted(FILE *fp, char *val)
5073446Smrj {
5083446Smrj 	(void) putc('\'', fp);
5093446Smrj 	while (*val) {
5103446Smrj 		switch (*val) {
5113446Smrj 		case '\'':
5123446Smrj 		case '\\':
5133446Smrj 			(void) putc('\\', fp);
5143446Smrj 			/* FALLTHROUGH */
5153446Smrj 		default:
5163446Smrj 			(void) putc(*val, fp);
5173446Smrj 			break;
5183446Smrj 		}
5193446Smrj 		val++;
5203446Smrj 	}
5213446Smrj 	(void) putc('\'', fp);
5223446Smrj }
5233446Smrj 
5243446Smrj static void
5253446Smrj set_bootadm_var(char *name, char *value)
5263446Smrj {
5273446Smrj 	char buf[BUFSIZ];
5283446Smrj 	char output[BUFSIZ] = "";
5293481Srscott 	char *console, *args;
5303446Smrj 	int is_console;
5313446Smrj 
5323446Smrj 	if (verbose) {
5333446Smrj 		(void) printf("old:");
5344088Srscott 		(void) print_bootadm_value(name, 0);
5353446Smrj 	}
5363446Smrj 
5373446Smrj 	/*
5383446Smrj 	 * For security, we single-quote whatever we run on the command line,
5393446Smrj 	 * and we don't allow single quotes in the string.
5403446Smrj 	 */
5413481Srscott 	if (strchr(value, '\'') != NULL) {
5423446Smrj 		eeprom_error("Single quotes are not allowed "
5433446Smrj 		    "in the %s property.\n", name);
5443446Smrj 		return;
5453446Smrj 	}
5463446Smrj 
5473446Smrj 	is_console = (strcmp(name, "console") == 0);
5483446Smrj 	if (strcmp(name, "boot-file") == 0) {
5493446Smrj 		(void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu "
5503446Smrj 		    "kernel='%s' 2>&1", value);
5513446Smrj 	} else if (is_console || (strcmp(name, "boot-args") == 0)) {
5523446Smrj 		if (is_console) {
5533481Srscott 			args = get_bootadm_value("boot-args", 1);
5543446Smrj 			console = value;
5553446Smrj 		} else {
5563446Smrj 			args = value;
5573481Srscott 			console = get_bootadm_value("console", 1);
5583446Smrj 		}
5593446Smrj 		if (((args == NULL) || (args[0] == '\0')) &&
5603446Smrj 		    ((console == NULL) || (console[0] == '\0'))) {
5613446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu "
5623446Smrj 			    "args= 2>&1");
5633446Smrj 		} else if ((args == NULL) || (args[0] == '\0')) {
5643446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
5653446Smrj 			    "set-menu args='-B console=%s' 2>&1",
5663446Smrj 			    console);
5673446Smrj 		} else if ((console == NULL) || (console[0] == '\0')) {
5683446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
5693446Smrj 			    "set-menu args='%s' 2>&1", args);
5703446Smrj 		} else if (strncmp(args, "-B ", 3) != 0) {
5713446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
5723446Smrj 			    "set-menu args='-B console=%s %s' 2>&1",
5733446Smrj 			    console, args);
5743446Smrj 		} else {
5753446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
5763446Smrj 			    "set-menu args='-B console=%s,%s' 2>&1",
5773446Smrj 			    console, args + 3);
5783446Smrj 		}
5793446Smrj 	} else {
5803446Smrj 		eeprom_error("Unknown value in set_bootadm_value: %s\n", name);
5813446Smrj 		return;
5823446Smrj 	}
5833446Smrj 
5843446Smrj 	if (exec_cmd(buf, output, BUFSIZ) != 0) {
5853446Smrj 		output_error_msg(output);
5863446Smrj 		return;
5873446Smrj 	}
5883446Smrj 
5893446Smrj 	if (verbose) {
5903446Smrj 		(void) printf("new:");
5914088Srscott 		(void) print_bootadm_value(name, 0);
5923446Smrj 	}
5933446Smrj }
5943446Smrj 
5954346Srscott /*
5964346Srscott  * Returns 1 if bootenv.rc was modified, 0 otherwise.
5974346Srscott  */
5984346Srscott static int
5993446Smrj set_var(char *name, char *val, eplist_t *list)
6003446Smrj {
6013446Smrj 	benv_ent_t *p;
6024088Srscott 	int old_verbose;
6033446Smrj 
604*12124SGangadhar.M@Sun.COM 	if (strcmp(name, "bootcmd") == 0)
605*12124SGangadhar.M@Sun.COM 		return (0);
606*12124SGangadhar.M@Sun.COM 
6073446Smrj 	if ((strcmp(name, "boot-file") == 0) ||
6084088Srscott 	    (strcmp(name, "boot-args") == 0)) {
6093446Smrj 		set_bootadm_var(name, val);
6104346Srscott 		return (0);
6113446Smrj 	}
6123446Smrj 
6134088Srscott 	/*
6144088Srscott 	 * The console property is kept in two places: menu.lst and bootenv.rc.
6154088Srscott 	 * Update them both.  We clear verbose to prevent duplicate messages.
6164088Srscott 	 */
6174088Srscott 	if (strcmp(name, "console") == 0) {
6184088Srscott 		old_verbose = verbose;
6194088Srscott 		verbose = 0;
6204088Srscott 		set_bootadm_var(name, val);
6214088Srscott 		verbose = old_verbose;
6224088Srscott 	}
6234088Srscott 
6243446Smrj 	if (verbose) {
6253446Smrj 		(void) printf("old:");
6263446Smrj 		print_var(name, list);
6273446Smrj 	}
6283446Smrj 
6293446Smrj 	if ((p = get_var(name, list)) != NULL) {
6303446Smrj 		free(p->val);
6313446Smrj 		p->val = strdup(val);
6323446Smrj 	} else
6333446Smrj 		add_bent(list, NULL, "setprop", name, val);
6343446Smrj 
6353446Smrj 	if (verbose) {
6363446Smrj 		(void) printf("new:");
6373446Smrj 		print_var(name, list);
6383446Smrj 	}
6394346Srscott 	return (1);
6403446Smrj }
6413446Smrj 
6423446Smrj /*
6434346Srscott  * Returns 1 if bootenv.rc is modified or 0 if no modification was
6444346Srscott  * necessary.  This allows us to implement non super-user look-up of
6454346Srscott  * variables by name without the user being yelled at for trying to
6464346Srscott  * modify the bootenv.rc file.
6473446Smrj  */
6483446Smrj static int
6493446Smrj proc_var(char *name, eplist_t *list)
6503446Smrj {
6513446Smrj 	register char *val;
6523446Smrj 
6533446Smrj 	if ((val = strchr(name, '=')) == NULL) {
6543446Smrj 		print_var(name, list);
6553446Smrj 		return (0);
6563446Smrj 	} else {
6573446Smrj 		*val++ = '\0';
6584346Srscott 		return (set_var(name, val, list));
6593446Smrj 	}
6603446Smrj }
6613446Smrj 
6623446Smrj static void
6633446Smrj init_benv(benv_des_t *bd, char *file)
6643446Smrj {
6653446Smrj 	get_kbenv();
6663446Smrj 
6673446Smrj 	if (test)
6683446Smrj 		boottree = "/tmp";
6693446Smrj 	else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL)
6703446Smrj 		boottree = strcats("/boot", NULL);
6713446Smrj 
6723446Smrj 	if (file != NULL)
6733446Smrj 		bd->name = file;
6743446Smrj 	else
6753446Smrj 		bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL);
6763446Smrj }
6773446Smrj 
6783446Smrj static void
6793446Smrj map_benv(benv_des_t *bd)
6803446Smrj {
6813446Smrj 	if ((bd->fd = open(bd->name, O_RDONLY)) == -1)
6823446Smrj 		if (errno == ENOENT)
6833446Smrj 			return;
6843446Smrj 		else
6853446Smrj 			exit(_error(PERROR, "cannot open %s", bd->name));
6863446Smrj 
6873446Smrj 	if ((bd->len = (size_t)lseek(bd->fd, 0, SEEK_END)) == 0) {
6883446Smrj 		if (close(bd->fd) == -1)
6893446Smrj 			exit(_error(PERROR, "close error on %s", bd->name));
6903446Smrj 		return;
6913446Smrj 	}
6923446Smrj 
6933446Smrj 	(void) lseek(bd->fd, 0, SEEK_SET);
6943446Smrj 
6953446Smrj 	if ((bd->adr = mmap((caddr_t)0, bd->len, (PROT_READ | PROT_WRITE),
6963446Smrj 	    MAP_PRIVATE, bd->fd, 0)) == MAP_FAILED)
6973446Smrj 		exit(_error(PERROR, "cannot map %s", bd->name));
6983446Smrj }
6993446Smrj 
7003446Smrj static void
7013446Smrj unmap_benv(benv_des_t *bd)
7023446Smrj {
7033446Smrj 	if (munmap(bd->adr, bd->len) == -1)
7043446Smrj 		exit(_error(PERROR, "unmap error on %s", bd->name));
7053446Smrj 
7063446Smrj 	if (close(bd->fd) == -1)
7073446Smrj 		exit(_error(PERROR, "close error on %s", bd->name));
7083446Smrj }
7093446Smrj 
7103446Smrj #define	NL	'\n'
7113446Smrj #define	COMM	'#'
7123446Smrj 
7133446Smrj /*
7143446Smrj  * Add a comment block to the benv list.
7153446Smrj  */
7163446Smrj static void
7173446Smrj add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line)
7183446Smrj {
7193446Smrj 	int nl, lines;
7203446Smrj 	char *p;
7213446Smrj 
7223446Smrj 	nl = 0;
7233446Smrj 	for (p = base, lines = 0; p < last; p++) {
7243446Smrj 		if (*p == NL) {
7253446Smrj 			nl++;
7263446Smrj 			lines++;
7273446Smrj 		} else if (nl) {
7283446Smrj 			if (*p != COMM)
7293446Smrj 				break;
7303446Smrj 			nl = 0;
7313446Smrj 		}
7323446Smrj 	}
7333446Smrj 	*(p - 1) = NULL;
7343446Smrj 	add_bent(bd->elist, base, NULL, NULL, NULL);
7353446Smrj 	*next = p;
7363446Smrj 	*line += lines;
7373446Smrj }
7383446Smrj 
7393446Smrj /*
7403446Smrj  * Parse out an operator (setprop) from the boot environment
7413446Smrj  */
7423446Smrj static char *
7433446Smrj parse_cmd(benv_des_t *bd, char **next, int *line)
7443446Smrj {
7453446Smrj 	char *strbegin;
7463446Smrj 	char *badeof = "unexpected EOF in %s line %d";
7473446Smrj 	char *syntax = "syntax error in %s line %d";
7483446Smrj 	char *c = *next;
7493446Smrj 
7503446Smrj 	/*
7513446Smrj 	 * Skip spaces or tabs. New lines increase the line count.
7523446Smrj 	 */
7533446Smrj 	while (isspace(*c)) {
7543446Smrj 		if (*c++ == '\n')
7553446Smrj 			(*line)++;
7563446Smrj 	}
7573446Smrj 
7583446Smrj 	/*
7593446Smrj 	 * Check for a the setprop command.  Currently that's all we
7603446Smrj 	 * seem to support.
7613446Smrj 	 *
7623446Smrj 	 * XXX need support for setbinprop?
7633446Smrj 	 */
7643446Smrj 
7653446Smrj 	/*
7663446Smrj 	 * Check first for end of file.  Finding one now would be okay.
7673446Smrj 	 * We should also bail if we are at the start of a comment.
7683446Smrj 	 */
7693446Smrj 	if (*c == '\0' || *c == COMM) {
7703446Smrj 		*next = c;
7713446Smrj 		return (NULL);
7723446Smrj 	}
7733446Smrj 
7743446Smrj 	strbegin = c;
7753446Smrj 	while (*c && !isspace(*c))
7763446Smrj 		c++;
7773446Smrj 
7783446Smrj 	/*
7793446Smrj 	 * Check again for end of file.  Finding one now would NOT be okay.
7803446Smrj 	 */
7813446Smrj 	if (*c == '\0') {
7823446Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
7833446Smrj 	}
7843446Smrj 
7853446Smrj 	*c++ = '\0';
7863446Smrj 	*next = c;
7873446Smrj 
7883446Smrj 	/*
7893446Smrj 	 * Last check is to make sure the command is a setprop!
7903446Smrj 	 */
7913446Smrj 	if (strcmp(strbegin, "setprop") != 0) {
7923446Smrj 		exit(_error(NO_PERROR, syntax, bd->name, *line));
7933446Smrj 		/* NOTREACHED */
7943446Smrj 	}
7953446Smrj 	return (strbegin);
7963446Smrj }
7973446Smrj 
7983446Smrj /*
7993446Smrj  * Parse out the name (LHS) of a setprop from the boot environment
8003446Smrj  */
8013446Smrj static char *
8023446Smrj parse_name(benv_des_t *bd, char **next, int *line)
8033446Smrj {
8043446Smrj 	char *strbegin;
8053446Smrj 	char *badeof = "unexpected EOF in %s line %d";
8063446Smrj 	char *syntax = "syntax error in %s line %d";
8073446Smrj 	char *c = *next;
8083446Smrj 
8093446Smrj 	/*
8103446Smrj 	 * Skip spaces or tabs. No tolerance for new lines now.
8113446Smrj 	 */
8123446Smrj 	while (isspace(*c)) {
8133446Smrj 		if (*c++ == '\n')
8143446Smrj 			exit(_error(NO_PERROR, syntax, bd->name, *line));
8153446Smrj 	}
8163446Smrj 
8173446Smrj 	/*
8183446Smrj 	 * Grab a name for the property to set.
8193446Smrj 	 */
8203446Smrj 
8213446Smrj 	/*
8223446Smrj 	 * Check first for end of file.  Finding one now would NOT be okay.
8233446Smrj 	 */
8243446Smrj 	if (*c == '\0') {
8253446Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
8263446Smrj 	}
8273446Smrj 
8283446Smrj 	strbegin = c;
8293446Smrj 	while (*c && !isspace(*c))
8303446Smrj 		c++;
8313446Smrj 
8323446Smrj 	/*
8333446Smrj 	 * At this point in parsing we have 'setprop name'.  What follows
8343446Smrj 	 * is a newline, other whitespace, or EOF.  Most of the time we
8353446Smrj 	 * want to replace a white space character with a NULL to terminate
8363446Smrj 	 * the name, and then continue on processing.  A newline here provides
8373446Smrj 	 * the most grief.  If we just replace it with a null we'll
8383446Smrj 	 * potentially get the setprop on the next line as the value of this
8393446Smrj 	 * setprop! So, if the last thing we see is a newline we'll have to
8403446Smrj 	 * dup the string.
8413446Smrj 	 */
8423446Smrj 	if (isspace(*c)) {
8433446Smrj 		if (*c == '\n') {
8443446Smrj 			*c = '\0';
8453446Smrj 			strbegin = strdup(strbegin);
8463446Smrj 			*c = '\n';
8473446Smrj 		} else {
8483446Smrj 			*c++ = '\0';
8493446Smrj 		}
8503446Smrj 	}
8513446Smrj 
8523446Smrj 	*next = c;
8533446Smrj 	return (strbegin);
8543446Smrj }
8553446Smrj 
8563446Smrj /*
8573446Smrj  * Parse out the value (RHS) of a setprop line from the boot environment
8583446Smrj  */
8593446Smrj static char *
8603446Smrj parse_value(benv_des_t *bd, char **next, int *line)
8613446Smrj {
8623446Smrj 	char *strbegin;
8633446Smrj 	char *badeof = "unexpected EOF in %s line %d";
8643446Smrj 	char *result;
8653446Smrj 	char *c = *next;
8663446Smrj 	char quote;
8673446Smrj 
8683446Smrj 	/*
8693446Smrj 	 * Skip spaces or tabs. A newline here would indicate a
8703446Smrj 	 * NULL property value.
8713446Smrj 	 */
8723446Smrj 	while (isspace(*c)) {
8733446Smrj 		if (*c++ == '\n') {
8743446Smrj 			(*line)++;
8753446Smrj 			*next = c;
8763446Smrj 			return (NULL);
8773446Smrj 		}
8783446Smrj 	}
8793446Smrj 
8803446Smrj 	/*
8813446Smrj 	 * Grab the value of the property to set.
8823446Smrj 	 */
8833446Smrj 
8843446Smrj 	/*
8853446Smrj 	 * Check first for end of file.  Finding one now would
8863446Smrj 	 * also indicate a NULL property.
8873446Smrj 	 */
8883446Smrj 	if (*c == '\0') {
8893446Smrj 		*next = c;
8903446Smrj 		return (NULL);
8913446Smrj 	}
8923446Smrj 
8933446Smrj 	/*
8943446Smrj 	 * Value may be quoted, in which case we assume the end of the value
8953446Smrj 	 * comes with a closing quote.
8963446Smrj 	 *
8973446Smrj 	 * We also allow escaped quote characters inside the quoted value.
8983446Smrj 	 *
8993446Smrj 	 * For obvious reasons we do not attempt to parse variable references.
9003446Smrj 	 */
9013446Smrj 	if (*c == '"' || *c == '\'') {
9023446Smrj 		quote = *c;
9033446Smrj 		c++;
9043446Smrj 		strbegin = c;
9053446Smrj 		result = c;
9063446Smrj 		while (*c != quote) {
9073446Smrj 			if (*c == '\\') {
9083446Smrj 				c++;
9093446Smrj 			}
9107861SMark.Logan@Sun.COM 			if (*c == '\0') {
9113446Smrj 				break;
9123446Smrj 			}
9133446Smrj 			*result++ = *c++;
9143446Smrj 		}
9153446Smrj 
9163446Smrj 		/*
9173446Smrj 		 *  Throw fatal exception if no end quote found.
9183446Smrj 		 */
9193446Smrj 		if (*c != quote) {
9203446Smrj 			exit(_error(NO_PERROR, badeof, bd->name, *line));
9213446Smrj 		}
9223446Smrj 
9233446Smrj 		*result = '\0';		/* Terminate the result */
9243446Smrj 		c++;			/* and step past the close quote */
9253446Smrj 	} else {
9263446Smrj 		strbegin = c;
9273446Smrj 		while (*c && !isspace(*c))
9283446Smrj 			c++;
9293446Smrj 	}
9303446Smrj 
9313446Smrj 	/*
9323446Smrj 	 * Check again for end of file.  Finding one now is okay.
9333446Smrj 	 */
9343446Smrj 	if (*c == '\0') {
9353446Smrj 		*next = c;
9363446Smrj 		return (strbegin);
9373446Smrj 	}
9383446Smrj 
9393446Smrj 	*c++ = '\0';
9403446Smrj 	*next = c;
9413446Smrj 	return (strbegin);
9423446Smrj }
9433446Smrj 
9443446Smrj /*
9453446Smrj  * Add a command to the benv list.
9463446Smrj  */
9473446Smrj static void
9483446Smrj add_cmd(benv_des_t *bd, char *last, char **next, int *line)
9493446Smrj {
9503446Smrj 	char *cmd, *name, *val;
9513446Smrj 
9523446Smrj 	while (*next <= last && **next != COMM) {
9533446Smrj 		if ((cmd = parse_cmd(bd, next, line)) == NULL)
9543446Smrj 			break;
9553446Smrj 		name = parse_name(bd, next, line);
9563446Smrj 		val = parse_value(bd, next, line);
9573446Smrj 		add_bent(bd->elist, NULL, cmd, name, val);
9583446Smrj 		(*line)++;
9593446Smrj 	};
96011906SGangadhar.M@Sun.COM 
9613446Smrj }
9623446Smrj 
9633446Smrj /*
9643446Smrj  * Parse the benv (bootenv.rc) file and break it into a benv
9653446Smrj  * list.  List entries may be comment blocks or commands.
9663446Smrj  */
9673446Smrj static void
9683446Smrj parse_benv(benv_des_t *bd)
9693446Smrj {
9703446Smrj 	int line;
9713446Smrj 	char *pbase, *pend;
9723446Smrj 	char *tok, *tnext;
9733446Smrj 
9743446Smrj 	line = 1;
9753446Smrj 	pbase = (char *)bd->adr;
9763446Smrj 	pend = pbase + bd->len;
9773446Smrj 
9787861SMark.Logan@Sun.COM 	for (tok = tnext = pbase; tnext < pend && '\0' != *tnext; tok = tnext)
9793446Smrj 		if (*tok == COMM)
9803446Smrj 			add_comm(bd, tok, pend, &tnext, &line);
9813446Smrj 		else
9823446Smrj 			add_cmd(bd, pend, &tnext, &line);
9833446Smrj }
9843446Smrj 
9853446Smrj static void
9863446Smrj write_benv(benv_des_t *bd)
9873446Smrj {
9883446Smrj 	FILE *fp;
9893446Smrj 	eplist_t *list, *e;
9903446Smrj 	benv_ent_t *bent;
9913446Smrj 	char *name;
9923446Smrj 
9933446Smrj 	list = bd->elist;
9943446Smrj 
9953446Smrj 	if (list->next == list)
9963446Smrj 		return;
9973446Smrj 
9983446Smrj 	if ((fp = fopen(bd->name, "w")) == NULL)
9993446Smrj 		exit(_error(PERROR, "cannot open %s", bd->name));
10003446Smrj 
10013446Smrj 	for (e = list->next; e != list; e = e->next) {
10023446Smrj 		bent = (benv_ent_t *)e->item;
10033446Smrj 		name = bent->name;
10043446Smrj 		if (name) {
10053446Smrj 			if (bent->val) {
10063446Smrj 				(void) fprintf(fp, "%s %s ",
10074346Srscott 				    bent->cmd, bent->name);
10083446Smrj 				put_quoted(fp, bent->val);
10093446Smrj 				(void) fprintf(fp, "\n");
10103446Smrj 			} else {
10113446Smrj 				(void) fprintf(fp, "%s %s\n",
10124346Srscott 				    bent->cmd, bent->name);
10133446Smrj 			}
10143446Smrj 		} else {
10153446Smrj 			(void) fprintf(fp, "%s\n", bent->cmd);
10163446Smrj 		}
10173446Smrj 	}
10183446Smrj 
10193446Smrj 	(void) fclose(fp);
10203446Smrj }
10213446Smrj 
10223446Smrj static char *
10233446Smrj get_line(void)
10243446Smrj {
10253446Smrj 	int c;
10263446Smrj 	char *nl;
10273446Smrj 	static char line[256];
10283446Smrj 
10293446Smrj 	if (fgets(line, sizeof (line), stdin) != NULL) {
10303446Smrj 		/*
10313446Smrj 		 * Remove newline if present,
10323446Smrj 		 * otherwise discard rest of line.
10333446Smrj 		 */
10343446Smrj 		if (nl = strchr(line, '\n'))
10353446Smrj 			*nl = 0;
10363446Smrj 		else
10373446Smrj 			while ((c = getchar()) != '\n' && c != EOF)
10383446Smrj 				;
10393446Smrj 		return (line);
10403446Smrj 	} else
10413446Smrj 		return (NULL);
10423446Smrj }
10433446Smrj 
10443446Smrj int
10453446Smrj main(int argc, char **argv)
10463446Smrj {
10473446Smrj 	int c;
10483446Smrj 	int updates = 0;
10493446Smrj 	char *usage = "Usage: %s [-v] [-f prom-device]"
10503446Smrj 	    " [variable[=value] ...]";
10513446Smrj 	eplist_t *elist;
10523446Smrj 	benv_des_t *bd;
10533446Smrj 	char *file = NULL;
10543446Smrj 
10553446Smrj 	setprogname(argv[0]);
10563446Smrj 
10573446Smrj 	while ((c = getopt(argc, argv, "f:Itv")) != -1)
10583446Smrj 		switch (c) {
10593446Smrj 		case 'v':
10603446Smrj 			verbose++;
10613446Smrj 			break;
10623446Smrj 		case 'f':
10633446Smrj 			file = optarg;
10643446Smrj 			break;
10653446Smrj 		case 't':
10663446Smrj 			test++;
10673446Smrj 			break;
10683446Smrj 		default:
10693446Smrj 			exit(_error(NO_PERROR, usage, argv[0]));
10703446Smrj 		}
10713446Smrj 
10723446Smrj 	(void) uname(&uts_buf);
10733446Smrj 	bd = new_bd();
10743446Smrj 	init_benv(bd, file);
10753446Smrj 
10763446Smrj 	map_benv(bd);
10773446Smrj 	if (bd->len) {
10783446Smrj 		parse_benv(bd);
10793446Smrj 		unmap_benv(bd);
10803446Smrj 	}
10813446Smrj 
10823446Smrj 	elist = bd->elist;
10833446Smrj 
10843446Smrj 	if (optind >= argc) {
10853446Smrj 		print_vars(elist);
10863446Smrj 		return (0);
10873446Smrj 	} else
10883446Smrj 		while (optind < argc) {
10893446Smrj 			/*
10903446Smrj 			 * If "-" specified, read variables from stdin;
10913446Smrj 			 * otherwise, process each argument as a variable
10923446Smrj 			 * print or set request.
10933446Smrj 			 */
10943446Smrj 			if (strcmp(argv[optind], "-") == 0) {
10953446Smrj 				char *line;
10963446Smrj 
10973446Smrj 				while ((line = get_line()) != NULL)
10983446Smrj 					updates += proc_var(line, elist);
10993446Smrj 				clearerr(stdin);
11003446Smrj 			} else
11013446Smrj 				updates += proc_var(argv[optind], elist);
11023446Smrj 
11033446Smrj 			optind++;
11043446Smrj 		}
11053446Smrj 
11063446Smrj 	/*
11073446Smrj 	 * don't write benv if we are processing delayed writes since
11083446Smrj 	 * it is likely that the delayed writes changes bootenv.rc anyway...
11093446Smrj 	 */
11103446Smrj 	if (updates)
11113446Smrj 		write_benv(bd);
11123446Smrj 	close_kbenv();
11133446Smrj 
11143446Smrj 	return (0);
11153446Smrj }
1116