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 * 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 * 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 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 * 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 * 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 * 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 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