4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2016 Toomas Soome <tsoome@me.com>
24 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
36 * Usage: % eeprom [-v] [-f prom_dev] [-]
37 * % eeprom [-v] [-f prom_dev] field[=value] ...
40 extern void get_kbenv(void);
41 extern void close_kbenv(void);
42 extern caddr_t
get_propval(char *name
, char *node
);
43 extern void setpname(char *prog
);
44 extern char *getbootcmd(void);
47 struct utsname uts_buf
;
53 * Concatenate a NULL terminated list of strings into
64 for (ret
= NULL
, cp
= s
; cp
; cp
= va_arg(ap
, char *)) {
67 len
= strlen(ret
) + 1;
70 ret
= realloc(ret
, len
);
71 (void) strcat(ret
, cp
);
84 list
= (eplist_t
*)malloc(sizeof (eplist_t
));
85 (void) memset(list
, 0, sizeof (eplist_t
));
95 add_item(void *item
, eplist_t
*list
)
99 entry
= (eplist_t
*)malloc(sizeof (eplist_t
));
100 (void) memset(entry
, 0, sizeof (eplist_t
));
104 entry
->prev
= list
->prev
;
105 list
->prev
->next
= entry
;
109 typedef struct benv_ent
{
115 typedef struct benv_des
{
129 bd
= (benv_des_t
*)malloc(sizeof (benv_des_t
));
130 (void) memset(bd
, 0, sizeof (benv_des_t
));
132 bd
->elist
= new_list();
138 * Create a new entry. Comment entries have NULL names.
141 new_bent(char *comm
, char *cmd
, char *name
, char *val
)
145 bent
= (benv_ent_t
*)malloc(sizeof (benv_ent_t
));
146 (void) memset(bent
, 0, sizeof (benv_ent_t
));
149 bent
->cmd
= strdup(comm
);
152 bent
->cmd
= strdup(cmd
);
153 bent
->name
= strdup(name
);
155 bent
->val
= strdup(val
);
162 * Add a new entry to the benv entry list. Entries can be
163 * comments or commands.
166 add_bent(eplist_t
*list
, char *comm
, char *cmd
, char *name
, char *val
)
170 bent
= new_bent(comm
, cmd
, name
, val
);
171 add_item((void *)bent
, list
);
175 get_var(char *name
, eplist_t
*list
)
180 for (e
= list
->next
; e
!= list
; e
= e
->next
) {
181 p
= (benv_ent_t
*)e
->item
;
182 if (p
->name
!= NULL
&& strcmp(p
->name
, name
) == 0)
190 print_var(char *name
, eplist_t
*list
)
195 if (strcmp(name
, "bootcmd") == 0) {
196 bootcmd
= getbootcmd();
197 (void) printf("%s=%s\n", name
, bootcmd
? bootcmd
: "");
198 } else if ((p
= get_var(name
, list
)) == NULL
) {
199 (void) printf("%s: data not available.\n", name
);
201 (void) printf("%s=%s\n", name
, p
->val
? p
->val
: "");
206 print_vars(eplist_t
*list
)
211 for (e
= list
->next
; e
!= list
; e
= e
->next
) {
212 p
= (benv_ent_t
*)e
->item
;
213 if (p
->name
!= NULL
) {
214 (void) printf("%s=%s\n", p
->name
, p
->val
? p
->val
: "");
220 * Write a string to a file, quoted appropriately. We use single
221 * quotes to prevent any variable expansion. Of course, we backslash-quote
222 * any single quotes or backslashes.
225 put_quoted(FILE *fp
, char *val
)
227 (void) putc('\'', fp
);
232 (void) putc('\\', fp
);
235 (void) putc(*val
, fp
);
240 (void) putc('\'', fp
);
244 * Returns 1 if bootenv.rc was modified, 0 otherwise.
247 set_var(char *name
, char *val
, eplist_t
*list
)
251 if (strcmp(name
, "bootcmd") == 0)
255 (void) printf("old:");
256 print_var(name
, list
);
259 if ((p
= get_var(name
, list
)) != NULL
) {
261 p
->val
= strdup(val
);
263 add_bent(list
, NULL
, "setprop", name
, val
);
266 (void) printf("new:");
267 print_var(name
, list
);
273 * Returns 1 if bootenv.rc is modified or 0 if no modification was
274 * necessary. This allows us to implement non super-user look-up of
275 * variables by name without the user being yelled at for trying to
276 * modify the bootenv.rc file.
279 proc_var(char *name
, eplist_t
*list
)
283 if ((val
= strchr(name
, '=')) == NULL
) {
284 print_var(name
, list
);
288 return (set_var(name
, val
, list
));
293 init_benv(benv_des_t
*bd
, char *file
)
299 else if ((boottree
= (char *)get_propval("boottree", "chosen")) == NULL
)
300 boottree
= strcats("/boot", NULL
);
305 bd
->name
= strcats(boottree
, "/solaris/bootenv.rc", NULL
);
309 map_benv(benv_des_t
*bd
)
311 if ((bd
->fd
= open(bd
->name
, O_RDONLY
)) == -1)
315 exit(_error(PERROR
, "cannot open %s", bd
->name
));
317 if ((bd
->len
= (size_t)lseek(bd
->fd
, 0, SEEK_END
)) == 0) {
318 if (close(bd
->fd
) == -1)
319 exit(_error(PERROR
, "close error on %s", bd
->name
));
323 (void) lseek(bd
->fd
, 0, SEEK_SET
);
325 if ((bd
->adr
= mmap((caddr_t
)0, bd
->len
, (PROT_READ
| PROT_WRITE
),
326 MAP_PRIVATE
, bd
->fd
, 0)) == MAP_FAILED
)
327 exit(_error(PERROR
, "cannot map %s", bd
->name
));
331 unmap_benv(benv_des_t
*bd
)
333 if (munmap(bd
->adr
, bd
->len
) == -1)
334 exit(_error(PERROR
, "unmap error on %s", bd
->name
));
336 if (close(bd
->fd
) == -1)
337 exit(_error(PERROR
, "close error on %s", bd
->name
));
344 * Add a comment block to the benv list.
347 add_comm(benv_des_t
*bd
, char *base
, char *last
, char **next
, int *line
)
353 for (p
= base
, lines
= 0; p
< last
; p
++) {
364 add_bent(bd
->elist
, base
, NULL
, NULL
, NULL
);
370 * Parse out an operator (setprop) from the boot environment
373 parse_cmd(benv_des_t
*bd
, char **next
, int *line
)
376 char *badeof
= "unexpected EOF in %s line %d";
377 char *syntax
= "syntax error in %s line %d";
381 * Skip spaces or tabs. New lines increase the line count.
383 while (isspace(*c
)) {
389 * Check for a the setprop command. Currently that's all we
392 * XXX need support for setbinprop?
396 * Check first for end of file. Finding one now would be okay.
397 * We should also bail if we are at the start of a comment.
399 if (*c
== '\0' || *c
== COMM
) {
405 while (*c
&& !isspace(*c
))
409 * Check again for end of file. Finding one now would NOT be okay.
412 exit(_error(NO_PERROR
, badeof
, bd
->name
, *line
));
419 * Last check is to make sure the command is a setprop!
421 if (strcmp(strbegin
, "setprop") != 0) {
422 exit(_error(NO_PERROR
, syntax
, bd
->name
, *line
));
429 * Parse out the name (LHS) of a setprop from the boot environment
432 parse_name(benv_des_t
*bd
, char **next
, int *line
)
435 char *badeof
= "unexpected EOF in %s line %d";
436 char *syntax
= "syntax error in %s line %d";
440 * Skip spaces or tabs. No tolerance for new lines now.
442 while (isspace(*c
)) {
444 exit(_error(NO_PERROR
, syntax
, bd
->name
, *line
));
448 * Grab a name for the property to set.
452 * Check first for end of file. Finding one now would NOT be okay.
455 exit(_error(NO_PERROR
, badeof
, bd
->name
, *line
));
459 while (*c
&& !isspace(*c
))
463 * At this point in parsing we have 'setprop name'. What follows
464 * is a newline, other whitespace, or EOF. Most of the time we
465 * want to replace a white space character with a NULL to terminate
466 * the name, and then continue on processing. A newline here provides
467 * the most grief. If we just replace it with a null we'll
468 * potentially get the setprop on the next line as the value of this
469 * setprop! So, if the last thing we see is a newline we'll have to
475 strbegin
= strdup(strbegin
);
487 * Parse out the value (RHS) of a setprop line from the boot environment
490 parse_value(benv_des_t
*bd
, char **next
, int *line
)
493 char *badeof
= "unexpected EOF in %s line %d";
499 * Skip spaces or tabs. A newline here would indicate a
500 * NULL property value.
502 while (isspace(*c
)) {
511 * Grab the value of the property to set.
515 * Check first for end of file. Finding one now would
516 * also indicate a NULL property.
524 * Value may be quoted, in which case we assume the end of the value
525 * comes with a closing quote.
527 * We also allow escaped quote characters inside the quoted value.
529 * For obvious reasons we do not attempt to parse variable references.
531 if (*c
== '"' || *c
== '\'') {
536 while (*c
!= quote
) {
547 * Throw fatal exception if no end quote found.
550 exit(_error(NO_PERROR
, badeof
, bd
->name
, *line
));
553 *result
= '\0'; /* Terminate the result */
554 c
++; /* and step past the close quote */
557 while (*c
&& !isspace(*c
))
562 * Check again for end of file. Finding one now is okay.
575 * Add a command to the benv list.
578 add_cmd(benv_des_t
*bd
, char *last
, char **next
, int *line
)
580 char *cmd
, *name
, *val
;
582 while (*next
<= last
&& **next
!= COMM
) {
583 if ((cmd
= parse_cmd(bd
, next
, line
)) == NULL
)
585 name
= parse_name(bd
, next
, line
);
586 val
= parse_value(bd
, next
, line
);
587 add_bent(bd
->elist
, NULL
, cmd
, name
, val
);
594 * Parse the benv (bootenv.rc) file and break it into a benv
595 * list. List entries may be comment blocks or commands.
598 parse_benv(benv_des_t
*bd
)
605 pbase
= (char *)bd
->adr
;
606 pend
= pbase
+ bd
->len
;
608 for (tok
= tnext
= pbase
; tnext
< pend
&& '\0' != *tnext
; tok
= tnext
)
610 add_comm(bd
, tok
, pend
, &tnext
, &line
);
612 add_cmd(bd
, pend
, &tnext
, &line
);
616 write_benv(benv_des_t
*bd
)
625 if (list
->next
== list
)
628 if ((fp
= fopen(bd
->name
, "w")) == NULL
)
629 exit(_error(PERROR
, "cannot open %s", bd
->name
));
631 for (e
= list
->next
; e
!= list
; e
= e
->next
) {
632 bent
= (benv_ent_t
*)e
->item
;
636 (void) fprintf(fp
, "%s %s ",
637 bent
->cmd
, bent
->name
);
638 put_quoted(fp
, bent
->val
);
639 (void) fprintf(fp
, "\n");
641 (void) fprintf(fp
, "%s %s\n",
642 bent
->cmd
, bent
->name
);
645 (void) fprintf(fp
, "%s\n", bent
->cmd
);
657 static char line
[256];
659 if (fgets(line
, sizeof (line
), stdin
) != NULL
) {
661 * Remove newline if present,
662 * otherwise discard rest of line.
664 if (nl
= strchr(line
, '\n'))
667 while ((c
= getchar()) != '\n' && c
!= EOF
)
675 main(int argc
, char **argv
)
679 char *usage
= "Usage: %s [-v] [-f prom-device]"
680 " [variable[=value] ...]";
687 while ((c
= getopt(argc
, argv
, "f:Itv")) != -1)
699 exit(_error(NO_PERROR
, usage
, argv
[0]));
702 (void) uname(&uts_buf
);
714 if (optind
>= argc
) {
718 while (optind
< argc
) {
720 * If "-" specified, read variables from stdin;
721 * otherwise, process each argument as a variable
722 * print or set request.
724 if (strcmp(argv
[optind
], "-") == 0) {
727 while ((line
= get_line()) != NULL
)
728 updates
+= proc_var(line
, elist
);
731 updates
+= proc_var(argv
[optind
], elist
);
737 * don't write benv if we are processing delayed writes since
738 * it is likely that the delayed writes changes bootenv.rc anyway...