1*3446Smrj /* 2*3446Smrj * CDDL HEADER START 3*3446Smrj * 4*3446Smrj * The contents of this file are subject to the terms of the 5*3446Smrj * Common Development and Distribution License (the "License"). 6*3446Smrj * You may not use this file except in compliance with the License. 7*3446Smrj * 8*3446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3446Smrj * or http://www.opensolaris.org/os/licensing. 10*3446Smrj * See the License for the specific language governing permissions 11*3446Smrj * and limitations under the License. 12*3446Smrj * 13*3446Smrj * When distributing Covered Code, include this CDDL HEADER in each 14*3446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3446Smrj * If applicable, add the following below this CDDL HEADER, with the 16*3446Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17*3446Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18*3446Smrj * 19*3446Smrj * CDDL HEADER END 20*3446Smrj */ 21*3446Smrj /* 22*3446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*3446Smrj * Use is subject to license terms. 24*3446Smrj */ 25*3446Smrj 26*3446Smrj #pragma ident "%Z%%M% %I% %E% SMI" 27*3446Smrj 28*3446Smrj #include "benv.h" 29*3446Smrj #include "message.h" 30*3446Smrj #include <ctype.h> 31*3446Smrj #include <stdarg.h> 32*3446Smrj #include <sys/mman.h> 33*3446Smrj #include <unistd.h> 34*3446Smrj #include <signal.h> 35*3446Smrj #include <sys/wait.h> 36*3446Smrj 37*3446Smrj /* 38*3446Smrj * Usage: % eeprom [-v] [-f prom_dev] [-] 39*3446Smrj * % eeprom [-v] [-f prom_dev] field[=value] ... 40*3446Smrj */ 41*3446Smrj 42*3446Smrj extern void get_kbenv(void); 43*3446Smrj extern void close_kbenv(void); 44*3446Smrj extern caddr_t get_propval(char *name, char *node); 45*3446Smrj extern void setprogname(char *prog); 46*3446Smrj 47*3446Smrj char *boottree; 48*3446Smrj struct utsname uts_buf; 49*3446Smrj 50*3446Smrj static int test; 51*3446Smrj int verbose; 52*3446Smrj 53*3446Smrj /* 54*3446Smrj * Concatenate a NULL terminated list of strings into 55*3446Smrj * a single string. 56*3446Smrj */ 57*3446Smrj char * 58*3446Smrj strcats(char *s, ...) 59*3446Smrj { 60*3446Smrj char *cp, *ret; 61*3446Smrj size_t len; 62*3446Smrj va_list ap; 63*3446Smrj 64*3446Smrj va_start(ap, s); 65*3446Smrj for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) { 66*3446Smrj if (ret == NULL) { 67*3446Smrj ret = strdup(s); 68*3446Smrj len = strlen(ret) + 1; 69*3446Smrj } else { 70*3446Smrj len += strlen(cp); 71*3446Smrj ret = realloc(ret, len); 72*3446Smrj (void) strcat(ret, cp); 73*3446Smrj } 74*3446Smrj } 75*3446Smrj va_end(ap); 76*3446Smrj 77*3446Smrj return (ret); 78*3446Smrj } 79*3446Smrj 80*3446Smrj eplist_t * 81*3446Smrj new_list(void) 82*3446Smrj { 83*3446Smrj eplist_t *list; 84*3446Smrj 85*3446Smrj list = (eplist_t *)malloc(sizeof (eplist_t)); 86*3446Smrj (void) memset(list, 0, sizeof (eplist_t)); 87*3446Smrj 88*3446Smrj list->next = list; 89*3446Smrj list->prev = list; 90*3446Smrj list->item = NULL; 91*3446Smrj 92*3446Smrj return (list); 93*3446Smrj } 94*3446Smrj 95*3446Smrj void 96*3446Smrj add_item(void *item, eplist_t *list) 97*3446Smrj { 98*3446Smrj eplist_t *entry; 99*3446Smrj 100*3446Smrj entry = (eplist_t *)malloc(sizeof (eplist_t)); 101*3446Smrj (void) memset(entry, 0, sizeof (eplist_t)); 102*3446Smrj entry->item = item; 103*3446Smrj 104*3446Smrj entry->next = list; 105*3446Smrj entry->prev = list->prev; 106*3446Smrj list->prev->next = entry; 107*3446Smrj list->prev = entry; 108*3446Smrj } 109*3446Smrj 110*3446Smrj typedef struct benv_ent { 111*3446Smrj char *cmd; 112*3446Smrj char *name; 113*3446Smrj char *val; 114*3446Smrj } benv_ent_t; 115*3446Smrj 116*3446Smrj typedef struct benv_des { 117*3446Smrj char *name; 118*3446Smrj int fd; 119*3446Smrj caddr_t adr; 120*3446Smrj size_t len; 121*3446Smrj eplist_t *elist; 122*3446Smrj } benv_des_t; 123*3446Smrj 124*3446Smrj static benv_des_t * 125*3446Smrj new_bd(void) 126*3446Smrj { 127*3446Smrj 128*3446Smrj benv_des_t *bd; 129*3446Smrj 130*3446Smrj bd = (benv_des_t *)malloc(sizeof (benv_des_t)); 131*3446Smrj (void) memset(bd, 0, sizeof (benv_des_t)); 132*3446Smrj 133*3446Smrj bd->elist = new_list(); 134*3446Smrj 135*3446Smrj return (bd); 136*3446Smrj } 137*3446Smrj 138*3446Smrj /* 139*3446Smrj * Create a new entry. Comment entries have NULL names. 140*3446Smrj */ 141*3446Smrj static benv_ent_t * 142*3446Smrj new_bent(char *comm, char *cmd, char *name, char *val) 143*3446Smrj { 144*3446Smrj benv_ent_t *bent; 145*3446Smrj 146*3446Smrj bent = (benv_ent_t *)malloc(sizeof (benv_ent_t)); 147*3446Smrj (void) memset(bent, 0, sizeof (benv_ent_t)); 148*3446Smrj 149*3446Smrj if (comm) { 150*3446Smrj bent->cmd = strdup(comm); 151*3446Smrj comm = NULL; 152*3446Smrj } else { 153*3446Smrj bent->cmd = strdup(cmd); 154*3446Smrj bent->name = strdup(name); 155*3446Smrj if (val) 156*3446Smrj bent->val = strdup(val); 157*3446Smrj } 158*3446Smrj 159*3446Smrj return (bent); 160*3446Smrj } 161*3446Smrj 162*3446Smrj /* 163*3446Smrj * Add a new entry to the benv entry list. Entries can be 164*3446Smrj * comments or commands. 165*3446Smrj */ 166*3446Smrj static void 167*3446Smrj add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val) 168*3446Smrj { 169*3446Smrj benv_ent_t *bent; 170*3446Smrj 171*3446Smrj bent = new_bent(comm, cmd, name, val); 172*3446Smrj add_item((void *)bent, list); 173*3446Smrj } 174*3446Smrj 175*3446Smrj static benv_ent_t * 176*3446Smrj get_var(char *name, eplist_t *list) 177*3446Smrj { 178*3446Smrj eplist_t *e; 179*3446Smrj benv_ent_t *p; 180*3446Smrj 181*3446Smrj for (e = list->next; e != list; e = e->next) { 182*3446Smrj p = (benv_ent_t *)e->item; 183*3446Smrj if (p->name != NULL && strcmp(p->name, name) == 0) 184*3446Smrj return (p); 185*3446Smrj } 186*3446Smrj 187*3446Smrj return (NULL); 188*3446Smrj } 189*3446Smrj 190*3446Smrj /*PRINTFLIKE1*/ 191*3446Smrj static void 192*3446Smrj eeprom_error(const char *format, ...) 193*3446Smrj { 194*3446Smrj va_list ap; 195*3446Smrj 196*3446Smrj va_start(ap, format); 197*3446Smrj (void) fprintf(stderr, "eeprom: "); 198*3446Smrj (void) vfprintf(stderr, format, ap); 199*3446Smrj va_end(ap); 200*3446Smrj } 201*3446Smrj 202*3446Smrj static int 203*3446Smrj exec_cmd(char *cmdline, char *output, int64_t osize) 204*3446Smrj { 205*3446Smrj char buf[BUFSIZ]; 206*3446Smrj int ret; 207*3446Smrj size_t len; 208*3446Smrj FILE *ptr; 209*3446Smrj sigset_t set; 210*3446Smrj void (*disp)(int); 211*3446Smrj 212*3446Smrj if (output) 213*3446Smrj output[0] = '\0'; 214*3446Smrj 215*3446Smrj /* 216*3446Smrj * For security 217*3446Smrj * - only absolute paths are allowed 218*3446Smrj * - set IFS to space and tab 219*3446Smrj */ 220*3446Smrj if (*cmdline != '/') { 221*3446Smrj eeprom_error(ABS_PATH_REQ, cmdline); 222*3446Smrj return (-1); 223*3446Smrj } 224*3446Smrj (void) putenv("IFS= \t"); 225*3446Smrj 226*3446Smrj /* 227*3446Smrj * We may have been exec'ed with SIGCHLD blocked 228*3446Smrj * unblock it here 229*3446Smrj */ 230*3446Smrj (void) sigemptyset(&set); 231*3446Smrj (void) sigaddset(&set, SIGCHLD); 232*3446Smrj if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 233*3446Smrj eeprom_error(FAILED_SIG, strerror(errno)); 234*3446Smrj return (-1); 235*3446Smrj } 236*3446Smrj 237*3446Smrj /* 238*3446Smrj * Set SIGCHLD disposition to SIG_DFL for popen/pclose 239*3446Smrj */ 240*3446Smrj disp = sigset(SIGCHLD, SIG_DFL); 241*3446Smrj if (disp == SIG_ERR) { 242*3446Smrj eeprom_error(FAILED_SIG, strerror(errno)); 243*3446Smrj return (-1); 244*3446Smrj } 245*3446Smrj if (disp == SIG_HOLD) { 246*3446Smrj eeprom_error(BLOCKED_SIG, cmdline); 247*3446Smrj return (-1); 248*3446Smrj } 249*3446Smrj 250*3446Smrj ptr = popen(cmdline, "r"); 251*3446Smrj if (ptr == NULL) { 252*3446Smrj eeprom_error(POPEN_FAIL, cmdline, strerror(errno)); 253*3446Smrj return (-1); 254*3446Smrj } 255*3446Smrj 256*3446Smrj /* 257*3446Smrj * If we simply do a pclose() following a popen(), pclose() 258*3446Smrj * will close the reader end of the pipe immediately even 259*3446Smrj * if the child process has not started/exited. pclose() 260*3446Smrj * does wait for cmd to terminate before returning though. 261*3446Smrj * When the executed command writes its output to the pipe 262*3446Smrj * there is no reader process and the command dies with 263*3446Smrj * SIGPIPE. To avoid this we read repeatedly until read 264*3446Smrj * terminates with EOF. This indicates that the command 265*3446Smrj * (writer) has closed the pipe and we can safely do a 266*3446Smrj * pclose(). 267*3446Smrj * 268*3446Smrj * Since pclose() does wait for the command to exit, 269*3446Smrj * we can safely reap the exit status of the command 270*3446Smrj * from the value returned by pclose() 271*3446Smrj */ 272*3446Smrj while (fgets(buf, sizeof (buf), ptr) != NULL) { 273*3446Smrj if (output && osize > 0) { 274*3446Smrj (void) snprintf(output, osize, "%s", buf); 275*3446Smrj len = strlen(buf); 276*3446Smrj output += len; 277*3446Smrj osize -= len; 278*3446Smrj } 279*3446Smrj } 280*3446Smrj 281*3446Smrj /* 282*3446Smrj * If there's a "\n" at the end, we want to chop it off 283*3446Smrj */ 284*3446Smrj if (output) { 285*3446Smrj len = strlen(output) - 1; 286*3446Smrj if (output[len] == '\n') 287*3446Smrj output[len] = '\0'; 288*3446Smrj } 289*3446Smrj 290*3446Smrj ret = pclose(ptr); 291*3446Smrj if (ret == -1) { 292*3446Smrj eeprom_error(PCLOSE_FAIL, cmdline, strerror(errno)); 293*3446Smrj return (-1); 294*3446Smrj } 295*3446Smrj 296*3446Smrj if (WIFEXITED(ret)) { 297*3446Smrj return (WEXITSTATUS(ret)); 298*3446Smrj } else { 299*3446Smrj eeprom_error(EXEC_FAIL, cmdline, ret); 300*3446Smrj return (-1); 301*3446Smrj } 302*3446Smrj } 303*3446Smrj 304*3446Smrj #define BOOTADM_STR "bootadm: " 305*3446Smrj 306*3446Smrj /* 307*3446Smrj * bootadm starts all error messages with "bootadm: ". 308*3446Smrj * Add a note so users don't get confused on how they ran bootadm. 309*3446Smrj */ 310*3446Smrj static void 311*3446Smrj output_error_msg(const char *msg) 312*3446Smrj { 313*3446Smrj size_t len = sizeof (BOOTADM_STR) - 1; 314*3446Smrj 315*3446Smrj if (strncmp(msg, BOOTADM_STR, len) == 0) { 316*3446Smrj eeprom_error("error returned from %s\n", msg); 317*3446Smrj } else if (msg[0] != '\0') { 318*3446Smrj eeprom_error("%s\n", msg); 319*3446Smrj } 320*3446Smrj } 321*3446Smrj 322*3446Smrj static char * 323*3446Smrj get_bootadm_value(char *name) 324*3446Smrj { 325*3446Smrj char *ptr, *ret_str, *end_ptr, *orig_ptr; 326*3446Smrj char output[BUFSIZ]; 327*3446Smrj int is_console, is_kernel = 0; 328*3446Smrj size_t len; 329*3446Smrj 330*3446Smrj is_console = (strcmp(name, "console") == 0); 331*3446Smrj 332*3446Smrj if (strcmp(name, "boot-file") == 0) { 333*3446Smrj is_kernel = 1; 334*3446Smrj ptr = "/sbin/bootadm set-menu kernel 2>&1"; 335*3446Smrj } else if (is_console || (strcmp(name, "boot-args") == 0)) { 336*3446Smrj ptr = "/sbin/bootadm set-menu args 2>&1"; 337*3446Smrj } else { 338*3446Smrj eeprom_error("Unknown value in get_bootadm_value: %s\n", name); 339*3446Smrj return (NULL); 340*3446Smrj } 341*3446Smrj 342*3446Smrj if (exec_cmd(ptr, output, BUFSIZ) != 0) { 343*3446Smrj output_error_msg(output); 344*3446Smrj return (NULL); 345*3446Smrj } 346*3446Smrj 347*3446Smrj if (is_console) { 348*3446Smrj if ((ptr = strstr(output, "console=")) == NULL) { 349*3446Smrj return (NULL); 350*3446Smrj } 351*3446Smrj ptr += strlen("console="); 352*3446Smrj 353*3446Smrj /* 354*3446Smrj * -B may have comma-separated values. It may also be 355*3446Smrj * followed by other flags. 356*3446Smrj */ 357*3446Smrj len = strcspn(ptr, " \t,"); 358*3446Smrj ret_str = calloc(len + 1, 1); 359*3446Smrj if (ret_str == NULL) { 360*3446Smrj eeprom_error(NO_MEM, len + 1); 361*3446Smrj return (NULL); 362*3446Smrj } 363*3446Smrj (void) strncpy(ret_str, ptr, len); 364*3446Smrj return (ret_str); 365*3446Smrj } else if (is_kernel) { 366*3446Smrj ret_str = strdup(output); 367*3446Smrj if (ret_str == NULL) 368*3446Smrj eeprom_error(NO_MEM, strlen(output) + 1); 369*3446Smrj return (ret_str); 370*3446Smrj } else { 371*3446Smrj /* If there's no console setting, we can return */ 372*3446Smrj if ((orig_ptr = strstr(output, "console=")) == NULL) { 373*3446Smrj return (strdup(output)); 374*3446Smrj } 375*3446Smrj len = strcspn(orig_ptr, " \t,"); 376*3446Smrj ptr = orig_ptr; 377*3446Smrj end_ptr = orig_ptr + len + 1; 378*3446Smrj 379*3446Smrj /* Eat up any white space */ 380*3446Smrj while ((*end_ptr == ' ') || (*end_ptr == '\t')) 381*3446Smrj end_ptr++; 382*3446Smrj 383*3446Smrj /* 384*3446Smrj * If there's data following the console string, copy it. 385*3446Smrj * If not, cut off the new string. 386*3446Smrj */ 387*3446Smrj if (*end_ptr == '\0') 388*3446Smrj *ptr = '\0'; 389*3446Smrj 390*3446Smrj while (*end_ptr != '\0') { 391*3446Smrj *ptr = *end_ptr; 392*3446Smrj ptr++; 393*3446Smrj end_ptr++; 394*3446Smrj } 395*3446Smrj *ptr = '\0'; 396*3446Smrj if ((strchr(output, '=') == NULL) && 397*3446Smrj (strncmp(output, "-B ", 3) == 0)) { 398*3446Smrj /* 399*3446Smrj * Since we removed the console setting, we no 400*3446Smrj * longer need the initial "-B " 401*3446Smrj */ 402*3446Smrj orig_ptr = output + 3; 403*3446Smrj } else { 404*3446Smrj orig_ptr = output; 405*3446Smrj } 406*3446Smrj 407*3446Smrj ret_str = strdup(orig_ptr); 408*3446Smrj if (ret_str == NULL) 409*3446Smrj eeprom_error(NO_MEM, strlen(orig_ptr) + 1); 410*3446Smrj return (ret_str); 411*3446Smrj } 412*3446Smrj } 413*3446Smrj 414*3446Smrj /* 415*3446Smrj * If quiet is 1, print nothing if there is no value. If quiet is 0, print 416*3446Smrj * a message. 417*3446Smrj */ 418*3446Smrj static void 419*3446Smrj print_bootadm_value(char *name, int quiet) 420*3446Smrj { 421*3446Smrj char *value = get_bootadm_value(name); 422*3446Smrj 423*3446Smrj if ((value != NULL) && (value[0] != '\0')) { 424*3446Smrj (void) printf("%s=%s\n", name, value); 425*3446Smrj } else if (quiet == 0) { 426*3446Smrj (void) printf("%s: data not available.\n", name); 427*3446Smrj } 428*3446Smrj 429*3446Smrj if (value != NULL) 430*3446Smrj free(value); 431*3446Smrj } 432*3446Smrj 433*3446Smrj static void 434*3446Smrj print_var(char *name, eplist_t *list) 435*3446Smrj { 436*3446Smrj benv_ent_t *p; 437*3446Smrj 438*3446Smrj if ((strcmp(name, "boot-file") == 0) || 439*3446Smrj (strcmp(name, "boot-args") == 0) || 440*3446Smrj (strcmp(name, "console") == 0)) { 441*3446Smrj print_bootadm_value(name, 0); 442*3446Smrj } else if ((p = get_var(name, list)) == NULL) 443*3446Smrj (void) printf("%s: data not available.\n", name); 444*3446Smrj else 445*3446Smrj (void) printf("%s=%s\n", name, p->val ? p->val : ""); 446*3446Smrj } 447*3446Smrj 448*3446Smrj static void 449*3446Smrj print_vars(eplist_t *list) 450*3446Smrj { 451*3446Smrj eplist_t *e; 452*3446Smrj benv_ent_t *p; 453*3446Smrj 454*3446Smrj for (e = list->next; e != list; e = e->next) { 455*3446Smrj p = (benv_ent_t *)e->item; 456*3446Smrj if (p->name != NULL) { 457*3446Smrj if ((strcmp(p->name, "boot-file") == 0) || 458*3446Smrj (strcmp(p->name, "boot-args") == 0) || 459*3446Smrj (strcmp(p->name, "console") == 0)) { 460*3446Smrj /* handle these separately */ 461*3446Smrj continue; 462*3446Smrj } 463*3446Smrj (void) printf("%s=%s\n", p->name, p->val ? p->val : ""); 464*3446Smrj } 465*3446Smrj } 466*3446Smrj print_bootadm_value("boot-file", 1); 467*3446Smrj print_bootadm_value("boot-args", 1); 468*3446Smrj print_bootadm_value("console", 1); 469*3446Smrj } 470*3446Smrj 471*3446Smrj /* 472*3446Smrj * Write a string to a file, quoted appropriately. We use single 473*3446Smrj * quotes to prevent any variable expansion. Of course, we backslash-quote 474*3446Smrj * any single quotes or backslashes. 475*3446Smrj */ 476*3446Smrj static void 477*3446Smrj put_quoted(FILE *fp, char *val) 478*3446Smrj { 479*3446Smrj (void) putc('\'', fp); 480*3446Smrj while (*val) { 481*3446Smrj switch (*val) { 482*3446Smrj case '\'': 483*3446Smrj case '\\': 484*3446Smrj (void) putc('\\', fp); 485*3446Smrj /* FALLTHROUGH */ 486*3446Smrj default: 487*3446Smrj (void) putc(*val, fp); 488*3446Smrj break; 489*3446Smrj } 490*3446Smrj val++; 491*3446Smrj } 492*3446Smrj (void) putc('\'', fp); 493*3446Smrj } 494*3446Smrj 495*3446Smrj static void 496*3446Smrj set_bootadm_var(char *name, char *value) 497*3446Smrj { 498*3446Smrj char buf[BUFSIZ]; 499*3446Smrj char output[BUFSIZ] = ""; 500*3446Smrj char *ptr, *console, *args; 501*3446Smrj int is_console; 502*3446Smrj 503*3446Smrj if (verbose) { 504*3446Smrj (void) printf("old:"); 505*3446Smrj print_bootadm_value(name, 0); 506*3446Smrj } 507*3446Smrj 508*3446Smrj /* 509*3446Smrj * For security, we single-quote whatever we run on the command line, 510*3446Smrj * and we don't allow single quotes in the string. 511*3446Smrj */ 512*3446Smrj if ((ptr = strchr(value, '\'')) != NULL) { 513*3446Smrj eeprom_error("Single quotes are not allowed " 514*3446Smrj "in the %s property.\n", name); 515*3446Smrj return; 516*3446Smrj } 517*3446Smrj 518*3446Smrj is_console = (strcmp(name, "console") == 0); 519*3446Smrj if (strcmp(name, "boot-file") == 0) { 520*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu " 521*3446Smrj "kernel='%s' 2>&1", value); 522*3446Smrj } else if (is_console || (strcmp(name, "boot-args") == 0)) { 523*3446Smrj if (is_console) { 524*3446Smrj args = get_bootadm_value("boot-args"); 525*3446Smrj console = value; 526*3446Smrj } else { 527*3446Smrj args = value; 528*3446Smrj console = get_bootadm_value("console"); 529*3446Smrj } 530*3446Smrj if (((args == NULL) || (args[0] == '\0')) && 531*3446Smrj ((console == NULL) || (console[0] == '\0'))) { 532*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm set-menu " 533*3446Smrj "args= 2>&1"); 534*3446Smrj } else if ((args == NULL) || (args[0] == '\0')) { 535*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm " 536*3446Smrj "set-menu args='-B console=%s' 2>&1", 537*3446Smrj console); 538*3446Smrj } else if ((console == NULL) || (console[0] == '\0')) { 539*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm " 540*3446Smrj "set-menu args='%s' 2>&1", args); 541*3446Smrj } else if (strncmp(args, "-B ", 3) != 0) { 542*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm " 543*3446Smrj "set-menu args='-B console=%s %s' 2>&1", 544*3446Smrj console, args); 545*3446Smrj } else { 546*3446Smrj (void) snprintf(buf, BUFSIZ, "/sbin/bootadm " 547*3446Smrj "set-menu args='-B console=%s,%s' 2>&1", 548*3446Smrj console, args + 3); 549*3446Smrj } 550*3446Smrj } else { 551*3446Smrj eeprom_error("Unknown value in set_bootadm_value: %s\n", name); 552*3446Smrj return; 553*3446Smrj } 554*3446Smrj 555*3446Smrj if (exec_cmd(buf, output, BUFSIZ) != 0) { 556*3446Smrj output_error_msg(output); 557*3446Smrj return; 558*3446Smrj } 559*3446Smrj 560*3446Smrj if (verbose) { 561*3446Smrj (void) printf("new:"); 562*3446Smrj print_bootadm_value(name, 0); 563*3446Smrj } 564*3446Smrj } 565*3446Smrj 566*3446Smrj static void 567*3446Smrj set_var(char *name, char *val, eplist_t *list) 568*3446Smrj { 569*3446Smrj benv_ent_t *p; 570*3446Smrj 571*3446Smrj if ((strcmp(name, "boot-file") == 0) || 572*3446Smrj (strcmp(name, "boot-args") == 0) || 573*3446Smrj (strcmp(name, "console") == 0)) { 574*3446Smrj set_bootadm_var(name, val); 575*3446Smrj return; 576*3446Smrj } 577*3446Smrj 578*3446Smrj if (verbose) { 579*3446Smrj (void) printf("old:"); 580*3446Smrj print_var(name, list); 581*3446Smrj } 582*3446Smrj 583*3446Smrj if ((p = get_var(name, list)) != NULL) { 584*3446Smrj free(p->val); 585*3446Smrj p->val = strdup(val); 586*3446Smrj } else 587*3446Smrj add_bent(list, NULL, "setprop", name, val); 588*3446Smrj 589*3446Smrj if (verbose) { 590*3446Smrj (void) printf("new:"); 591*3446Smrj print_var(name, list); 592*3446Smrj } 593*3446Smrj } 594*3446Smrj 595*3446Smrj /* 596*3446Smrj * Modified to return 1 if value modified or 0 if no modification 597*3446Smrj * was necessary. This allows us to implement non super-user 598*3446Smrj * look up of variables by name without the user being yelled at for 599*3446Smrj * trying to modify the bootenv.rc file. 600*3446Smrj */ 601*3446Smrj static int 602*3446Smrj proc_var(char *name, eplist_t *list) 603*3446Smrj { 604*3446Smrj register char *val; 605*3446Smrj 606*3446Smrj if ((val = strchr(name, '=')) == NULL) { 607*3446Smrj print_var(name, list); 608*3446Smrj return (0); 609*3446Smrj } else { 610*3446Smrj *val++ = '\0'; 611*3446Smrj set_var(name, val, list); 612*3446Smrj return (1); 613*3446Smrj } 614*3446Smrj } 615*3446Smrj 616*3446Smrj static void 617*3446Smrj init_benv(benv_des_t *bd, char *file) 618*3446Smrj { 619*3446Smrj get_kbenv(); 620*3446Smrj 621*3446Smrj if (test) 622*3446Smrj boottree = "/tmp"; 623*3446Smrj else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL) 624*3446Smrj boottree = strcats("/boot", NULL); 625*3446Smrj 626*3446Smrj if (file != NULL) 627*3446Smrj bd->name = file; 628*3446Smrj else 629*3446Smrj bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL); 630*3446Smrj } 631*3446Smrj 632*3446Smrj static void 633*3446Smrj map_benv(benv_des_t *bd) 634*3446Smrj { 635*3446Smrj if ((bd->fd = open(bd->name, O_RDONLY)) == -1) 636*3446Smrj if (errno == ENOENT) 637*3446Smrj return; 638*3446Smrj else 639*3446Smrj exit(_error(PERROR, "cannot open %s", bd->name)); 640*3446Smrj 641*3446Smrj if ((bd->len = (size_t)lseek(bd->fd, 0, SEEK_END)) == 0) { 642*3446Smrj if (close(bd->fd) == -1) 643*3446Smrj exit(_error(PERROR, "close error on %s", bd->name)); 644*3446Smrj return; 645*3446Smrj } 646*3446Smrj 647*3446Smrj (void) lseek(bd->fd, 0, SEEK_SET); 648*3446Smrj 649*3446Smrj if ((bd->adr = mmap((caddr_t)0, bd->len, (PROT_READ | PROT_WRITE), 650*3446Smrj MAP_PRIVATE, bd->fd, 0)) == MAP_FAILED) 651*3446Smrj exit(_error(PERROR, "cannot map %s", bd->name)); 652*3446Smrj } 653*3446Smrj 654*3446Smrj static void 655*3446Smrj unmap_benv(benv_des_t *bd) 656*3446Smrj { 657*3446Smrj if (munmap(bd->adr, bd->len) == -1) 658*3446Smrj exit(_error(PERROR, "unmap error on %s", bd->name)); 659*3446Smrj 660*3446Smrj if (close(bd->fd) == -1) 661*3446Smrj exit(_error(PERROR, "close error on %s", bd->name)); 662*3446Smrj } 663*3446Smrj 664*3446Smrj #define NL '\n' 665*3446Smrj #define COMM '#' 666*3446Smrj 667*3446Smrj /* 668*3446Smrj * Add a comment block to the benv list. 669*3446Smrj */ 670*3446Smrj static void 671*3446Smrj add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line) 672*3446Smrj { 673*3446Smrj int nl, lines; 674*3446Smrj char *p; 675*3446Smrj 676*3446Smrj nl = 0; 677*3446Smrj for (p = base, lines = 0; p < last; p++) { 678*3446Smrj if (*p == NL) { 679*3446Smrj nl++; 680*3446Smrj lines++; 681*3446Smrj } else if (nl) { 682*3446Smrj if (*p != COMM) 683*3446Smrj break; 684*3446Smrj nl = 0; 685*3446Smrj } 686*3446Smrj } 687*3446Smrj *(p - 1) = NULL; 688*3446Smrj add_bent(bd->elist, base, NULL, NULL, NULL); 689*3446Smrj *next = p; 690*3446Smrj *line += lines; 691*3446Smrj } 692*3446Smrj 693*3446Smrj /* 694*3446Smrj * Parse out an operator (setprop) from the boot environment 695*3446Smrj */ 696*3446Smrj static char * 697*3446Smrj parse_cmd(benv_des_t *bd, char **next, int *line) 698*3446Smrj { 699*3446Smrj char *strbegin; 700*3446Smrj char *badeof = "unexpected EOF in %s line %d"; 701*3446Smrj char *syntax = "syntax error in %s line %d"; 702*3446Smrj char *c = *next; 703*3446Smrj 704*3446Smrj /* 705*3446Smrj * Skip spaces or tabs. New lines increase the line count. 706*3446Smrj */ 707*3446Smrj while (isspace(*c)) { 708*3446Smrj if (*c++ == '\n') 709*3446Smrj (*line)++; 710*3446Smrj } 711*3446Smrj 712*3446Smrj /* 713*3446Smrj * Check for a the setprop command. Currently that's all we 714*3446Smrj * seem to support. 715*3446Smrj * 716*3446Smrj * XXX need support for setbinprop? 717*3446Smrj */ 718*3446Smrj 719*3446Smrj /* 720*3446Smrj * Check first for end of file. Finding one now would be okay. 721*3446Smrj * We should also bail if we are at the start of a comment. 722*3446Smrj */ 723*3446Smrj if (*c == '\0' || *c == COMM) { 724*3446Smrj *next = c; 725*3446Smrj return (NULL); 726*3446Smrj } 727*3446Smrj 728*3446Smrj strbegin = c; 729*3446Smrj while (*c && !isspace(*c)) 730*3446Smrj c++; 731*3446Smrj 732*3446Smrj /* 733*3446Smrj * Check again for end of file. Finding one now would NOT be okay. 734*3446Smrj */ 735*3446Smrj if (*c == '\0') { 736*3446Smrj exit(_error(NO_PERROR, badeof, bd->name, *line)); 737*3446Smrj } 738*3446Smrj 739*3446Smrj *c++ = '\0'; 740*3446Smrj *next = c; 741*3446Smrj 742*3446Smrj /* 743*3446Smrj * Last check is to make sure the command is a setprop! 744*3446Smrj */ 745*3446Smrj if (strcmp(strbegin, "setprop") != 0) { 746*3446Smrj exit(_error(NO_PERROR, syntax, bd->name, *line)); 747*3446Smrj /* NOTREACHED */ 748*3446Smrj } 749*3446Smrj return (strbegin); 750*3446Smrj } 751*3446Smrj 752*3446Smrj /* 753*3446Smrj * Parse out the name (LHS) of a setprop from the boot environment 754*3446Smrj */ 755*3446Smrj static char * 756*3446Smrj parse_name(benv_des_t *bd, char **next, int *line) 757*3446Smrj { 758*3446Smrj char *strbegin; 759*3446Smrj char *badeof = "unexpected EOF in %s line %d"; 760*3446Smrj char *syntax = "syntax error in %s line %d"; 761*3446Smrj char *c = *next; 762*3446Smrj 763*3446Smrj /* 764*3446Smrj * Skip spaces or tabs. No tolerance for new lines now. 765*3446Smrj */ 766*3446Smrj while (isspace(*c)) { 767*3446Smrj if (*c++ == '\n') 768*3446Smrj exit(_error(NO_PERROR, syntax, bd->name, *line)); 769*3446Smrj } 770*3446Smrj 771*3446Smrj /* 772*3446Smrj * Grab a name for the property to set. 773*3446Smrj */ 774*3446Smrj 775*3446Smrj /* 776*3446Smrj * Check first for end of file. Finding one now would NOT be okay. 777*3446Smrj */ 778*3446Smrj if (*c == '\0') { 779*3446Smrj exit(_error(NO_PERROR, badeof, bd->name, *line)); 780*3446Smrj } 781*3446Smrj 782*3446Smrj strbegin = c; 783*3446Smrj while (*c && !isspace(*c)) 784*3446Smrj c++; 785*3446Smrj 786*3446Smrj /* 787*3446Smrj * At this point in parsing we have 'setprop name'. What follows 788*3446Smrj * is a newline, other whitespace, or EOF. Most of the time we 789*3446Smrj * want to replace a white space character with a NULL to terminate 790*3446Smrj * the name, and then continue on processing. A newline here provides 791*3446Smrj * the most grief. If we just replace it with a null we'll 792*3446Smrj * potentially get the setprop on the next line as the value of this 793*3446Smrj * setprop! So, if the last thing we see is a newline we'll have to 794*3446Smrj * dup the string. 795*3446Smrj */ 796*3446Smrj if (isspace(*c)) { 797*3446Smrj if (*c == '\n') { 798*3446Smrj *c = '\0'; 799*3446Smrj strbegin = strdup(strbegin); 800*3446Smrj *c = '\n'; 801*3446Smrj } else { 802*3446Smrj *c++ = '\0'; 803*3446Smrj } 804*3446Smrj } 805*3446Smrj 806*3446Smrj *next = c; 807*3446Smrj return (strbegin); 808*3446Smrj } 809*3446Smrj 810*3446Smrj /* 811*3446Smrj * Parse out the value (RHS) of a setprop line from the boot environment 812*3446Smrj */ 813*3446Smrj static char * 814*3446Smrj parse_value(benv_des_t *bd, char **next, int *line) 815*3446Smrj { 816*3446Smrj char *strbegin; 817*3446Smrj char *badeof = "unexpected EOF in %s line %d"; 818*3446Smrj char *result; 819*3446Smrj char *c = *next; 820*3446Smrj char quote; 821*3446Smrj 822*3446Smrj /* 823*3446Smrj * Skip spaces or tabs. A newline here would indicate a 824*3446Smrj * NULL property value. 825*3446Smrj */ 826*3446Smrj while (isspace(*c)) { 827*3446Smrj if (*c++ == '\n') { 828*3446Smrj (*line)++; 829*3446Smrj *next = c; 830*3446Smrj return (NULL); 831*3446Smrj } 832*3446Smrj } 833*3446Smrj 834*3446Smrj /* 835*3446Smrj * Grab the value of the property to set. 836*3446Smrj */ 837*3446Smrj 838*3446Smrj /* 839*3446Smrj * Check first for end of file. Finding one now would 840*3446Smrj * also indicate a NULL property. 841*3446Smrj */ 842*3446Smrj if (*c == '\0') { 843*3446Smrj *next = c; 844*3446Smrj return (NULL); 845*3446Smrj } 846*3446Smrj 847*3446Smrj /* 848*3446Smrj * Value may be quoted, in which case we assume the end of the value 849*3446Smrj * comes with a closing quote. 850*3446Smrj * 851*3446Smrj * We also allow escaped quote characters inside the quoted value. 852*3446Smrj * 853*3446Smrj * For obvious reasons we do not attempt to parse variable references. 854*3446Smrj */ 855*3446Smrj if (*c == '"' || *c == '\'') { 856*3446Smrj quote = *c; 857*3446Smrj c++; 858*3446Smrj strbegin = c; 859*3446Smrj result = c; 860*3446Smrj while (*c != quote) { 861*3446Smrj if (*c == '\\') { 862*3446Smrj c++; 863*3446Smrj } 864*3446Smrj if (*c == '\0' || *c == '\n') { 865*3446Smrj break; 866*3446Smrj } 867*3446Smrj *result++ = *c++; 868*3446Smrj } 869*3446Smrj 870*3446Smrj /* 871*3446Smrj * Throw fatal exception if no end quote found. 872*3446Smrj */ 873*3446Smrj if (*c != quote) { 874*3446Smrj exit(_error(NO_PERROR, badeof, bd->name, *line)); 875*3446Smrj } 876*3446Smrj 877*3446Smrj *result = '\0'; /* Terminate the result */ 878*3446Smrj c++; /* and step past the close quote */ 879*3446Smrj } else { 880*3446Smrj strbegin = c; 881*3446Smrj while (*c && !isspace(*c)) 882*3446Smrj c++; 883*3446Smrj } 884*3446Smrj 885*3446Smrj /* 886*3446Smrj * Check again for end of file. Finding one now is okay. 887*3446Smrj */ 888*3446Smrj if (*c == '\0') { 889*3446Smrj *next = c; 890*3446Smrj return (strbegin); 891*3446Smrj } 892*3446Smrj 893*3446Smrj *c++ = '\0'; 894*3446Smrj *next = c; 895*3446Smrj return (strbegin); 896*3446Smrj } 897*3446Smrj 898*3446Smrj /* 899*3446Smrj * Add a command to the benv list. 900*3446Smrj */ 901*3446Smrj static void 902*3446Smrj add_cmd(benv_des_t *bd, char *last, char **next, int *line) 903*3446Smrj { 904*3446Smrj char *cmd, *name, *val; 905*3446Smrj 906*3446Smrj while (*next <= last && **next != COMM) { 907*3446Smrj if ((cmd = parse_cmd(bd, next, line)) == NULL) 908*3446Smrj break; 909*3446Smrj name = parse_name(bd, next, line); 910*3446Smrj val = parse_value(bd, next, line); 911*3446Smrj add_bent(bd->elist, NULL, cmd, name, val); 912*3446Smrj (*line)++; 913*3446Smrj }; 914*3446Smrj } 915*3446Smrj 916*3446Smrj /* 917*3446Smrj * Parse the benv (bootenv.rc) file and break it into a benv 918*3446Smrj * list. List entries may be comment blocks or commands. 919*3446Smrj */ 920*3446Smrj static void 921*3446Smrj parse_benv(benv_des_t *bd) 922*3446Smrj { 923*3446Smrj int line; 924*3446Smrj char *pbase, *pend; 925*3446Smrj char *tok, *tnext; 926*3446Smrj 927*3446Smrj line = 1; 928*3446Smrj pbase = (char *)bd->adr; 929*3446Smrj pend = pbase + bd->len; 930*3446Smrj 931*3446Smrj for (tok = tnext = pbase; tnext < pend; tok = tnext) 932*3446Smrj if (*tok == COMM) 933*3446Smrj add_comm(bd, tok, pend, &tnext, &line); 934*3446Smrj else 935*3446Smrj add_cmd(bd, pend, &tnext, &line); 936*3446Smrj } 937*3446Smrj 938*3446Smrj static void 939*3446Smrj write_benv(benv_des_t *bd) 940*3446Smrj { 941*3446Smrj FILE *fp; 942*3446Smrj eplist_t *list, *e; 943*3446Smrj benv_ent_t *bent; 944*3446Smrj char *name; 945*3446Smrj 946*3446Smrj list = bd->elist; 947*3446Smrj 948*3446Smrj if (list->next == list) 949*3446Smrj return; 950*3446Smrj 951*3446Smrj if ((fp = fopen(bd->name, "w")) == NULL) 952*3446Smrj exit(_error(PERROR, "cannot open %s", bd->name)); 953*3446Smrj 954*3446Smrj for (e = list->next; e != list; e = e->next) { 955*3446Smrj bent = (benv_ent_t *)e->item; 956*3446Smrj name = bent->name; 957*3446Smrj if (name) { 958*3446Smrj if (bent->val) { 959*3446Smrj (void) fprintf(fp, "%s %s ", 960*3446Smrj bent->cmd, bent->name); 961*3446Smrj put_quoted(fp, bent->val); 962*3446Smrj (void) fprintf(fp, "\n"); 963*3446Smrj } else { 964*3446Smrj (void) fprintf(fp, "%s %s\n", 965*3446Smrj bent->cmd, bent->name); 966*3446Smrj } 967*3446Smrj } else { 968*3446Smrj (void) fprintf(fp, "%s\n", bent->cmd); 969*3446Smrj } 970*3446Smrj } 971*3446Smrj 972*3446Smrj (void) fclose(fp); 973*3446Smrj } 974*3446Smrj 975*3446Smrj static char * 976*3446Smrj get_line(void) 977*3446Smrj { 978*3446Smrj int c; 979*3446Smrj char *nl; 980*3446Smrj static char line[256]; 981*3446Smrj 982*3446Smrj if (fgets(line, sizeof (line), stdin) != NULL) { 983*3446Smrj /* 984*3446Smrj * Remove newline if present, 985*3446Smrj * otherwise discard rest of line. 986*3446Smrj */ 987*3446Smrj if (nl = strchr(line, '\n')) 988*3446Smrj *nl = 0; 989*3446Smrj else 990*3446Smrj while ((c = getchar()) != '\n' && c != EOF) 991*3446Smrj ; 992*3446Smrj return (line); 993*3446Smrj } else 994*3446Smrj return (NULL); 995*3446Smrj } 996*3446Smrj 997*3446Smrj int 998*3446Smrj main(int argc, char **argv) 999*3446Smrj { 1000*3446Smrj int c; 1001*3446Smrj int updates = 0; 1002*3446Smrj char *usage = "Usage: %s [-v] [-f prom-device]" 1003*3446Smrj " [variable[=value] ...]"; 1004*3446Smrj eplist_t *elist; 1005*3446Smrj benv_des_t *bd; 1006*3446Smrj char *file = NULL; 1007*3446Smrj 1008*3446Smrj setprogname(argv[0]); 1009*3446Smrj 1010*3446Smrj while ((c = getopt(argc, argv, "f:Itv")) != -1) 1011*3446Smrj switch (c) { 1012*3446Smrj case 'v': 1013*3446Smrj verbose++; 1014*3446Smrj break; 1015*3446Smrj case 'f': 1016*3446Smrj file = optarg; 1017*3446Smrj break; 1018*3446Smrj case 't': 1019*3446Smrj test++; 1020*3446Smrj break; 1021*3446Smrj default: 1022*3446Smrj exit(_error(NO_PERROR, usage, argv[0])); 1023*3446Smrj } 1024*3446Smrj 1025*3446Smrj (void) uname(&uts_buf); 1026*3446Smrj bd = new_bd(); 1027*3446Smrj init_benv(bd, file); 1028*3446Smrj 1029*3446Smrj map_benv(bd); 1030*3446Smrj if (bd->len) { 1031*3446Smrj parse_benv(bd); 1032*3446Smrj unmap_benv(bd); 1033*3446Smrj } 1034*3446Smrj 1035*3446Smrj elist = bd->elist; 1036*3446Smrj 1037*3446Smrj if (optind >= argc) { 1038*3446Smrj print_vars(elist); 1039*3446Smrj return (0); 1040*3446Smrj } else 1041*3446Smrj while (optind < argc) { 1042*3446Smrj /* 1043*3446Smrj * If "-" specified, read variables from stdin; 1044*3446Smrj * otherwise, process each argument as a variable 1045*3446Smrj * print or set request. 1046*3446Smrj */ 1047*3446Smrj if (strcmp(argv[optind], "-") == 0) { 1048*3446Smrj char *line; 1049*3446Smrj 1050*3446Smrj while ((line = get_line()) != NULL) 1051*3446Smrj updates += proc_var(line, elist); 1052*3446Smrj clearerr(stdin); 1053*3446Smrj } else 1054*3446Smrj updates += proc_var(argv[optind], elist); 1055*3446Smrj 1056*3446Smrj optind++; 1057*3446Smrj } 1058*3446Smrj 1059*3446Smrj /* 1060*3446Smrj * don't write benv if we are processing delayed writes since 1061*3446Smrj * it is likely that the delayed writes changes bootenv.rc anyway... 1062*3446Smrj */ 1063*3446Smrj if (updates) 1064*3446Smrj write_benv(bd); 1065*3446Smrj close_kbenv(); 1066*3446Smrj 1067*3446Smrj return (0); 1068*3446Smrj } 1069