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