xref: /titanic_52/usr/src/cmd/eeprom/i386/benv.c (revision f64ca10231919db05e806441ccd6186ea9c6e734)
1ae115bc7Smrj /*
2ae115bc7Smrj  * CDDL HEADER START
3ae115bc7Smrj  *
4ae115bc7Smrj  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
7ae115bc7Smrj  *
8ae115bc7Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj  * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj  * See the License for the specific language governing permissions
11ae115bc7Smrj  * and limitations under the License.
12ae115bc7Smrj  *
13ae115bc7Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj  * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj  *
19ae115bc7Smrj  * CDDL HEADER END
20ae115bc7Smrj  */
2123a1cceaSRoger A. Faulkner 
22ae115bc7Smrj /*
23*f64ca102SToomas Soome  * Copyright 2016 Toomas Soome <tsoome@me.com>
247d59361aSGangadhar Mylapuram  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
25ae115bc7Smrj  */
26ae115bc7Smrj 
27ae115bc7Smrj #include "benv.h"
28ae115bc7Smrj #include <ctype.h>
29ae115bc7Smrj #include <stdarg.h>
30ae115bc7Smrj #include <sys/mman.h>
31ae115bc7Smrj #include <unistd.h>
32ae115bc7Smrj #include <signal.h>
33ae115bc7Smrj #include <sys/wait.h>
34ae115bc7Smrj 
35ae115bc7Smrj /*
36ae115bc7Smrj  * Usage:  % eeprom [-v] [-f prom_dev] [-]
37ae115bc7Smrj  *	   % eeprom [-v] [-f prom_dev] field[=value] ...
38ae115bc7Smrj  */
39ae115bc7Smrj 
40ae115bc7Smrj extern void get_kbenv(void);
41ae115bc7Smrj extern void close_kbenv(void);
42ae115bc7Smrj extern caddr_t get_propval(char *name, char *node);
4323a1cceaSRoger A. Faulkner extern void setpname(char *prog);
443b133becSGangadhar Mylapuram extern char *getbootcmd(void);
45ae115bc7Smrj 
46ae115bc7Smrj char *boottree;
47ae115bc7Smrj struct utsname uts_buf;
48ae115bc7Smrj 
49ae115bc7Smrj static int test;
50ae115bc7Smrj int verbose;
51ae115bc7Smrj 
52ae115bc7Smrj /*
53ae115bc7Smrj  * Concatenate a NULL terminated list of strings into
54ae115bc7Smrj  * a single string.
55ae115bc7Smrj  */
56ae115bc7Smrj char *
57ae115bc7Smrj strcats(char *s, ...)
58ae115bc7Smrj {
59ae115bc7Smrj 	char *cp, *ret;
60ae115bc7Smrj 	size_t len;
61ae115bc7Smrj 	va_list ap;
62ae115bc7Smrj 
63ae115bc7Smrj 	va_start(ap, s);
64ae115bc7Smrj 	for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) {
65ae115bc7Smrj 		if (ret == NULL) {
66ae115bc7Smrj 			ret = strdup(s);
67ae115bc7Smrj 			len = strlen(ret) + 1;
68ae115bc7Smrj 		} else {
69ae115bc7Smrj 			len += strlen(cp);
70ae115bc7Smrj 			ret = realloc(ret, len);
71ae115bc7Smrj 			(void) strcat(ret, cp);
72ae115bc7Smrj 		}
73ae115bc7Smrj 	}
74ae115bc7Smrj 	va_end(ap);
75ae115bc7Smrj 
76ae115bc7Smrj 	return (ret);
77ae115bc7Smrj }
78ae115bc7Smrj 
79ae115bc7Smrj eplist_t *
80ae115bc7Smrj new_list(void)
81ae115bc7Smrj {
82ae115bc7Smrj 	eplist_t *list;
83ae115bc7Smrj 
84ae115bc7Smrj 	list = (eplist_t *)malloc(sizeof (eplist_t));
85ae115bc7Smrj 	(void) memset(list, 0, sizeof (eplist_t));
86ae115bc7Smrj 
87ae115bc7Smrj 	list->next = list;
88ae115bc7Smrj 	list->prev = list;
89ae115bc7Smrj 	list->item = NULL;
90ae115bc7Smrj 
91ae115bc7Smrj 	return (list);
92ae115bc7Smrj }
93ae115bc7Smrj 
94ae115bc7Smrj void
95ae115bc7Smrj add_item(void *item, eplist_t *list)
96ae115bc7Smrj {
97ae115bc7Smrj 	eplist_t *entry;
98ae115bc7Smrj 
99ae115bc7Smrj 	entry = (eplist_t *)malloc(sizeof (eplist_t));
100ae115bc7Smrj 	(void) memset(entry, 0, sizeof (eplist_t));
101ae115bc7Smrj 	entry->item = item;
102ae115bc7Smrj 
103ae115bc7Smrj 	entry->next = list;
104ae115bc7Smrj 	entry->prev = list->prev;
105ae115bc7Smrj 	list->prev->next = entry;
106ae115bc7Smrj 	list->prev = entry;
107ae115bc7Smrj }
108ae115bc7Smrj 
109ae115bc7Smrj typedef struct benv_ent {
110ae115bc7Smrj 	char *cmd;
111ae115bc7Smrj 	char *name;
112ae115bc7Smrj 	char *val;
113ae115bc7Smrj } benv_ent_t;
114ae115bc7Smrj 
115ae115bc7Smrj typedef struct benv_des {
116ae115bc7Smrj 	char *name;
117ae115bc7Smrj 	int fd;
118ae115bc7Smrj 	caddr_t adr;
119ae115bc7Smrj 	size_t len;
120ae115bc7Smrj 	eplist_t *elist;
121ae115bc7Smrj } benv_des_t;
122ae115bc7Smrj 
123ae115bc7Smrj static benv_des_t *
124ae115bc7Smrj new_bd(void)
125ae115bc7Smrj {
126ae115bc7Smrj 
127ae115bc7Smrj 	benv_des_t *bd;
128ae115bc7Smrj 
129ae115bc7Smrj 	bd = (benv_des_t *)malloc(sizeof (benv_des_t));
130ae115bc7Smrj 	(void) memset(bd, 0, sizeof (benv_des_t));
131ae115bc7Smrj 
132ae115bc7Smrj 	bd->elist = new_list();
133ae115bc7Smrj 
134ae115bc7Smrj 	return (bd);
135ae115bc7Smrj }
136ae115bc7Smrj 
137ae115bc7Smrj /*
138ae115bc7Smrj  * Create a new entry.  Comment entries have NULL names.
139ae115bc7Smrj  */
140ae115bc7Smrj static benv_ent_t *
141ae115bc7Smrj new_bent(char *comm, char *cmd, char *name, char *val)
142ae115bc7Smrj {
143ae115bc7Smrj 	benv_ent_t *bent;
144ae115bc7Smrj 
145ae115bc7Smrj 	bent = (benv_ent_t *)malloc(sizeof (benv_ent_t));
146ae115bc7Smrj 	(void) memset(bent, 0, sizeof (benv_ent_t));
147ae115bc7Smrj 
148ae115bc7Smrj 	if (comm) {
149ae115bc7Smrj 		bent->cmd = strdup(comm);
150ae115bc7Smrj 		comm = NULL;
151ae115bc7Smrj 	} else {
152ae115bc7Smrj 		bent->cmd = strdup(cmd);
153ae115bc7Smrj 		bent->name = strdup(name);
154ae115bc7Smrj 		if (val)
155ae115bc7Smrj 			bent->val = strdup(val);
156ae115bc7Smrj 	}
157ae115bc7Smrj 
158ae115bc7Smrj 	return (bent);
159ae115bc7Smrj }
160ae115bc7Smrj 
161ae115bc7Smrj /*
162ae115bc7Smrj  * Add a new entry to the benv entry list.  Entries can be
163ae115bc7Smrj  * comments or commands.
164ae115bc7Smrj  */
165ae115bc7Smrj static void
166ae115bc7Smrj add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val)
167ae115bc7Smrj {
168ae115bc7Smrj 	benv_ent_t *bent;
169ae115bc7Smrj 
170ae115bc7Smrj 	bent = new_bent(comm, cmd, name, val);
171ae115bc7Smrj 	add_item((void *)bent, list);
172ae115bc7Smrj }
173ae115bc7Smrj 
174ae115bc7Smrj static benv_ent_t *
175ae115bc7Smrj get_var(char *name, eplist_t *list)
176ae115bc7Smrj {
177ae115bc7Smrj 	eplist_t *e;
178ae115bc7Smrj 	benv_ent_t *p;
179ae115bc7Smrj 
180ae115bc7Smrj 	for (e = list->next; e != list; e = e->next) {
181ae115bc7Smrj 		p = (benv_ent_t *)e->item;
182ae115bc7Smrj 		if (p->name != NULL && strcmp(p->name, name) == 0)
183ae115bc7Smrj 			return (p);
184ae115bc7Smrj 	}
185ae115bc7Smrj 
186ae115bc7Smrj 	return (NULL);
187ae115bc7Smrj }
188ae115bc7Smrj 
189ae115bc7Smrj static void
190ae115bc7Smrj print_var(char *name, eplist_t *list)
191ae115bc7Smrj {
192ae115bc7Smrj 	benv_ent_t *p;
1937d59361aSGangadhar Mylapuram 	char *bootcmd;
194ae115bc7Smrj 
195*f64ca102SToomas Soome 	if (strcmp(name, "bootcmd") == 0) {
1967d59361aSGangadhar Mylapuram 		bootcmd = getbootcmd();
1977d59361aSGangadhar Mylapuram 		(void) printf("%s=%s\n", name, bootcmd ? bootcmd : "");
1987d59361aSGangadhar Mylapuram 	} else if ((p = get_var(name, list)) == NULL) {
199ae115bc7Smrj 		(void) printf("%s: data not available.\n", name);
2007d59361aSGangadhar Mylapuram 	} else {
201ae115bc7Smrj 		(void) printf("%s=%s\n", name, p->val ? p->val : "");
202ae115bc7Smrj 	}
2037d59361aSGangadhar Mylapuram }
204ae115bc7Smrj 
205ae115bc7Smrj static void
206ae115bc7Smrj print_vars(eplist_t *list)
207ae115bc7Smrj {
208ae115bc7Smrj 	eplist_t *e;
209ae115bc7Smrj 	benv_ent_t *p;
210ae115bc7Smrj 
211ae115bc7Smrj 	for (e = list->next; e != list; e = e->next) {
212ae115bc7Smrj 		p = (benv_ent_t *)e->item;
213ae115bc7Smrj 		if (p->name != NULL) {
214ae115bc7Smrj 			(void) printf("%s=%s\n", p->name, p->val ? p->val : "");
215ae115bc7Smrj 		}
216ae115bc7Smrj 	}
217ae115bc7Smrj }
218ae115bc7Smrj 
219ae115bc7Smrj /*
220ae115bc7Smrj  * Write a string to a file, quoted appropriately.  We use single
221ae115bc7Smrj  * quotes to prevent any variable expansion.  Of course, we backslash-quote
222ae115bc7Smrj  * any single quotes or backslashes.
223ae115bc7Smrj  */
224ae115bc7Smrj static void
225ae115bc7Smrj put_quoted(FILE *fp, char *val)
226ae115bc7Smrj {
227ae115bc7Smrj 	(void) putc('\'', fp);
228ae115bc7Smrj 	while (*val) {
229ae115bc7Smrj 		switch (*val) {
230ae115bc7Smrj 		case '\'':
231ae115bc7Smrj 		case '\\':
232ae115bc7Smrj 			(void) putc('\\', fp);
233ae115bc7Smrj 			/* FALLTHROUGH */
234ae115bc7Smrj 		default:
235ae115bc7Smrj 			(void) putc(*val, fp);
236ae115bc7Smrj 			break;
237ae115bc7Smrj 		}
238ae115bc7Smrj 		val++;
239ae115bc7Smrj 	}
240ae115bc7Smrj 	(void) putc('\'', fp);
241ae115bc7Smrj }
242ae115bc7Smrj 
243455710d3Srscott /*
244455710d3Srscott  * Returns 1 if bootenv.rc was modified, 0 otherwise.
245455710d3Srscott  */
246455710d3Srscott static int
247ae115bc7Smrj set_var(char *name, char *val, eplist_t *list)
248ae115bc7Smrj {
249ae115bc7Smrj 	benv_ent_t *p;
250ae115bc7Smrj 
2517d59361aSGangadhar Mylapuram 	if (strcmp(name, "bootcmd") == 0)
2527d59361aSGangadhar Mylapuram 		return (0);
2537d59361aSGangadhar Mylapuram 
254ae115bc7Smrj 	if (verbose) {
255ae115bc7Smrj 		(void) printf("old:");
256ae115bc7Smrj 		print_var(name, list);
257ae115bc7Smrj 	}
258ae115bc7Smrj 
259ae115bc7Smrj 	if ((p = get_var(name, list)) != NULL) {
260ae115bc7Smrj 		free(p->val);
261ae115bc7Smrj 		p->val = strdup(val);
262ae115bc7Smrj 	} else
263ae115bc7Smrj 		add_bent(list, NULL, "setprop", name, val);
264ae115bc7Smrj 
265ae115bc7Smrj 	if (verbose) {
266ae115bc7Smrj 		(void) printf("new:");
267ae115bc7Smrj 		print_var(name, list);
268ae115bc7Smrj 	}
269455710d3Srscott 	return (1);
270ae115bc7Smrj }
271ae115bc7Smrj 
272ae115bc7Smrj /*
273455710d3Srscott  * Returns 1 if bootenv.rc is modified or 0 if no modification was
274455710d3Srscott  * necessary.  This allows us to implement non super-user look-up of
275455710d3Srscott  * variables by name without the user being yelled at for trying to
276455710d3Srscott  * modify the bootenv.rc file.
277ae115bc7Smrj  */
278ae115bc7Smrj static int
279ae115bc7Smrj proc_var(char *name, eplist_t *list)
280ae115bc7Smrj {
281ae115bc7Smrj 	register char *val;
282ae115bc7Smrj 
283ae115bc7Smrj 	if ((val = strchr(name, '=')) == NULL) {
284ae115bc7Smrj 		print_var(name, list);
285ae115bc7Smrj 		return (0);
286ae115bc7Smrj 	} else {
287ae115bc7Smrj 		*val++ = '\0';
288455710d3Srscott 		return (set_var(name, val, list));
289ae115bc7Smrj 	}
290ae115bc7Smrj }
291ae115bc7Smrj 
292ae115bc7Smrj static void
293ae115bc7Smrj init_benv(benv_des_t *bd, char *file)
294ae115bc7Smrj {
295ae115bc7Smrj 	get_kbenv();
296ae115bc7Smrj 
297ae115bc7Smrj 	if (test)
298ae115bc7Smrj 		boottree = "/tmp";
299ae115bc7Smrj 	else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL)
300ae115bc7Smrj 		boottree = strcats("/boot", NULL);
301ae115bc7Smrj 
302ae115bc7Smrj 	if (file != NULL)
303ae115bc7Smrj 		bd->name = file;
304ae115bc7Smrj 	else
305ae115bc7Smrj 		bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL);
306ae115bc7Smrj }
307ae115bc7Smrj 
308ae115bc7Smrj static void
309ae115bc7Smrj map_benv(benv_des_t *bd)
310ae115bc7Smrj {
311ae115bc7Smrj 	if ((bd->fd = open(bd->name, O_RDONLY)) == -1)
312ae115bc7Smrj 		if (errno == ENOENT)
313ae115bc7Smrj 			return;
314ae115bc7Smrj 		else
315ae115bc7Smrj 			exit(_error(PERROR, "cannot open %s", bd->name));
316ae115bc7Smrj 
317ae115bc7Smrj 	if ((bd->len = (size_t)lseek(bd->fd, 0, SEEK_END)) == 0) {
318ae115bc7Smrj 		if (close(bd->fd) == -1)
319ae115bc7Smrj 			exit(_error(PERROR, "close error on %s", bd->name));
320ae115bc7Smrj 		return;
321ae115bc7Smrj 	}
322ae115bc7Smrj 
323ae115bc7Smrj 	(void) lseek(bd->fd, 0, SEEK_SET);
324ae115bc7Smrj 
325ae115bc7Smrj 	if ((bd->adr = mmap((caddr_t)0, bd->len, (PROT_READ | PROT_WRITE),
326ae115bc7Smrj 	    MAP_PRIVATE, bd->fd, 0)) == MAP_FAILED)
327ae115bc7Smrj 		exit(_error(PERROR, "cannot map %s", bd->name));
328ae115bc7Smrj }
329ae115bc7Smrj 
330ae115bc7Smrj static void
331ae115bc7Smrj unmap_benv(benv_des_t *bd)
332ae115bc7Smrj {
333ae115bc7Smrj 	if (munmap(bd->adr, bd->len) == -1)
334ae115bc7Smrj 		exit(_error(PERROR, "unmap error on %s", bd->name));
335ae115bc7Smrj 
336ae115bc7Smrj 	if (close(bd->fd) == -1)
337ae115bc7Smrj 		exit(_error(PERROR, "close error on %s", bd->name));
338ae115bc7Smrj }
339ae115bc7Smrj 
340ae115bc7Smrj #define	NL	'\n'
341ae115bc7Smrj #define	COMM	'#'
342ae115bc7Smrj 
343ae115bc7Smrj /*
344ae115bc7Smrj  * Add a comment block to the benv list.
345ae115bc7Smrj  */
346ae115bc7Smrj static void
347ae115bc7Smrj add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line)
348ae115bc7Smrj {
349ae115bc7Smrj 	int nl, lines;
350ae115bc7Smrj 	char *p;
351ae115bc7Smrj 
352ae115bc7Smrj 	nl = 0;
353ae115bc7Smrj 	for (p = base, lines = 0; p < last; p++) {
354ae115bc7Smrj 		if (*p == NL) {
355ae115bc7Smrj 			nl++;
356ae115bc7Smrj 			lines++;
357ae115bc7Smrj 		} else if (nl) {
358ae115bc7Smrj 			if (*p != COMM)
359ae115bc7Smrj 				break;
360ae115bc7Smrj 			nl = 0;
361ae115bc7Smrj 		}
362ae115bc7Smrj 	}
363ae115bc7Smrj 	*(p - 1) = NULL;
364ae115bc7Smrj 	add_bent(bd->elist, base, NULL, NULL, NULL);
365ae115bc7Smrj 	*next = p;
366ae115bc7Smrj 	*line += lines;
367ae115bc7Smrj }
368ae115bc7Smrj 
369ae115bc7Smrj /*
370ae115bc7Smrj  * Parse out an operator (setprop) from the boot environment
371ae115bc7Smrj  */
372ae115bc7Smrj static char *
373ae115bc7Smrj parse_cmd(benv_des_t *bd, char **next, int *line)
374ae115bc7Smrj {
375ae115bc7Smrj 	char *strbegin;
376ae115bc7Smrj 	char *badeof = "unexpected EOF in %s line %d";
377ae115bc7Smrj 	char *syntax = "syntax error in %s line %d";
378ae115bc7Smrj 	char *c = *next;
379ae115bc7Smrj 
380ae115bc7Smrj 	/*
381ae115bc7Smrj 	 * Skip spaces or tabs. New lines increase the line count.
382ae115bc7Smrj 	 */
383ae115bc7Smrj 	while (isspace(*c)) {
384ae115bc7Smrj 		if (*c++ == '\n')
385ae115bc7Smrj 			(*line)++;
386ae115bc7Smrj 	}
387ae115bc7Smrj 
388ae115bc7Smrj 	/*
389ae115bc7Smrj 	 * Check for a the setprop command.  Currently that's all we
390ae115bc7Smrj 	 * seem to support.
391ae115bc7Smrj 	 *
392ae115bc7Smrj 	 * XXX need support for setbinprop?
393ae115bc7Smrj 	 */
394ae115bc7Smrj 
395ae115bc7Smrj 	/*
396ae115bc7Smrj 	 * Check first for end of file.  Finding one now would be okay.
397ae115bc7Smrj 	 * We should also bail if we are at the start of a comment.
398ae115bc7Smrj 	 */
399ae115bc7Smrj 	if (*c == '\0' || *c == COMM) {
400ae115bc7Smrj 		*next = c;
401ae115bc7Smrj 		return (NULL);
402ae115bc7Smrj 	}
403ae115bc7Smrj 
404ae115bc7Smrj 	strbegin = c;
405ae115bc7Smrj 	while (*c && !isspace(*c))
406ae115bc7Smrj 		c++;
407ae115bc7Smrj 
408ae115bc7Smrj 	/*
409ae115bc7Smrj 	 * Check again for end of file.  Finding one now would NOT be okay.
410ae115bc7Smrj 	 */
411ae115bc7Smrj 	if (*c == '\0') {
412ae115bc7Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
413ae115bc7Smrj 	}
414ae115bc7Smrj 
415ae115bc7Smrj 	*c++ = '\0';
416ae115bc7Smrj 	*next = c;
417ae115bc7Smrj 
418ae115bc7Smrj 	/*
419ae115bc7Smrj 	 * Last check is to make sure the command is a setprop!
420ae115bc7Smrj 	 */
421ae115bc7Smrj 	if (strcmp(strbegin, "setprop") != 0) {
422ae115bc7Smrj 		exit(_error(NO_PERROR, syntax, bd->name, *line));
423ae115bc7Smrj 		/* NOTREACHED */
424ae115bc7Smrj 	}
425ae115bc7Smrj 	return (strbegin);
426ae115bc7Smrj }
427ae115bc7Smrj 
428ae115bc7Smrj /*
429ae115bc7Smrj  * Parse out the name (LHS) of a setprop from the boot environment
430ae115bc7Smrj  */
431ae115bc7Smrj static char *
432ae115bc7Smrj parse_name(benv_des_t *bd, char **next, int *line)
433ae115bc7Smrj {
434ae115bc7Smrj 	char *strbegin;
435ae115bc7Smrj 	char *badeof = "unexpected EOF in %s line %d";
436ae115bc7Smrj 	char *syntax = "syntax error in %s line %d";
437ae115bc7Smrj 	char *c = *next;
438ae115bc7Smrj 
439ae115bc7Smrj 	/*
440ae115bc7Smrj 	 * Skip spaces or tabs. No tolerance for new lines now.
441ae115bc7Smrj 	 */
442ae115bc7Smrj 	while (isspace(*c)) {
443ae115bc7Smrj 		if (*c++ == '\n')
444ae115bc7Smrj 			exit(_error(NO_PERROR, syntax, bd->name, *line));
445ae115bc7Smrj 	}
446ae115bc7Smrj 
447ae115bc7Smrj 	/*
448ae115bc7Smrj 	 * Grab a name for the property to set.
449ae115bc7Smrj 	 */
450ae115bc7Smrj 
451ae115bc7Smrj 	/*
452ae115bc7Smrj 	 * Check first for end of file.  Finding one now would NOT be okay.
453ae115bc7Smrj 	 */
454ae115bc7Smrj 	if (*c == '\0') {
455ae115bc7Smrj 		exit(_error(NO_PERROR, badeof, bd->name, *line));
456ae115bc7Smrj 	}
457ae115bc7Smrj 
458ae115bc7Smrj 	strbegin = c;
459ae115bc7Smrj 	while (*c && !isspace(*c))
460ae115bc7Smrj 		c++;
461ae115bc7Smrj 
462ae115bc7Smrj 	/*
463ae115bc7Smrj 	 * At this point in parsing we have 'setprop name'.  What follows
464ae115bc7Smrj 	 * is a newline, other whitespace, or EOF.  Most of the time we
465ae115bc7Smrj 	 * want to replace a white space character with a NULL to terminate
466ae115bc7Smrj 	 * the name, and then continue on processing.  A newline here provides
467ae115bc7Smrj 	 * the most grief.  If we just replace it with a null we'll
468ae115bc7Smrj 	 * potentially get the setprop on the next line as the value of this
469ae115bc7Smrj 	 * setprop! So, if the last thing we see is a newline we'll have to
470ae115bc7Smrj 	 * dup the string.
471ae115bc7Smrj 	 */
472ae115bc7Smrj 	if (isspace(*c)) {
473ae115bc7Smrj 		if (*c == '\n') {
474ae115bc7Smrj 			*c = '\0';
475ae115bc7Smrj 			strbegin = strdup(strbegin);
476ae115bc7Smrj 			*c = '\n';
477ae115bc7Smrj 		} else {
478ae115bc7Smrj 			*c++ = '\0';
479ae115bc7Smrj 		}
480ae115bc7Smrj 	}
481ae115bc7Smrj 
482ae115bc7Smrj 	*next = c;
483ae115bc7Smrj 	return (strbegin);
484ae115bc7Smrj }
485ae115bc7Smrj 
486ae115bc7Smrj /*
487ae115bc7Smrj  * Parse out the value (RHS) of a setprop line from the boot environment
488ae115bc7Smrj  */
489ae115bc7Smrj static char *
490ae115bc7Smrj parse_value(benv_des_t *bd, char **next, int *line)
491ae115bc7Smrj {
492ae115bc7Smrj 	char *strbegin;
493ae115bc7Smrj 	char *badeof = "unexpected EOF in %s line %d";
494ae115bc7Smrj 	char *result;
495ae115bc7Smrj 	char *c = *next;
496ae115bc7Smrj 	char quote;
497ae115bc7Smrj 
498ae115bc7Smrj 	/*
499ae115bc7Smrj 	 * Skip spaces or tabs. A newline here would indicate a
500ae115bc7Smrj 	 * NULL property value.
501ae115bc7Smrj 	 */
502ae115bc7Smrj 	while (isspace(*c)) {
503ae115bc7Smrj 		if (*c++ == '\n') {
504ae115bc7Smrj 			(*line)++;
505ae115bc7Smrj 			*next = c;
506ae115bc7Smrj 			return (NULL);
507ae115bc7Smrj 		}
508ae115bc7Smrj 	}
509ae115bc7Smrj 
510ae115bc7Smrj 	/*
511ae115bc7Smrj 	 * Grab the value of the property to set.
512ae115bc7Smrj 	 */
513ae115bc7Smrj 
514ae115bc7Smrj 	/*
515ae115bc7Smrj 	 * Check first for end of file.  Finding one now would
516ae115bc7Smrj 	 * also indicate a NULL property.
517ae115bc7Smrj 	 */
518ae115bc7Smrj 	if (*c == '\0') {
519ae115bc7Smrj 		*next = c;
520ae115bc7Smrj 		return (NULL);
521ae115bc7Smrj 	}
522ae115bc7Smrj 
523ae115bc7Smrj 	/*
524ae115bc7Smrj 	 * Value may be quoted, in which case we assume the end of the value
525ae115bc7Smrj 	 * comes with a closing quote.
526ae115bc7Smrj 	 *
527ae115bc7Smrj 	 * We also allow escaped quote characters inside the quoted value.
528ae115bc7Smrj 	 *
529ae115bc7Smrj 	 * For obvious reasons we do not attempt to parse variable references.
530ae115bc7Smrj 	 */
531ae115bc7Smrj 	if (*c == '"' || *c == '\'') {
532ae115bc7Smrj 		quote = *c;
533ae115bc7Smrj 		c++;
534ae115bc7Smrj 		strbegin = c;
535ae115bc7Smrj 		result = c;
536ae115bc7Smrj 		while (*c != quote) {
537ae115bc7Smrj 			if (*c == '\\') {
538ae115bc7Smrj 				c++;
539ae115bc7Smrj 			}
540d92a527cSMark Logan 			if (*c == '\0') {
541ae115bc7Smrj 				break;
542ae115bc7Smrj 			}
543ae115bc7Smrj 			*result++ = *c++;
544ae115bc7Smrj 		}
545ae115bc7Smrj 
546ae115bc7Smrj 		/*
547ae115bc7Smrj 		 *  Throw fatal exception if no end quote found.
548ae115bc7Smrj 		 */
549ae115bc7Smrj 		if (*c != quote) {
550ae115bc7Smrj 			exit(_error(NO_PERROR, badeof, bd->name, *line));
551ae115bc7Smrj 		}
552ae115bc7Smrj 
553ae115bc7Smrj 		*result = '\0';		/* Terminate the result */
554ae115bc7Smrj 		c++;			/* and step past the close quote */
555ae115bc7Smrj 	} else {
556ae115bc7Smrj 		strbegin = c;
557ae115bc7Smrj 		while (*c && !isspace(*c))
558ae115bc7Smrj 			c++;
559ae115bc7Smrj 	}
560ae115bc7Smrj 
561ae115bc7Smrj 	/*
562ae115bc7Smrj 	 * Check again for end of file.  Finding one now is okay.
563ae115bc7Smrj 	 */
564ae115bc7Smrj 	if (*c == '\0') {
565ae115bc7Smrj 		*next = c;
566ae115bc7Smrj 		return (strbegin);
567ae115bc7Smrj 	}
568ae115bc7Smrj 
569ae115bc7Smrj 	*c++ = '\0';
570ae115bc7Smrj 	*next = c;
571ae115bc7Smrj 	return (strbegin);
572ae115bc7Smrj }
573ae115bc7Smrj 
574ae115bc7Smrj /*
575ae115bc7Smrj  * Add a command to the benv list.
576ae115bc7Smrj  */
577ae115bc7Smrj static void
578ae115bc7Smrj add_cmd(benv_des_t *bd, char *last, char **next, int *line)
579ae115bc7Smrj {
580ae115bc7Smrj 	char *cmd, *name, *val;
581ae115bc7Smrj 
582ae115bc7Smrj 	while (*next <= last && **next != COMM) {
583ae115bc7Smrj 		if ((cmd = parse_cmd(bd, next, line)) == NULL)
584ae115bc7Smrj 			break;
585ae115bc7Smrj 		name = parse_name(bd, next, line);
586ae115bc7Smrj 		val = parse_value(bd, next, line);
587ae115bc7Smrj 		add_bent(bd->elist, NULL, cmd, name, val);
588ae115bc7Smrj 		(*line)++;
589ae115bc7Smrj 	};
5903b133becSGangadhar Mylapuram 
591ae115bc7Smrj }
592ae115bc7Smrj 
593ae115bc7Smrj /*
594ae115bc7Smrj  * Parse the benv (bootenv.rc) file and break it into a benv
595ae115bc7Smrj  * list.  List entries may be comment blocks or commands.
596ae115bc7Smrj  */
597ae115bc7Smrj static void
598ae115bc7Smrj parse_benv(benv_des_t *bd)
599ae115bc7Smrj {
600ae115bc7Smrj 	int line;
601ae115bc7Smrj 	char *pbase, *pend;
602ae115bc7Smrj 	char *tok, *tnext;
603ae115bc7Smrj 
604ae115bc7Smrj 	line = 1;
605ae115bc7Smrj 	pbase = (char *)bd->adr;
606ae115bc7Smrj 	pend = pbase + bd->len;
607ae115bc7Smrj 
608d92a527cSMark Logan 	for (tok = tnext = pbase; tnext < pend && '\0' != *tnext; tok = tnext)
609ae115bc7Smrj 		if (*tok == COMM)
610ae115bc7Smrj 			add_comm(bd, tok, pend, &tnext, &line);
611ae115bc7Smrj 		else
612ae115bc7Smrj 			add_cmd(bd, pend, &tnext, &line);
613ae115bc7Smrj }
614ae115bc7Smrj 
615ae115bc7Smrj static void
616ae115bc7Smrj write_benv(benv_des_t *bd)
617ae115bc7Smrj {
618ae115bc7Smrj 	FILE *fp;
619ae115bc7Smrj 	eplist_t *list, *e;
620ae115bc7Smrj 	benv_ent_t *bent;
621ae115bc7Smrj 	char *name;
622ae115bc7Smrj 
623ae115bc7Smrj 	list = bd->elist;
624ae115bc7Smrj 
625ae115bc7Smrj 	if (list->next == list)
626ae115bc7Smrj 		return;
627ae115bc7Smrj 
628ae115bc7Smrj 	if ((fp = fopen(bd->name, "w")) == NULL)
629ae115bc7Smrj 		exit(_error(PERROR, "cannot open %s", bd->name));
630ae115bc7Smrj 
631ae115bc7Smrj 	for (e = list->next; e != list; e = e->next) {
632ae115bc7Smrj 		bent = (benv_ent_t *)e->item;
633ae115bc7Smrj 		name = bent->name;
634ae115bc7Smrj 		if (name) {
635ae115bc7Smrj 			if (bent->val) {
636ae115bc7Smrj 				(void) fprintf(fp, "%s %s ",
637ae115bc7Smrj 				    bent->cmd, bent->name);
638ae115bc7Smrj 				put_quoted(fp, bent->val);
639ae115bc7Smrj 				(void) fprintf(fp, "\n");
640ae115bc7Smrj 			} else {
641ae115bc7Smrj 				(void) fprintf(fp, "%s %s\n",
642ae115bc7Smrj 				    bent->cmd, bent->name);
643ae115bc7Smrj 			}
644ae115bc7Smrj 		} else {
645ae115bc7Smrj 			(void) fprintf(fp, "%s\n", bent->cmd);
646ae115bc7Smrj 		}
647ae115bc7Smrj 	}
648ae115bc7Smrj 
649ae115bc7Smrj 	(void) fclose(fp);
650ae115bc7Smrj }
651ae115bc7Smrj 
652ae115bc7Smrj static char *
653ae115bc7Smrj get_line(void)
654ae115bc7Smrj {
655ae115bc7Smrj 	int c;
656ae115bc7Smrj 	char *nl;
657ae115bc7Smrj 	static char line[256];
658ae115bc7Smrj 
659ae115bc7Smrj 	if (fgets(line, sizeof (line), stdin) != NULL) {
660ae115bc7Smrj 		/*
661ae115bc7Smrj 		 * Remove newline if present,
662ae115bc7Smrj 		 * otherwise discard rest of line.
663ae115bc7Smrj 		 */
664ae115bc7Smrj 		if (nl = strchr(line, '\n'))
665ae115bc7Smrj 			*nl = 0;
666ae115bc7Smrj 		else
667ae115bc7Smrj 			while ((c = getchar()) != '\n' && c != EOF)
668ae115bc7Smrj 				;
669ae115bc7Smrj 		return (line);
670ae115bc7Smrj 	} else
671ae115bc7Smrj 		return (NULL);
672ae115bc7Smrj }
673ae115bc7Smrj 
674ae115bc7Smrj int
675ae115bc7Smrj main(int argc, char **argv)
676ae115bc7Smrj {
677ae115bc7Smrj 	int c;
678ae115bc7Smrj 	int updates = 0;
679ae115bc7Smrj 	char *usage = "Usage: %s [-v] [-f prom-device]"
680ae115bc7Smrj 	    " [variable[=value] ...]";
681ae115bc7Smrj 	eplist_t *elist;
682ae115bc7Smrj 	benv_des_t *bd;
683ae115bc7Smrj 	char *file = NULL;
684ae115bc7Smrj 
68523a1cceaSRoger A. Faulkner 	setpname(argv[0]);
686ae115bc7Smrj 
687ae115bc7Smrj 	while ((c = getopt(argc, argv, "f:Itv")) != -1)
688ae115bc7Smrj 		switch (c) {
689ae115bc7Smrj 		case 'v':
690ae115bc7Smrj 			verbose++;
691ae115bc7Smrj 			break;
692ae115bc7Smrj 		case 'f':
693ae115bc7Smrj 			file = optarg;
694ae115bc7Smrj 			break;
695ae115bc7Smrj 		case 't':
696ae115bc7Smrj 			test++;
697ae115bc7Smrj 			break;
698ae115bc7Smrj 		default:
699ae115bc7Smrj 			exit(_error(NO_PERROR, usage, argv[0]));
700ae115bc7Smrj 		}
701ae115bc7Smrj 
702ae115bc7Smrj 	(void) uname(&uts_buf);
703ae115bc7Smrj 	bd = new_bd();
704ae115bc7Smrj 	init_benv(bd, file);
705ae115bc7Smrj 
706ae115bc7Smrj 	map_benv(bd);
707ae115bc7Smrj 	if (bd->len) {
708ae115bc7Smrj 		parse_benv(bd);
709ae115bc7Smrj 		unmap_benv(bd);
710ae115bc7Smrj 	}
711ae115bc7Smrj 
712ae115bc7Smrj 	elist = bd->elist;
713ae115bc7Smrj 
714ae115bc7Smrj 	if (optind >= argc) {
715ae115bc7Smrj 		print_vars(elist);
716ae115bc7Smrj 		return (0);
717ae115bc7Smrj 	} else
718ae115bc7Smrj 		while (optind < argc) {
719ae115bc7Smrj 			/*
720ae115bc7Smrj 			 * If "-" specified, read variables from stdin;
721ae115bc7Smrj 			 * otherwise, process each argument as a variable
722ae115bc7Smrj 			 * print or set request.
723ae115bc7Smrj 			 */
724ae115bc7Smrj 			if (strcmp(argv[optind], "-") == 0) {
725ae115bc7Smrj 				char *line;
726ae115bc7Smrj 
727ae115bc7Smrj 				while ((line = get_line()) != NULL)
728ae115bc7Smrj 					updates += proc_var(line, elist);
729ae115bc7Smrj 				clearerr(stdin);
730ae115bc7Smrj 			} else
731ae115bc7Smrj 				updates += proc_var(argv[optind], elist);
732ae115bc7Smrj 
733ae115bc7Smrj 			optind++;
734ae115bc7Smrj 		}
735ae115bc7Smrj 
736ae115bc7Smrj 	/*
737ae115bc7Smrj 	 * don't write benv if we are processing delayed writes since
738ae115bc7Smrj 	 * it is likely that the delayed writes changes bootenv.rc anyway...
739ae115bc7Smrj 	 */
740ae115bc7Smrj 	if (updates)
741ae115bc7Smrj 		write_benv(bd);
742ae115bc7Smrj 	close_kbenv();
743ae115bc7Smrj 
744ae115bc7Smrj 	return (0);
745ae115bc7Smrj }
746