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