xref: /onnv-gate/usr/src/cmd/eeprom/i386/benv.c (revision 3446)
1*3446Smrj /*
2*3446Smrj  * CDDL HEADER START
3*3446Smrj  *
4*3446Smrj  * The contents of this file are subject to the terms of the
5*3446Smrj  * Common Development and Distribution License (the "License").
6*3446Smrj  * You may not use this file except in compliance with the License.
7*3446Smrj  *
8*3446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3446Smrj  * or http://www.opensolaris.org/os/licensing.
10*3446Smrj  * See the License for the specific language governing permissions
11*3446Smrj  * and limitations under the License.
12*3446Smrj  *
13*3446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14*3446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3446Smrj  * If applicable, add the following below this CDDL HEADER, with the
16*3446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17*3446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3446Smrj  *
19*3446Smrj  * CDDL HEADER END
20*3446Smrj  */
21*3446Smrj /*
22*3446Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*3446Smrj  * Use is subject to license terms.
24*3446Smrj  */
25*3446Smrj 
26*3446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*3446Smrj 
28*3446Smrj #include "benv.h"
29*3446Smrj #include "message.h"
30*3446Smrj #include <ctype.h>
31*3446Smrj #include <stdarg.h>
32*3446Smrj #include <sys/mman.h>
33*3446Smrj #include <unistd.h>
34*3446Smrj #include <signal.h>
35*3446Smrj #include <sys/wait.h>
36*3446Smrj 
37*3446Smrj /*
38*3446Smrj  * Usage:  % eeprom [-v] [-f prom_dev] [-]
39*3446Smrj  *	   % eeprom [-v] [-f prom_dev] field[=value] ...
40*3446Smrj  */
41*3446Smrj 
42*3446Smrj extern void get_kbenv(void);
43*3446Smrj extern void close_kbenv(void);
44*3446Smrj extern caddr_t get_propval(char *name, char *node);
45*3446Smrj extern void setprogname(char *prog);
46*3446Smrj 
47*3446Smrj char *boottree;
48*3446Smrj struct utsname uts_buf;
49*3446Smrj 
50*3446Smrj static int test;
51*3446Smrj int verbose;
52*3446Smrj 
53*3446Smrj /*
54*3446Smrj  * Concatenate a NULL terminated list of strings into
55*3446Smrj  * a single string.
56*3446Smrj  */
57*3446Smrj char *
58*3446Smrj strcats(char *s, ...)
59*3446Smrj {
60*3446Smrj 	char *cp, *ret;
61*3446Smrj 	size_t len;
62*3446Smrj 	va_list ap;
63*3446Smrj 
64*3446Smrj 	va_start(ap, s);
65*3446Smrj 	for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) {
66*3446Smrj 		if (ret == NULL) {
67*3446Smrj 			ret = strdup(s);
68*3446Smrj 			len = strlen(ret) + 1;
69*3446Smrj 		} else {
70*3446Smrj 			len += strlen(cp);
71*3446Smrj 			ret = realloc(ret, len);
72*3446Smrj 			(void) strcat(ret, cp);
73*3446Smrj 		}
74*3446Smrj 	}
75*3446Smrj 	va_end(ap);
76*3446Smrj 
77*3446Smrj 	return (ret);
78*3446Smrj }
79*3446Smrj 
80*3446Smrj eplist_t *
81*3446Smrj new_list(void)
82*3446Smrj {
83*3446Smrj 	eplist_t *list;
84*3446Smrj 
85*3446Smrj 	list = (eplist_t *)malloc(sizeof (eplist_t));
86*3446Smrj 	(void) memset(list, 0, sizeof (eplist_t));
87*3446Smrj 
88*3446Smrj 	list->next = list;
89*3446Smrj 	list->prev = list;
90*3446Smrj 	list->item = NULL;
91*3446Smrj 
92*3446Smrj 	return (list);
93*3446Smrj }
94*3446Smrj 
95*3446Smrj void
96*3446Smrj add_item(void *item, eplist_t *list)
97*3446Smrj {
98*3446Smrj 	eplist_t *entry;
99*3446Smrj 
100*3446Smrj 	entry = (eplist_t *)malloc(sizeof (eplist_t));
101*3446Smrj 	(void) memset(entry, 0, sizeof (eplist_t));
102*3446Smrj 	entry->item = item;
103*3446Smrj 
104*3446Smrj 	entry->next = list;
105*3446Smrj 	entry->prev = list->prev;
106*3446Smrj 	list->prev->next = entry;
107*3446Smrj 	list->prev = entry;
108*3446Smrj }
109*3446Smrj 
110*3446Smrj typedef struct benv_ent {
111*3446Smrj 	char *cmd;
112*3446Smrj 	char *name;
113*3446Smrj 	char *val;
114*3446Smrj } benv_ent_t;
115*3446Smrj 
116*3446Smrj typedef struct benv_des {
117*3446Smrj 	char *name;
118*3446Smrj 	int fd;
119*3446Smrj 	caddr_t adr;
120*3446Smrj 	size_t len;
121*3446Smrj 	eplist_t *elist;
122*3446Smrj } benv_des_t;
123*3446Smrj 
124*3446Smrj static benv_des_t *
125*3446Smrj new_bd(void)
126*3446Smrj {
127*3446Smrj 
128*3446Smrj 	benv_des_t *bd;
129*3446Smrj 
130*3446Smrj 	bd = (benv_des_t *)malloc(sizeof (benv_des_t));
131*3446Smrj 	(void) memset(bd, 0, sizeof (benv_des_t));
132*3446Smrj 
133*3446Smrj 	bd->elist = new_list();
134*3446Smrj 
135*3446Smrj 	return (bd);
136*3446Smrj }
137*3446Smrj 
138*3446Smrj /*
139*3446Smrj  * Create a new entry.  Comment entries have NULL names.
140*3446Smrj  */
141*3446Smrj static benv_ent_t *
142*3446Smrj new_bent(char *comm, char *cmd, char *name, char *val)
143*3446Smrj {
144*3446Smrj 	benv_ent_t *bent;
145*3446Smrj 
146*3446Smrj 	bent = (benv_ent_t *)malloc(sizeof (benv_ent_t));
147*3446Smrj 	(void) memset(bent, 0, sizeof (benv_ent_t));
148*3446Smrj 
149*3446Smrj 	if (comm) {
150*3446Smrj 		bent->cmd = strdup(comm);
151*3446Smrj 		comm = NULL;
152*3446Smrj 	} else {
153*3446Smrj 		bent->cmd = strdup(cmd);
154*3446Smrj 		bent->name = strdup(name);
155*3446Smrj 		if (val)
156*3446Smrj 			bent->val = strdup(val);
157*3446Smrj 	}
158*3446Smrj 
159*3446Smrj 	return (bent);
160*3446Smrj }
161*3446Smrj 
162*3446Smrj /*
163*3446Smrj  * Add a new entry to the benv entry list.  Entries can be
164*3446Smrj  * comments or commands.
165*3446Smrj  */
166*3446Smrj static void
167*3446Smrj add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val)
168*3446Smrj {
169*3446Smrj 	benv_ent_t *bent;
170*3446Smrj 
171*3446Smrj 	bent = new_bent(comm, cmd, name, val);
172*3446Smrj 	add_item((void *)bent, list);
173*3446Smrj }
174*3446Smrj 
175*3446Smrj static benv_ent_t *
176*3446Smrj get_var(char *name, eplist_t *list)
177*3446Smrj {
178*3446Smrj 	eplist_t *e;
179*3446Smrj 	benv_ent_t *p;
180*3446Smrj 
181*3446Smrj 	for (e = list->next; e != list; e = e->next) {
182*3446Smrj 		p = (benv_ent_t *)e->item;
183*3446Smrj 		if (p->name != NULL && strcmp(p->name, name) == 0)
184*3446Smrj 			return (p);
185*3446Smrj 	}
186*3446Smrj 
187*3446Smrj 	return (NULL);
188*3446Smrj }
189*3446Smrj 
190*3446Smrj /*PRINTFLIKE1*/
191*3446Smrj static void
192*3446Smrj eeprom_error(const char *format, ...)
193*3446Smrj {
194*3446Smrj 	va_list ap;
195*3446Smrj 
196*3446Smrj 	va_start(ap, format);
197*3446Smrj 	(void) fprintf(stderr, "eeprom: ");
198*3446Smrj 	(void) vfprintf(stderr, format, ap);
199*3446Smrj 	va_end(ap);
200*3446Smrj }
201*3446Smrj 
202*3446Smrj static int
203*3446Smrj exec_cmd(char *cmdline, char *output, int64_t osize)
204*3446Smrj {
205*3446Smrj 	char buf[BUFSIZ];
206*3446Smrj 	int ret;
207*3446Smrj 	size_t len;
208*3446Smrj 	FILE *ptr;
209*3446Smrj 	sigset_t set;
210*3446Smrj 	void (*disp)(int);
211*3446Smrj 
212*3446Smrj 	if (output)
213*3446Smrj 		output[0] = '\0';
214*3446Smrj 
215*3446Smrj 	/*
216*3446Smrj 	 * For security
217*3446Smrj 	 * - only absolute paths are allowed
218*3446Smrj 	 * - set IFS to space and tab
219*3446Smrj 	 */
220*3446Smrj 	if (*cmdline != '/') {
221*3446Smrj 		eeprom_error(ABS_PATH_REQ, cmdline);
222*3446Smrj 		return (-1);
223*3446Smrj 	}
224*3446Smrj 	(void) putenv("IFS= \t");
225*3446Smrj 
226*3446Smrj 	/*
227*3446Smrj 	 * We may have been exec'ed with SIGCHLD blocked
228*3446Smrj 	 * unblock it here
229*3446Smrj 	 */
230*3446Smrj 	(void) sigemptyset(&set);
231*3446Smrj 	(void) sigaddset(&set, SIGCHLD);
232*3446Smrj 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
233*3446Smrj 		eeprom_error(FAILED_SIG, strerror(errno));
234*3446Smrj 		return (-1);
235*3446Smrj 	}
236*3446Smrj 
237*3446Smrj 	/*
238*3446Smrj 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
239*3446Smrj 	 */
240*3446Smrj 	disp = sigset(SIGCHLD, SIG_DFL);
241*3446Smrj 	if (disp == SIG_ERR) {
242*3446Smrj 		eeprom_error(FAILED_SIG, strerror(errno));
243*3446Smrj 		return (-1);
244*3446Smrj 	}
245*3446Smrj 	if (disp == SIG_HOLD) {
246*3446Smrj 		eeprom_error(BLOCKED_SIG, cmdline);
247*3446Smrj 		return (-1);
248*3446Smrj 	}
249*3446Smrj 
250*3446Smrj 	ptr = popen(cmdline, "r");
251*3446Smrj 	if (ptr == NULL) {
252*3446Smrj 		eeprom_error(POPEN_FAIL, cmdline, strerror(errno));
253*3446Smrj 		return (-1);
254*3446Smrj 	}
255*3446Smrj 
256*3446Smrj 	/*
257*3446Smrj 	 * If we simply do a pclose() following a popen(), pclose()
258*3446Smrj 	 * will close the reader end of the pipe immediately even
259*3446Smrj 	 * if the child process has not started/exited. pclose()
260*3446Smrj 	 * does wait for cmd to terminate before returning though.
261*3446Smrj 	 * When the executed command writes its output to the pipe
262*3446Smrj 	 * there is no reader process and the command dies with
263*3446Smrj 	 * SIGPIPE. To avoid this we read repeatedly until read
264*3446Smrj 	 * terminates with EOF. This indicates that the command
265*3446Smrj 	 * (writer) has closed the pipe and we can safely do a
266*3446Smrj 	 * pclose().
267*3446Smrj 	 *
268*3446Smrj 	 * Since pclose() does wait for the command to exit,
269*3446Smrj 	 * we can safely reap the exit status of the command
270*3446Smrj 	 * from the value returned by pclose()
271*3446Smrj 	 */
272*3446Smrj 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
273*3446Smrj 		if (output && osize > 0) {
274*3446Smrj 			(void) snprintf(output, osize, "%s", buf);
275*3446Smrj 			len = strlen(buf);
276*3446Smrj 			output += len;
277*3446Smrj 			osize -= len;
278*3446Smrj 		}
279*3446Smrj 	}
280*3446Smrj 
281*3446Smrj 	/*
282*3446Smrj 	 * If there's a "\n" at the end, we want to chop it off
283*3446Smrj 	 */
284*3446Smrj 	if (output) {
285*3446Smrj 		len = strlen(output) - 1;
286*3446Smrj 		if (output[len] == '\n')
287*3446Smrj 			output[len] = '\0';
288*3446Smrj 	}
289*3446Smrj 
290*3446Smrj 	ret = pclose(ptr);
291*3446Smrj 	if (ret == -1) {
292*3446Smrj 		eeprom_error(PCLOSE_FAIL, cmdline, strerror(errno));
293*3446Smrj 		return (-1);
294*3446Smrj 	}
295*3446Smrj 
296*3446Smrj 	if (WIFEXITED(ret)) {
297*3446Smrj 		return (WEXITSTATUS(ret));
298*3446Smrj 	} else {
299*3446Smrj 		eeprom_error(EXEC_FAIL, cmdline, ret);
300*3446Smrj 		return (-1);
301*3446Smrj 	}
302*3446Smrj }
303*3446Smrj 
304*3446Smrj #define	BOOTADM_STR	"bootadm: "
305*3446Smrj 
306*3446Smrj /*
307*3446Smrj  * bootadm starts all error messages with "bootadm: ".
308*3446Smrj  * Add a note so users don't get confused on how they ran bootadm.
309*3446Smrj  */
310*3446Smrj static void
311*3446Smrj output_error_msg(const char *msg)
312*3446Smrj {
313*3446Smrj 	size_t len = sizeof (BOOTADM_STR) - 1;
314*3446Smrj 
315*3446Smrj 	if (strncmp(msg, BOOTADM_STR, len) == 0) {
316*3446Smrj 		eeprom_error("error returned from %s\n", msg);
317*3446Smrj 	} else if (msg[0] != '\0') {
318*3446Smrj 		eeprom_error("%s\n", msg);
319*3446Smrj 	}
320*3446Smrj }
321*3446Smrj 
322*3446Smrj static char *
323*3446Smrj get_bootadm_value(char *name)
324*3446Smrj {
325*3446Smrj 	char *ptr, *ret_str, *end_ptr, *orig_ptr;
326*3446Smrj 	char output[BUFSIZ];
327*3446Smrj 	int is_console, is_kernel = 0;
328*3446Smrj 	size_t len;
329*3446Smrj 
330*3446Smrj 	is_console = (strcmp(name, "console") == 0);
331*3446Smrj 
332*3446Smrj 	if (strcmp(name, "boot-file") == 0) {
333*3446Smrj 		is_kernel = 1;
334*3446Smrj 		ptr = "/sbin/bootadm set-menu kernel 2>&1";
335*3446Smrj 	} else if (is_console || (strcmp(name, "boot-args") == 0)) {
336*3446Smrj 		ptr = "/sbin/bootadm set-menu args 2>&1";
337*3446Smrj 	} else {
338*3446Smrj 		eeprom_error("Unknown value in get_bootadm_value: %s\n", name);
339*3446Smrj 		return (NULL);
340*3446Smrj 	}
341*3446Smrj 
342*3446Smrj 	if (exec_cmd(ptr, output, BUFSIZ) != 0) {
343*3446Smrj 		output_error_msg(output);
344*3446Smrj 		return (NULL);
345*3446Smrj 	}
346*3446Smrj 
347*3446Smrj 	if (is_console) {
348*3446Smrj 		if ((ptr = strstr(output, "console=")) == NULL) {
349*3446Smrj 			return (NULL);
350*3446Smrj 		}
351*3446Smrj 		ptr += strlen("console=");
352*3446Smrj 
353*3446Smrj 		/*
354*3446Smrj 		 * -B may have comma-separated values.  It may also be
355*3446Smrj 		 * followed by other flags.
356*3446Smrj 		 */
357*3446Smrj 		len = strcspn(ptr, " \t,");
358*3446Smrj 		ret_str = calloc(len + 1, 1);
359*3446Smrj 		if (ret_str == NULL) {
360*3446Smrj 			eeprom_error(NO_MEM, len + 1);
361*3446Smrj 			return (NULL);
362*3446Smrj 		}
363*3446Smrj 		(void) strncpy(ret_str, ptr, len);
364*3446Smrj 		return (ret_str);
365*3446Smrj 	} else if (is_kernel) {
366*3446Smrj 		ret_str = strdup(output);
367*3446Smrj 		if (ret_str == NULL)
368*3446Smrj 			eeprom_error(NO_MEM, strlen(output) + 1);
369*3446Smrj 		return (ret_str);
370*3446Smrj 	} else {
371*3446Smrj 		/* If there's no console setting, we can return */
372*3446Smrj 		if ((orig_ptr = strstr(output, "console=")) == NULL) {
373*3446Smrj 			return (strdup(output));
374*3446Smrj 		}
375*3446Smrj 		len = strcspn(orig_ptr, " \t,");
376*3446Smrj 		ptr = orig_ptr;
377*3446Smrj 		end_ptr = orig_ptr + len + 1;
378*3446Smrj 
379*3446Smrj 		/* Eat up any white space */
380*3446Smrj 		while ((*end_ptr == ' ') || (*end_ptr == '\t'))
381*3446Smrj 			end_ptr++;
382*3446Smrj 
383*3446Smrj 		/*
384*3446Smrj 		 * If there's data following the console string, copy it.
385*3446Smrj 		 * If not, cut off the new string.
386*3446Smrj 		 */
387*3446Smrj 		if (*end_ptr == '\0')
388*3446Smrj 			*ptr = '\0';
389*3446Smrj 
390*3446Smrj 		while (*end_ptr != '\0') {
391*3446Smrj 			*ptr = *end_ptr;
392*3446Smrj 			ptr++;
393*3446Smrj 			end_ptr++;
394*3446Smrj 		}
395*3446Smrj 		*ptr = '\0';
396*3446Smrj 		if ((strchr(output, '=') == NULL) &&
397*3446Smrj 		    (strncmp(output, "-B ", 3) == 0)) {
398*3446Smrj 			/*
399*3446Smrj 			 * Since we removed the console setting, we no
400*3446Smrj 			 * longer need the initial "-B "
401*3446Smrj 			 */
402*3446Smrj 			orig_ptr = output + 3;
403*3446Smrj 		} else {
404*3446Smrj 			orig_ptr = output;
405*3446Smrj 		}
406*3446Smrj 
407*3446Smrj 		ret_str = strdup(orig_ptr);
408*3446Smrj 		if (ret_str == NULL)
409*3446Smrj 			eeprom_error(NO_MEM, strlen(orig_ptr) + 1);
410*3446Smrj 		return (ret_str);
411*3446Smrj 	}
412*3446Smrj }
413*3446Smrj 
414*3446Smrj /*
415*3446Smrj  * If quiet is 1, print nothing if there is no value.  If quiet is 0, print
416*3446Smrj  * a message.
417*3446Smrj  */
418*3446Smrj static void
419*3446Smrj print_bootadm_value(char *name, int quiet)
420*3446Smrj {
421*3446Smrj 	char *value = get_bootadm_value(name);
422*3446Smrj 
423*3446Smrj 	if ((value != NULL) && (value[0] != '\0')) {
424*3446Smrj 		(void) printf("%s=%s\n", name, value);
425*3446Smrj 	} else if (quiet == 0) {
426*3446Smrj 		(void) printf("%s: data not available.\n", name);
427*3446Smrj 	}
428*3446Smrj 
429*3446Smrj 	if (value != NULL)
430*3446Smrj 		free(value);
431*3446Smrj }
432*3446Smrj 
433*3446Smrj static void
434*3446Smrj print_var(char *name, eplist_t *list)
435*3446Smrj {
436*3446Smrj 	benv_ent_t *p;
437*3446Smrj 
438*3446Smrj 	if ((strcmp(name, "boot-file") == 0) ||
439*3446Smrj 	    (strcmp(name, "boot-args") == 0) ||
440*3446Smrj 	    (strcmp(name, "console") == 0)) {
441*3446Smrj 		print_bootadm_value(name, 0);
442*3446Smrj 	} else if ((p = get_var(name, list)) == NULL)
443*3446Smrj 		(void) printf("%s: data not available.\n", name);
444*3446Smrj 	else
445*3446Smrj 		(void) printf("%s=%s\n", name, p->val ? p->val : "");
446*3446Smrj }
447*3446Smrj 
448*3446Smrj static void
449*3446Smrj print_vars(eplist_t *list)
450*3446Smrj {
451*3446Smrj 	eplist_t *e;
452*3446Smrj 	benv_ent_t *p;
453*3446Smrj 
454*3446Smrj 	for (e = list->next; e != list; e = e->next) {
455*3446Smrj 		p = (benv_ent_t *)e->item;
456*3446Smrj 		if (p->name != NULL) {
457*3446Smrj 			if ((strcmp(p->name, "boot-file") == 0) ||
458*3446Smrj 			    (strcmp(p->name, "boot-args") == 0) ||
459*3446Smrj 			    (strcmp(p->name, "console") == 0)) {
460*3446Smrj 				/* handle these separately */
461*3446Smrj 				continue;
462*3446Smrj 			}
463*3446Smrj 			(void) printf("%s=%s\n", p->name, p->val ? p->val : "");
464*3446Smrj 		}
465*3446Smrj 	}
466*3446Smrj 	print_bootadm_value("boot-file", 1);
467*3446Smrj 	print_bootadm_value("boot-args", 1);
468*3446Smrj 	print_bootadm_value("console", 1);
469*3446Smrj }
470*3446Smrj 
471*3446Smrj /*
472*3446Smrj  * Write a string to a file, quoted appropriately.  We use single
473*3446Smrj  * quotes to prevent any variable expansion.  Of course, we backslash-quote
474*3446Smrj  * any single quotes or backslashes.
475*3446Smrj  */
476*3446Smrj static void
477*3446Smrj put_quoted(FILE *fp, char *val)
478*3446Smrj {
479*3446Smrj 	(void) putc('\'', fp);
480*3446Smrj 	while (*val) {
481*3446Smrj 		switch (*val) {
482*3446Smrj 		case '\'':
483*3446Smrj 		case '\\':
484*3446Smrj 			(void) putc('\\', fp);
485*3446Smrj 			/* FALLTHROUGH */
486*3446Smrj 		default:
487*3446Smrj 			(void) putc(*val, fp);
488*3446Smrj 			break;
489*3446Smrj 		}
490*3446Smrj 		val++;
491*3446Smrj 	}
492*3446Smrj 	(void) putc('\'', fp);
493*3446Smrj }
494*3446Smrj 
495*3446Smrj static void
496*3446Smrj set_bootadm_var(char *name, char *value)
497*3446Smrj {
498*3446Smrj 	char buf[BUFSIZ];
499*3446Smrj 	char output[BUFSIZ] = "";
500*3446Smrj 	char *ptr, *console, *args;
501*3446Smrj 	int is_console;
502*3446Smrj 
503*3446Smrj 	if (verbose) {
504*3446Smrj 		(void) printf("old:");
505*3446Smrj 		print_bootadm_value(name, 0);
506*3446Smrj 	}
507*3446Smrj 
508*3446Smrj 	/*
509*3446Smrj 	 * For security, we single-quote whatever we run on the command line,
510*3446Smrj 	 * and we don't allow single quotes in the string.
511*3446Smrj 	 */
512*3446Smrj 	if ((ptr = strchr(value, '\'')) != NULL) {
513*3446Smrj 		eeprom_error("Single quotes are not allowed "
514*3446Smrj 		    "in the %s property.\n", name);
515*3446Smrj 		return;
516*3446Smrj 	}
517*3446Smrj 
518*3446Smrj 	is_console = (strcmp(name, "console") == 0);
519*3446Smrj 	if (strcmp(name, "boot-file") == 0) {
520*3446Smrj 		(void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu "
521*3446Smrj 		    "kernel='%s' 2>&1", value);
522*3446Smrj 	} else if (is_console || (strcmp(name, "boot-args") == 0)) {
523*3446Smrj 		if (is_console) {
524*3446Smrj 			args = get_bootadm_value("boot-args");
525*3446Smrj 			console = value;
526*3446Smrj 		} else {
527*3446Smrj 			args = value;
528*3446Smrj 			console = get_bootadm_value("console");
529*3446Smrj 		}
530*3446Smrj 		if (((args == NULL) || (args[0] == '\0')) &&
531*3446Smrj 		    ((console == NULL) || (console[0] == '\0'))) {
532*3446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu "
533*3446Smrj 			    "args= 2>&1");
534*3446Smrj 		} else if ((args == NULL) || (args[0] == '\0')) {
535*3446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
536*3446Smrj 			    "set-menu args='-B console=%s' 2>&1",
537*3446Smrj 			    console);
538*3446Smrj 		} else if ((console == NULL) || (console[0] == '\0')) {
539*3446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
540*3446Smrj 			    "set-menu args='%s' 2>&1", args);
541*3446Smrj 		} else if (strncmp(args, "-B ", 3) != 0) {
542*3446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
543*3446Smrj 			    "set-menu args='-B console=%s %s' 2>&1",
544*3446Smrj 			    console, args);
545*3446Smrj 		} else {
546*3446Smrj 			(void) snprintf(buf, BUFSIZ, "/sbin/bootadm "
547*3446Smrj 			    "set-menu args='-B console=%s,%s' 2>&1",
548*3446Smrj 			    console, args + 3);
549*3446Smrj 		}
550*3446Smrj 	} else {
551*3446Smrj 		eeprom_error("Unknown value in set_bootadm_value: %s\n", name);
552*3446Smrj 		return;
553*3446Smrj 	}
554*3446Smrj 
555*3446Smrj 	if (exec_cmd(buf, output, BUFSIZ) != 0) {
556*3446Smrj 		output_error_msg(output);
557*3446Smrj 		return;
558*3446Smrj 	}
559*3446Smrj 
560*3446Smrj 	if (verbose) {
561*3446Smrj 		(void) printf("new:");
562*3446Smrj 		print_bootadm_value(name, 0);
563*3446Smrj 	}
564*3446Smrj }
565*3446Smrj 
566*3446Smrj static void
567*3446Smrj set_var(char *name, char *val, eplist_t *list)
568*3446Smrj {
569*3446Smrj 	benv_ent_t *p;
570*3446Smrj 
571*3446Smrj 	if ((strcmp(name, "boot-file") == 0) ||
572*3446Smrj 	    (strcmp(name, "boot-args") == 0) ||
573*3446Smrj 	    (strcmp(name, "console") == 0)) {
574*3446Smrj 		set_bootadm_var(name, val);
575*3446Smrj 		return;
576*3446Smrj 	}
577*3446Smrj 
578*3446Smrj 	if (verbose) {
579*3446Smrj 		(void) printf("old:");
580*3446Smrj 		print_var(name, list);
581*3446Smrj 	}
582*3446Smrj 
583*3446Smrj 	if ((p = get_var(name, list)) != NULL) {
584*3446Smrj 		free(p->val);
585*3446Smrj 		p->val = strdup(val);
586*3446Smrj 	} else
587*3446Smrj 		add_bent(list, NULL, "setprop", name, val);
588*3446Smrj 
589*3446Smrj 	if (verbose) {
590*3446Smrj 		(void) printf("new:");
591*3446Smrj 		print_var(name, list);
592*3446Smrj 	}
593*3446Smrj }
594*3446Smrj 
595*3446Smrj /*
596*3446Smrj  *  Modified to return 1 if value modified or 0 if no modification
597*3446Smrj  *  was necessary.  This allows us to implement non super-user
598*3446Smrj  *  look up of variables by name without the user being yelled at for
599*3446Smrj  *  trying to modify the bootenv.rc file.
600*3446Smrj  */
601*3446Smrj static int
602*3446Smrj proc_var(char *name, eplist_t *list)
603*3446Smrj {
604*3446Smrj 	register char *val;
605*3446Smrj 
606*3446Smrj 	if ((val = strchr(name, '=')) == NULL) {
607*3446Smrj 		print_var(name, list);
608*3446Smrj 		return (0);
609*3446Smrj 	} else {
610*3446Smrj 		*val++ = '\0';
611*3446Smrj 		set_var(name, val, list);
612*3446Smrj 		return (1);
613*3446Smrj 	}
614*3446Smrj }
615*3446Smrj 
616*3446Smrj static void
617*3446Smrj init_benv(benv_des_t *bd, char *file)
618*3446Smrj {
619*3446Smrj 	get_kbenv();
620*3446Smrj 
621*3446Smrj 	if (test)
622*3446Smrj 		boottree = "/tmp";
623*3446Smrj 	else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL)
624*3446Smrj 		boottree = strcats("/boot", NULL);
625*3446Smrj 
626*3446Smrj 	if (file != NULL)
627*3446Smrj 		bd->name = file;
628*3446Smrj 	else
629*3446Smrj 		bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL);
630*3446Smrj }
631*3446Smrj 
632*3446Smrj static void
633*3446Smrj map_benv(benv_des_t *bd)
634*3446Smrj {
635*3446Smrj 	if ((bd->fd = open(bd->name, O_RDONLY)) == -1)
636*3446Smrj 		if (errno == ENOENT)
637*3446Smrj 			return;
638*3446Smrj 		else
639*3446Smrj 			exit(_error(PERROR, "cannot open %s", bd->name));
640*3446Smrj 
641*3446Smrj 	if ((bd->len = (size_t)lseek(bd->fd, 0, SEEK_END)) == 0) {
642*3446Smrj 		if (close(bd->fd) == -1)
643*3446Smrj 			exit(_error(PERROR, "close error on %s", bd->name));
644*3446Smrj 		return;
645*3446Smrj 	}
646*3446Smrj 
647*3446Smrj 	(void) lseek(bd->fd, 0, SEEK_SET);
648*3446Smrj 
649*3446Smrj 	if ((bd->adr = mmap((caddr_t)0, bd->len, (PROT_READ | PROT_WRITE),
650*3446Smrj 	    MAP_PRIVATE, bd->fd, 0)) == MAP_FAILED)
651*3446Smrj 		exit(_error(PERROR, "cannot map %s", bd->name));
652*3446Smrj }
653*3446Smrj 
654*3446Smrj static void
655*3446Smrj unmap_benv(benv_des_t *bd)
656*3446Smrj {
657*3446Smrj 	if (munmap(bd->adr, bd->len) == -1)
658*3446Smrj 		exit(_error(PERROR, "unmap error on %s", bd->name));
659*3446Smrj 
660*3446Smrj 	if (close(bd->fd) == -1)
661*3446Smrj 		exit(_error(PERROR, "close error on %s", bd->name));
662*3446Smrj }
663*3446Smrj 
664*3446Smrj #define	NL	'\n'
665*3446Smrj #define	COMM	'#'
666*3446Smrj 
667*3446Smrj /*
668*3446Smrj  * Add a comment block to the benv list.
669*3446Smrj  */
670*3446Smrj static void
671*3446Smrj add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line)
672*3446Smrj {
673*3446Smrj 	int nl, lines;
674*3446Smrj 	char *p;
675*3446Smrj 
676*3446Smrj 	nl = 0;
677*3446Smrj 	for (p = base, lines = 0; p < last; p++) {
678*3446Smrj 		if (*p == NL) {
679*3446Smrj 			nl++;
680*3446Smrj 			lines++;
681*3446Smrj 		} else if (nl) {
682*3446Smrj 			if (*p != COMM)
683*3446Smrj 				break;
684*3446Smrj 			nl = 0;
685*3446Smrj 		}
686*3446Smrj 	}
687*3446Smrj 	*(p - 1) = NULL;
688*3446Smrj 	add_bent(bd->elist, base, NULL, NULL, NULL);
689*3446Smrj 	*next = p;
690*3446Smrj 	*line += lines;
691*3446Smrj }
692*3446Smrj 
693*3446Smrj /*
694*3446Smrj  * Parse out an operator (setprop) from the boot environment
695*3446Smrj  */
696*3446Smrj static char *
697*3446Smrj parse_cmd(benv_des_t *bd, char **next, int *line)
698*3446Smrj {
699*3446Smrj 	char *strbegin;
700*3446Smrj 	char *badeof = "unexpected EOF in %s line %d";
701*3446Smrj 	char *syntax = "syntax error in %s line %d";
702*3446Smrj 	char *c = *next;
703*3446Smrj 
704*3446Smrj 	/*
705*3446Smrj 	 * Skip spaces or tabs. New lines increase the line count.
706*3446Smrj 	 */
707*3446Smrj 	while (isspace(*c)) {
708*3446Smrj 		if (*c++ == '\n')
709*3446Smrj 			(*line)++;
710*3446Smrj 	}
711*3446Smrj 
712*3446Smrj 	/*
713*3446Smrj 	 * Check for a the setprop command.  Currently that's all we
714*3446Smrj 	 * seem to support.
715*3446Smrj 	 *
716*3446Smrj 	 * XXX need support for setbinprop?
717*3446Smrj 	 */
718*3446Smrj 
719*3446Smrj 	/*
720*3446Smrj 	 * Check first for end of file.  Finding one now would be okay.
721*3446Smrj 	 * We should also bail if we are at the start of a comment.
722*3446Smrj 	 */
723*3446Smrj 	if (*c == '\0' || *c == COMM) {
724*3446Smrj 		*next = c;
725*3446Smrj 		return (NULL);
726*3446Smrj 	}
727*3446Smrj 
728*3446Smrj 	strbegin = c;
729*3446Smrj 	while (*c && !isspace(*c))
730*3446Smrj 		c++;
731*3446Smrj 
732*3446Smrj 	/*
733*3446Smrj 	 * Check again for end of file.  Finding one now would NOT be okay.
734*3446Smrj 	 */
735*3446Smrj 	if (*c == '\0') {
736*3446Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
737*3446Smrj 	}
738*3446Smrj 
739*3446Smrj 	*c++ = '\0';
740*3446Smrj 	*next = c;
741*3446Smrj 
742*3446Smrj 	/*
743*3446Smrj 	 * Last check is to make sure the command is a setprop!
744*3446Smrj 	 */
745*3446Smrj 	if (strcmp(strbegin, "setprop") != 0) {
746*3446Smrj 		exit(_error(NO_PERROR, syntax, bd->name, *line));
747*3446Smrj 		/* NOTREACHED */
748*3446Smrj 	}
749*3446Smrj 	return (strbegin);
750*3446Smrj }
751*3446Smrj 
752*3446Smrj /*
753*3446Smrj  * Parse out the name (LHS) of a setprop from the boot environment
754*3446Smrj  */
755*3446Smrj static char *
756*3446Smrj parse_name(benv_des_t *bd, char **next, int *line)
757*3446Smrj {
758*3446Smrj 	char *strbegin;
759*3446Smrj 	char *badeof = "unexpected EOF in %s line %d";
760*3446Smrj 	char *syntax = "syntax error in %s line %d";
761*3446Smrj 	char *c = *next;
762*3446Smrj 
763*3446Smrj 	/*
764*3446Smrj 	 * Skip spaces or tabs. No tolerance for new lines now.
765*3446Smrj 	 */
766*3446Smrj 	while (isspace(*c)) {
767*3446Smrj 		if (*c++ == '\n')
768*3446Smrj 			exit(_error(NO_PERROR, syntax, bd->name, *line));
769*3446Smrj 	}
770*3446Smrj 
771*3446Smrj 	/*
772*3446Smrj 	 * Grab a name for the property to set.
773*3446Smrj 	 */
774*3446Smrj 
775*3446Smrj 	/*
776*3446Smrj 	 * Check first for end of file.  Finding one now would NOT be okay.
777*3446Smrj 	 */
778*3446Smrj 	if (*c == '\0') {
779*3446Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
780*3446Smrj 	}
781*3446Smrj 
782*3446Smrj 	strbegin = c;
783*3446Smrj 	while (*c && !isspace(*c))
784*3446Smrj 		c++;
785*3446Smrj 
786*3446Smrj 	/*
787*3446Smrj 	 * At this point in parsing we have 'setprop name'.  What follows
788*3446Smrj 	 * is a newline, other whitespace, or EOF.  Most of the time we
789*3446Smrj 	 * want to replace a white space character with a NULL to terminate
790*3446Smrj 	 * the name, and then continue on processing.  A newline here provides
791*3446Smrj 	 * the most grief.  If we just replace it with a null we'll
792*3446Smrj 	 * potentially get the setprop on the next line as the value of this
793*3446Smrj 	 * setprop! So, if the last thing we see is a newline we'll have to
794*3446Smrj 	 * dup the string.
795*3446Smrj 	 */
796*3446Smrj 	if (isspace(*c)) {
797*3446Smrj 		if (*c == '\n') {
798*3446Smrj 			*c = '\0';
799*3446Smrj 			strbegin = strdup(strbegin);
800*3446Smrj 			*c = '\n';
801*3446Smrj 		} else {
802*3446Smrj 			*c++ = '\0';
803*3446Smrj 		}
804*3446Smrj 	}
805*3446Smrj 
806*3446Smrj 	*next = c;
807*3446Smrj 	return (strbegin);
808*3446Smrj }
809*3446Smrj 
810*3446Smrj /*
811*3446Smrj  * Parse out the value (RHS) of a setprop line from the boot environment
812*3446Smrj  */
813*3446Smrj static char *
814*3446Smrj parse_value(benv_des_t *bd, char **next, int *line)
815*3446Smrj {
816*3446Smrj 	char *strbegin;
817*3446Smrj 	char *badeof = "unexpected EOF in %s line %d";
818*3446Smrj 	char *result;
819*3446Smrj 	char *c = *next;
820*3446Smrj 	char quote;
821*3446Smrj 
822*3446Smrj 	/*
823*3446Smrj 	 * Skip spaces or tabs. A newline here would indicate a
824*3446Smrj 	 * NULL property value.
825*3446Smrj 	 */
826*3446Smrj 	while (isspace(*c)) {
827*3446Smrj 		if (*c++ == '\n') {
828*3446Smrj 			(*line)++;
829*3446Smrj 			*next = c;
830*3446Smrj 			return (NULL);
831*3446Smrj 		}
832*3446Smrj 	}
833*3446Smrj 
834*3446Smrj 	/*
835*3446Smrj 	 * Grab the value of the property to set.
836*3446Smrj 	 */
837*3446Smrj 
838*3446Smrj 	/*
839*3446Smrj 	 * Check first for end of file.  Finding one now would
840*3446Smrj 	 * also indicate a NULL property.
841*3446Smrj 	 */
842*3446Smrj 	if (*c == '\0') {
843*3446Smrj 		*next = c;
844*3446Smrj 		return (NULL);
845*3446Smrj 	}
846*3446Smrj 
847*3446Smrj 	/*
848*3446Smrj 	 * Value may be quoted, in which case we assume the end of the value
849*3446Smrj 	 * comes with a closing quote.
850*3446Smrj 	 *
851*3446Smrj 	 * We also allow escaped quote characters inside the quoted value.
852*3446Smrj 	 *
853*3446Smrj 	 * For obvious reasons we do not attempt to parse variable references.
854*3446Smrj 	 */
855*3446Smrj 	if (*c == '"' || *c == '\'') {
856*3446Smrj 		quote = *c;
857*3446Smrj 		c++;
858*3446Smrj 		strbegin = c;
859*3446Smrj 		result = c;
860*3446Smrj 		while (*c != quote) {
861*3446Smrj 			if (*c == '\\') {
862*3446Smrj 				c++;
863*3446Smrj 			}
864*3446Smrj 			if (*c == '\0' || *c == '\n') {
865*3446Smrj 				break;
866*3446Smrj 			}
867*3446Smrj 			*result++ = *c++;
868*3446Smrj 		}
869*3446Smrj 
870*3446Smrj 		/*
871*3446Smrj 		 *  Throw fatal exception if no end quote found.
872*3446Smrj 		 */
873*3446Smrj 		if (*c != quote) {
874*3446Smrj 			exit(_error(NO_PERROR, badeof, bd->name, *line));
875*3446Smrj 		}
876*3446Smrj 
877*3446Smrj 		*result = '\0';		/* Terminate the result */
878*3446Smrj 		c++;			/* and step past the close quote */
879*3446Smrj 	} else {
880*3446Smrj 		strbegin = c;
881*3446Smrj 		while (*c && !isspace(*c))
882*3446Smrj 			c++;
883*3446Smrj 	}
884*3446Smrj 
885*3446Smrj 	/*
886*3446Smrj 	 * Check again for end of file.  Finding one now is okay.
887*3446Smrj 	 */
888*3446Smrj 	if (*c == '\0') {
889*3446Smrj 		*next = c;
890*3446Smrj 		return (strbegin);
891*3446Smrj 	}
892*3446Smrj 
893*3446Smrj 	*c++ = '\0';
894*3446Smrj 	*next = c;
895*3446Smrj 	return (strbegin);
896*3446Smrj }
897*3446Smrj 
898*3446Smrj /*
899*3446Smrj  * Add a command to the benv list.
900*3446Smrj  */
901*3446Smrj static void
902*3446Smrj add_cmd(benv_des_t *bd, char *last, char **next, int *line)
903*3446Smrj {
904*3446Smrj 	char *cmd, *name, *val;
905*3446Smrj 
906*3446Smrj 	while (*next <= last && **next != COMM) {
907*3446Smrj 		if ((cmd = parse_cmd(bd, next, line)) == NULL)
908*3446Smrj 			break;
909*3446Smrj 		name = parse_name(bd, next, line);
910*3446Smrj 		val = parse_value(bd, next, line);
911*3446Smrj 		add_bent(bd->elist, NULL, cmd, name, val);
912*3446Smrj 		(*line)++;
913*3446Smrj 	};
914*3446Smrj }
915*3446Smrj 
916*3446Smrj /*
917*3446Smrj  * Parse the benv (bootenv.rc) file and break it into a benv
918*3446Smrj  * list.  List entries may be comment blocks or commands.
919*3446Smrj  */
920*3446Smrj static void
921*3446Smrj parse_benv(benv_des_t *bd)
922*3446Smrj {
923*3446Smrj 	int line;
924*3446Smrj 	char *pbase, *pend;
925*3446Smrj 	char *tok, *tnext;
926*3446Smrj 
927*3446Smrj 	line = 1;
928*3446Smrj 	pbase = (char *)bd->adr;
929*3446Smrj 	pend = pbase + bd->len;
930*3446Smrj 
931*3446Smrj 	for (tok = tnext = pbase; tnext < pend; tok = tnext)
932*3446Smrj 		if (*tok == COMM)
933*3446Smrj 			add_comm(bd, tok, pend, &tnext, &line);
934*3446Smrj 		else
935*3446Smrj 			add_cmd(bd, pend, &tnext, &line);
936*3446Smrj }
937*3446Smrj 
938*3446Smrj static void
939*3446Smrj write_benv(benv_des_t *bd)
940*3446Smrj {
941*3446Smrj 	FILE *fp;
942*3446Smrj 	eplist_t *list, *e;
943*3446Smrj 	benv_ent_t *bent;
944*3446Smrj 	char *name;
945*3446Smrj 
946*3446Smrj 	list = bd->elist;
947*3446Smrj 
948*3446Smrj 	if (list->next == list)
949*3446Smrj 		return;
950*3446Smrj 
951*3446Smrj 	if ((fp = fopen(bd->name, "w")) == NULL)
952*3446Smrj 		exit(_error(PERROR, "cannot open %s", bd->name));
953*3446Smrj 
954*3446Smrj 	for (e = list->next; e != list; e = e->next) {
955*3446Smrj 		bent = (benv_ent_t *)e->item;
956*3446Smrj 		name = bent->name;
957*3446Smrj 		if (name) {
958*3446Smrj 			if (bent->val) {
959*3446Smrj 				(void) fprintf(fp, "%s %s ",
960*3446Smrj 					bent->cmd, bent->name);
961*3446Smrj 				put_quoted(fp, bent->val);
962*3446Smrj 				(void) fprintf(fp, "\n");
963*3446Smrj 			} else {
964*3446Smrj 				(void) fprintf(fp, "%s %s\n",
965*3446Smrj 					bent->cmd, bent->name);
966*3446Smrj 			}
967*3446Smrj 		} else {
968*3446Smrj 			(void) fprintf(fp, "%s\n", bent->cmd);
969*3446Smrj 		}
970*3446Smrj 	}
971*3446Smrj 
972*3446Smrj 	(void) fclose(fp);
973*3446Smrj }
974*3446Smrj 
975*3446Smrj static char *
976*3446Smrj get_line(void)
977*3446Smrj {
978*3446Smrj 	int c;
979*3446Smrj 	char *nl;
980*3446Smrj 	static char line[256];
981*3446Smrj 
982*3446Smrj 	if (fgets(line, sizeof (line), stdin) != NULL) {
983*3446Smrj 		/*
984*3446Smrj 		 * Remove newline if present,
985*3446Smrj 		 * otherwise discard rest of line.
986*3446Smrj 		 */
987*3446Smrj 		if (nl = strchr(line, '\n'))
988*3446Smrj 			*nl = 0;
989*3446Smrj 		else
990*3446Smrj 			while ((c = getchar()) != '\n' && c != EOF)
991*3446Smrj 				;
992*3446Smrj 		return (line);
993*3446Smrj 	} else
994*3446Smrj 		return (NULL);
995*3446Smrj }
996*3446Smrj 
997*3446Smrj int
998*3446Smrj main(int argc, char **argv)
999*3446Smrj {
1000*3446Smrj 	int c;
1001*3446Smrj 	int updates = 0;
1002*3446Smrj 	char *usage = "Usage: %s [-v] [-f prom-device]"
1003*3446Smrj 	    " [variable[=value] ...]";
1004*3446Smrj 	eplist_t *elist;
1005*3446Smrj 	benv_des_t *bd;
1006*3446Smrj 	char *file = NULL;
1007*3446Smrj 
1008*3446Smrj 	setprogname(argv[0]);
1009*3446Smrj 
1010*3446Smrj 	while ((c = getopt(argc, argv, "f:Itv")) != -1)
1011*3446Smrj 		switch (c) {
1012*3446Smrj 		case 'v':
1013*3446Smrj 			verbose++;
1014*3446Smrj 			break;
1015*3446Smrj 		case 'f':
1016*3446Smrj 			file = optarg;
1017*3446Smrj 			break;
1018*3446Smrj 		case 't':
1019*3446Smrj 			test++;
1020*3446Smrj 			break;
1021*3446Smrj 		default:
1022*3446Smrj 			exit(_error(NO_PERROR, usage, argv[0]));
1023*3446Smrj 		}
1024*3446Smrj 
1025*3446Smrj 	(void) uname(&uts_buf);
1026*3446Smrj 	bd = new_bd();
1027*3446Smrj 	init_benv(bd, file);
1028*3446Smrj 
1029*3446Smrj 	map_benv(bd);
1030*3446Smrj 	if (bd->len) {
1031*3446Smrj 		parse_benv(bd);
1032*3446Smrj 		unmap_benv(bd);
1033*3446Smrj 	}
1034*3446Smrj 
1035*3446Smrj 	elist = bd->elist;
1036*3446Smrj 
1037*3446Smrj 	if (optind >= argc) {
1038*3446Smrj 		print_vars(elist);
1039*3446Smrj 		return (0);
1040*3446Smrj 	} else
1041*3446Smrj 		while (optind < argc) {
1042*3446Smrj 			/*
1043*3446Smrj 			 * If "-" specified, read variables from stdin;
1044*3446Smrj 			 * otherwise, process each argument as a variable
1045*3446Smrj 			 * print or set request.
1046*3446Smrj 			 */
1047*3446Smrj 			if (strcmp(argv[optind], "-") == 0) {
1048*3446Smrj 				char *line;
1049*3446Smrj 
1050*3446Smrj 				while ((line = get_line()) != NULL)
1051*3446Smrj 					updates += proc_var(line, elist);
1052*3446Smrj 				clearerr(stdin);
1053*3446Smrj 			} else
1054*3446Smrj 				updates += proc_var(argv[optind], elist);
1055*3446Smrj 
1056*3446Smrj 			optind++;
1057*3446Smrj 		}
1058*3446Smrj 
1059*3446Smrj 	/*
1060*3446Smrj 	 * don't write benv if we are processing delayed writes since
1061*3446Smrj 	 * it is likely that the delayed writes changes bootenv.rc anyway...
1062*3446Smrj 	 */
1063*3446Smrj 	if (updates)
1064*3446Smrj 		write_benv(bd);
1065*3446Smrj 	close_kbenv();
1066*3446Smrj 
1067*3446Smrj 	return (0);
1068*3446Smrj }
1069