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