640 number_to_scaled_string is duplicated in several commands
[unleashed.git] / usr / src / cmd / eeprom / i386 / benv.c
blob11613cc5ce002ce0d745bcbdd40ee5484f3dba87
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2016 Toomas Soome <tsoome@me.com>
24 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
27 #include "benv.h"
28 #include <ctype.h>
29 #include <stdarg.h>
30 #include <sys/mman.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <sys/wait.h>
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);
46 char *boottree;
47 struct utsname uts_buf;
49 static int test;
50 int verbose;
53 * Concatenate a NULL terminated list of strings into
54 * a single string.
56 char *
57 strcats(char *s, ...)
59 char *cp, *ret;
60 size_t len;
61 va_list ap;
63 va_start(ap, s);
64 for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) {
65 if (ret == NULL) {
66 ret = strdup(s);
67 len = strlen(ret) + 1;
68 } else {
69 len += strlen(cp);
70 ret = realloc(ret, len);
71 (void) strcat(ret, cp);
74 va_end(ap);
76 return (ret);
79 eplist_t *
80 new_list(void)
82 eplist_t *list;
84 list = (eplist_t *)malloc(sizeof (eplist_t));
85 (void) memset(list, 0, sizeof (eplist_t));
87 list->next = list;
88 list->prev = list;
89 list->item = NULL;
91 return (list);
94 void
95 add_item(void *item, eplist_t *list)
97 eplist_t *entry;
99 entry = (eplist_t *)malloc(sizeof (eplist_t));
100 (void) memset(entry, 0, sizeof (eplist_t));
101 entry->item = item;
103 entry->next = list;
104 entry->prev = list->prev;
105 list->prev->next = entry;
106 list->prev = entry;
109 typedef struct benv_ent {
110 char *cmd;
111 char *name;
112 char *val;
113 } benv_ent_t;
115 typedef struct benv_des {
116 char *name;
117 int fd;
118 caddr_t adr;
119 size_t len;
120 eplist_t *elist;
121 } benv_des_t;
123 static benv_des_t *
124 new_bd(void)
127 benv_des_t *bd;
129 bd = (benv_des_t *)malloc(sizeof (benv_des_t));
130 (void) memset(bd, 0, sizeof (benv_des_t));
132 bd->elist = new_list();
134 return (bd);
138 * Create a new entry. Comment entries have NULL names.
140 static benv_ent_t *
141 new_bent(char *comm, char *cmd, char *name, char *val)
143 benv_ent_t *bent;
145 bent = (benv_ent_t *)malloc(sizeof (benv_ent_t));
146 (void) memset(bent, 0, sizeof (benv_ent_t));
148 if (comm) {
149 bent->cmd = strdup(comm);
150 comm = NULL;
151 } else {
152 bent->cmd = strdup(cmd);
153 bent->name = strdup(name);
154 if (val)
155 bent->val = strdup(val);
158 return (bent);
162 * Add a new entry to the benv entry list. Entries can be
163 * comments or commands.
165 static void
166 add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val)
168 benv_ent_t *bent;
170 bent = new_bent(comm, cmd, name, val);
171 add_item((void *)bent, list);
174 static benv_ent_t *
175 get_var(char *name, eplist_t *list)
177 eplist_t *e;
178 benv_ent_t *p;
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)
183 return (p);
186 return (NULL);
189 static void
190 print_var(char *name, eplist_t *list)
192 benv_ent_t *p;
193 char *bootcmd;
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);
200 } else {
201 (void) printf("%s=%s\n", name, p->val ? p->val : "");
205 static void
206 print_vars(eplist_t *list)
208 eplist_t *e;
209 benv_ent_t *p;
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.
224 static void
225 put_quoted(FILE *fp, char *val)
227 (void) putc('\'', fp);
228 while (*val) {
229 switch (*val) {
230 case '\'':
231 case '\\':
232 (void) putc('\\', fp);
233 /* FALLTHROUGH */
234 default:
235 (void) putc(*val, fp);
236 break;
238 val++;
240 (void) putc('\'', fp);
244 * Returns 1 if bootenv.rc was modified, 0 otherwise.
246 static int
247 set_var(char *name, char *val, eplist_t *list)
249 benv_ent_t *p;
251 if (strcmp(name, "bootcmd") == 0)
252 return (0);
254 if (verbose) {
255 (void) printf("old:");
256 print_var(name, list);
259 if ((p = get_var(name, list)) != NULL) {
260 free(p->val);
261 p->val = strdup(val);
262 } else
263 add_bent(list, NULL, "setprop", name, val);
265 if (verbose) {
266 (void) printf("new:");
267 print_var(name, list);
269 return (1);
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.
278 static int
279 proc_var(char *name, eplist_t *list)
281 register char *val;
283 if ((val = strchr(name, '=')) == NULL) {
284 print_var(name, list);
285 return (0);
286 } else {
287 *val++ = '\0';
288 return (set_var(name, val, list));
292 static void
293 init_benv(benv_des_t *bd, char *file)
295 get_kbenv();
297 if (test)
298 boottree = "/tmp";
299 else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL)
300 boottree = strcats("/boot", NULL);
302 if (file != NULL)
303 bd->name = file;
304 else
305 bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL);
308 static void
309 map_benv(benv_des_t *bd)
311 if ((bd->fd = open(bd->name, O_RDONLY)) == -1)
312 if (errno == ENOENT)
313 return;
314 else
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));
320 return;
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));
330 static void
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));
340 #define NL '\n'
341 #define COMM '#'
344 * Add a comment block to the benv list.
346 static void
347 add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line)
349 int nl, lines;
350 char *p;
352 nl = 0;
353 for (p = base, lines = 0; p < last; p++) {
354 if (*p == NL) {
355 nl++;
356 lines++;
357 } else if (nl) {
358 if (*p != COMM)
359 break;
360 nl = 0;
363 *(p - 1) = NULL;
364 add_bent(bd->elist, base, NULL, NULL, NULL);
365 *next = p;
366 *line += lines;
370 * Parse out an operator (setprop) from the boot environment
372 static char *
373 parse_cmd(benv_des_t *bd, char **next, int *line)
375 char *strbegin;
376 char *badeof = "unexpected EOF in %s line %d";
377 char *syntax = "syntax error in %s line %d";
378 char *c = *next;
381 * Skip spaces or tabs. New lines increase the line count.
383 while (isspace(*c)) {
384 if (*c++ == '\n')
385 (*line)++;
389 * Check for a the setprop command. Currently that's all we
390 * seem to support.
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) {
400 *next = c;
401 return (NULL);
404 strbegin = c;
405 while (*c && !isspace(*c))
406 c++;
409 * Check again for end of file. Finding one now would NOT be okay.
411 if (*c == '\0') {
412 exit(_error(NO_PERROR, badeof, bd->name, *line));
415 *c++ = '\0';
416 *next = c;
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));
423 /* NOTREACHED */
425 return (strbegin);
429 * Parse out the name (LHS) of a setprop from the boot environment
431 static char *
432 parse_name(benv_des_t *bd, char **next, int *line)
434 char *strbegin;
435 char *badeof = "unexpected EOF in %s line %d";
436 char *syntax = "syntax error in %s line %d";
437 char *c = *next;
440 * Skip spaces or tabs. No tolerance for new lines now.
442 while (isspace(*c)) {
443 if (*c++ == '\n')
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.
454 if (*c == '\0') {
455 exit(_error(NO_PERROR, badeof, bd->name, *line));
458 strbegin = c;
459 while (*c && !isspace(*c))
460 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
470 * dup the string.
472 if (isspace(*c)) {
473 if (*c == '\n') {
474 *c = '\0';
475 strbegin = strdup(strbegin);
476 *c = '\n';
477 } else {
478 *c++ = '\0';
482 *next = c;
483 return (strbegin);
487 * Parse out the value (RHS) of a setprop line from the boot environment
489 static char *
490 parse_value(benv_des_t *bd, char **next, int *line)
492 char *strbegin;
493 char *badeof = "unexpected EOF in %s line %d";
494 char *result;
495 char *c = *next;
496 char quote;
499 * Skip spaces or tabs. A newline here would indicate a
500 * NULL property value.
502 while (isspace(*c)) {
503 if (*c++ == '\n') {
504 (*line)++;
505 *next = c;
506 return (NULL);
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.
518 if (*c == '\0') {
519 *next = c;
520 return (NULL);
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 == '\'') {
532 quote = *c;
533 c++;
534 strbegin = c;
535 result = c;
536 while (*c != quote) {
537 if (*c == '\\') {
538 c++;
540 if (*c == '\0') {
541 break;
543 *result++ = *c++;
547 * Throw fatal exception if no end quote found.
549 if (*c != quote) {
550 exit(_error(NO_PERROR, badeof, bd->name, *line));
553 *result = '\0'; /* Terminate the result */
554 c++; /* and step past the close quote */
555 } else {
556 strbegin = c;
557 while (*c && !isspace(*c))
558 c++;
562 * Check again for end of file. Finding one now is okay.
564 if (*c == '\0') {
565 *next = c;
566 return (strbegin);
569 *c++ = '\0';
570 *next = c;
571 return (strbegin);
575 * Add a command to the benv list.
577 static void
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)
584 break;
585 name = parse_name(bd, next, line);
586 val = parse_value(bd, next, line);
587 add_bent(bd->elist, NULL, cmd, name, val);
588 (*line)++;
594 * Parse the benv (bootenv.rc) file and break it into a benv
595 * list. List entries may be comment blocks or commands.
597 static void
598 parse_benv(benv_des_t *bd)
600 int line;
601 char *pbase, *pend;
602 char *tok, *tnext;
604 line = 1;
605 pbase = (char *)bd->adr;
606 pend = pbase + bd->len;
608 for (tok = tnext = pbase; tnext < pend && '\0' != *tnext; tok = tnext)
609 if (*tok == COMM)
610 add_comm(bd, tok, pend, &tnext, &line);
611 else
612 add_cmd(bd, pend, &tnext, &line);
615 static void
616 write_benv(benv_des_t *bd)
618 FILE *fp;
619 eplist_t *list, *e;
620 benv_ent_t *bent;
621 char *name;
623 list = bd->elist;
625 if (list->next == list)
626 return;
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;
633 name = bent->name;
634 if (name) {
635 if (bent->val) {
636 (void) fprintf(fp, "%s %s ",
637 bent->cmd, bent->name);
638 put_quoted(fp, bent->val);
639 (void) fprintf(fp, "\n");
640 } else {
641 (void) fprintf(fp, "%s %s\n",
642 bent->cmd, bent->name);
644 } else {
645 (void) fprintf(fp, "%s\n", bent->cmd);
649 (void) fclose(fp);
652 static char *
653 get_line(void)
655 int c;
656 char *nl;
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'))
665 *nl = 0;
666 else
667 while ((c = getchar()) != '\n' && c != EOF)
669 return (line);
670 } else
671 return (NULL);
675 main(int argc, char **argv)
677 int c;
678 int updates = 0;
679 char *usage = "Usage: %s [-v] [-f prom-device]"
680 " [variable[=value] ...]";
681 eplist_t *elist;
682 benv_des_t *bd;
683 char *file = NULL;
685 setpname(argv[0]);
687 while ((c = getopt(argc, argv, "f:Itv")) != -1)
688 switch (c) {
689 case 'v':
690 verbose++;
691 break;
692 case 'f':
693 file = optarg;
694 break;
695 case 't':
696 test++;
697 break;
698 default:
699 exit(_error(NO_PERROR, usage, argv[0]));
702 (void) uname(&uts_buf);
703 bd = new_bd();
704 init_benv(bd, file);
706 map_benv(bd);
707 if (bd->len) {
708 parse_benv(bd);
709 unmap_benv(bd);
712 elist = bd->elist;
714 if (optind >= argc) {
715 print_vars(elist);
716 return (0);
717 } else
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) {
725 char *line;
727 while ((line = get_line()) != NULL)
728 updates += proc_var(line, elist);
729 clearerr(stdin);
730 } else
731 updates += proc_var(argv[optind], elist);
733 optind++;
737 * don't write benv if we are processing delayed writes since
738 * it is likely that the delayed writes changes bootenv.rc anyway...
740 if (updates)
741 write_benv(bd);
742 close_kbenv();
744 return (0);