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 /* 23 * Copyright 2016 Toomas Soome <tsoome@me.com> 24 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. 25 */ 26 27 #include "benv.h" 28 #include <ctype.h> 29 #include <stdarg.h> 30 #include <stdbool.h> 31 #include <sys/mman.h> 32 #include <unistd.h> 33 #include <signal.h> 34 #include <sys/wait.h> 35 #include <libzfsbootenv.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 setpname(char *prog); 46 extern char *getbootcmd(void); 47 48 char *boottree; 49 struct utsname uts_buf; 50 51 static int test; 52 int verbose; 53 54 /* 55 * Concatenate a NULL terminated list of strings into 56 * a single string. 57 */ 58 char * 59 strcats(char *s, ...) 60 { 61 char *cp, *ret; 62 size_t len; 63 va_list ap; 64 65 va_start(ap, s); 66 for (ret = NULL, cp = s; cp; cp = va_arg(ap, char *)) { 67 if (ret == NULL) { 68 ret = strdup(s); 69 len = strlen(ret) + 1; 70 } else { 71 len += strlen(cp); 72 ret = realloc(ret, len); 73 (void) strcat(ret, cp); 74 } 75 } 76 va_end(ap); 77 78 return (ret); 79 } 80 81 eplist_t * 82 new_list(void) 83 { 84 eplist_t *list; 85 86 list = (eplist_t *)malloc(sizeof (eplist_t)); 87 (void) memset(list, 0, sizeof (eplist_t)); 88 89 list->next = list; 90 list->prev = list; 91 list->item = NULL; 92 93 return (list); 94 } 95 96 void 97 add_item(void *item, eplist_t *list) 98 { 99 eplist_t *entry; 100 101 entry = (eplist_t *)malloc(sizeof (eplist_t)); 102 (void) memset(entry, 0, sizeof (eplist_t)); 103 entry->item = item; 104 105 entry->next = list; 106 entry->prev = list->prev; 107 list->prev->next = entry; 108 list->prev = entry; 109 } 110 111 typedef struct benv_ent { 112 char *cmd; 113 char *name; 114 char *val; 115 } benv_ent_t; 116 117 typedef struct benv_des { 118 char *name; 119 int fd; 120 caddr_t adr; 121 size_t len; 122 eplist_t *elist; 123 } benv_des_t; 124 125 static benv_des_t * 126 new_bd(void) 127 { 128 129 benv_des_t *bd; 130 131 bd = (benv_des_t *)malloc(sizeof (benv_des_t)); 132 (void) memset(bd, 0, sizeof (benv_des_t)); 133 134 bd->elist = new_list(); 135 136 return (bd); 137 } 138 139 /* 140 * Create a new entry. Comment entries have NULL names. 141 */ 142 static benv_ent_t * 143 new_bent(char *comm, char *cmd, char *name, char *val) 144 { 145 benv_ent_t *bent; 146 147 bent = (benv_ent_t *)malloc(sizeof (benv_ent_t)); 148 (void) memset(bent, 0, sizeof (benv_ent_t)); 149 150 if (comm) { 151 bent->cmd = strdup(comm); 152 comm = NULL; 153 } else { 154 bent->cmd = strdup(cmd); 155 bent->name = strdup(name); 156 if (val) 157 bent->val = strdup(val); 158 } 159 160 return (bent); 161 } 162 163 /* 164 * Add a new entry to the benv entry list. Entries can be 165 * comments or commands. 166 */ 167 static void 168 add_bent(eplist_t *list, char *comm, char *cmd, char *name, char *val) 169 { 170 benv_ent_t *bent; 171 172 bent = new_bent(comm, cmd, name, val); 173 add_item((void *)bent, list); 174 } 175 176 static benv_ent_t * 177 get_var(char *name, eplist_t *list) 178 { 179 eplist_t *e; 180 benv_ent_t *p; 181 182 for (e = list->next; e != list; e = e->next) { 183 p = (benv_ent_t *)e->item; 184 if (p->name != NULL && strcmp(p->name, name) == 0) 185 return (p); 186 } 187 188 return (NULL); 189 } 190 191 static void 192 print_var(char *name, eplist_t *list) 193 { 194 benv_ent_t *p; 195 char *bootcmd; 196 197 if (strcmp(name, "bootcmd") == 0) { 198 bootcmd = getbootcmd(); 199 (void) printf("%s=%s\n", name, bootcmd ? bootcmd : ""); 200 } else if ((p = get_var(name, list)) == NULL) { 201 (void) printf("%s: data not available.\n", name); 202 } else { 203 (void) printf("%s=%s\n", name, p->val ? p->val : ""); 204 } 205 } 206 207 static void 208 print_vars(eplist_t *list) 209 { 210 eplist_t *e; 211 benv_ent_t *p; 212 213 for (e = list->next; e != list; e = e->next) { 214 p = (benv_ent_t *)e->item; 215 if (p->name != NULL) { 216 (void) printf("%s=%s\n", p->name, p->val ? p->val : ""); 217 } 218 } 219 } 220 221 /* 222 * Write a string to a file, quoted appropriately. We use single 223 * quotes to prevent any variable expansion. Of course, we backslash-quote 224 * any single quotes or backslashes. 225 */ 226 static void 227 put_quoted(FILE *fp, char *val) 228 { 229 (void) putc('\'', fp); 230 while (*val) { 231 switch (*val) { 232 case '\'': 233 case '\\': 234 (void) putc('\\', fp); 235 /* FALLTHROUGH */ 236 default: 237 (void) putc(*val, fp); 238 break; 239 } 240 val++; 241 } 242 (void) putc('\'', fp); 243 } 244 245 /* 246 * Returns 1 if bootenv.rc was modified, 0 otherwise. 247 */ 248 static int 249 set_var(char *name, char *val, eplist_t *list) 250 { 251 benv_ent_t *p; 252 253 if (strcmp(name, "bootcmd") == 0) 254 return (0); 255 256 if (verbose) { 257 (void) printf("old:"); 258 print_var(name, list); 259 } 260 261 if ((p = get_var(name, list)) != NULL) { 262 free(p->val); 263 p->val = strdup(val); 264 } else 265 add_bent(list, NULL, "setprop", name, val); 266 267 if (verbose) { 268 (void) printf("new:"); 269 print_var(name, list); 270 } 271 return (1); 272 } 273 274 /* 275 * Returns 1 if bootenv.rc is modified or 0 if no modification was 276 * necessary. This allows us to implement non super-user look-up of 277 * variables by name without the user being yelled at for trying to 278 * modify the bootenv.rc file. 279 */ 280 static int 281 proc_var(char *name, eplist_t *list) 282 { 283 register char *val; 284 285 if ((val = strchr(name, '=')) == NULL) { 286 print_var(name, list); 287 return (0); 288 } else { 289 *val++ = '\0'; 290 return (set_var(name, val, list)); 291 } 292 } 293 294 static void 295 init_benv(benv_des_t *bd, char *file) 296 { 297 get_kbenv(); 298 299 if (test) 300 boottree = "/tmp"; 301 else if ((boottree = (char *)get_propval("boottree", "chosen")) == NULL) 302 boottree = strcats("/boot", NULL); 303 304 if (file != NULL) 305 bd->name = file; 306 else 307 bd->name = strcats(boottree, "/solaris/bootenv.rc", NULL); 308 } 309 310 static void 311 map_benv(benv_des_t *bd) 312 { 313 if ((bd->fd = open(bd->name, O_RDONLY)) == -1) 314 if (errno == ENOENT) 315 return; 316 else 317 exit(_error(PERROR, "cannot open %s", bd->name)); 318 319 if ((bd->len = (size_t)lseek(bd->fd, 0, SEEK_END)) == 0) { 320 if (close(bd->fd) == -1) 321 exit(_error(PERROR, "close error on %s", bd->name)); 322 return; 323 } 324 325 (void) lseek(bd->fd, 0, SEEK_SET); 326 327 if ((bd->adr = mmap((caddr_t)0, bd->len, (PROT_READ | PROT_WRITE), 328 MAP_PRIVATE, bd->fd, 0)) == MAP_FAILED) 329 exit(_error(PERROR, "cannot map %s", bd->name)); 330 } 331 332 static void 333 unmap_benv(benv_des_t *bd) 334 { 335 if (munmap(bd->adr, bd->len) == -1) 336 exit(_error(PERROR, "unmap error on %s", bd->name)); 337 338 if (close(bd->fd) == -1) 339 exit(_error(PERROR, "close error on %s", bd->name)); 340 } 341 342 #define NL '\n' 343 #define COMM '#' 344 345 /* 346 * Add a comment block to the benv list. 347 */ 348 static void 349 add_comm(benv_des_t *bd, char *base, char *last, char **next, int *line) 350 { 351 int nl, lines; 352 char *p; 353 354 nl = 0; 355 for (p = base, lines = 0; p < last; p++) { 356 if (*p == NL) { 357 nl++; 358 lines++; 359 } else if (nl) { 360 if (*p != COMM) 361 break; 362 nl = 0; 363 } 364 } 365 *(p - 1) = '\0'; 366 add_bent(bd->elist, base, NULL, NULL, NULL); 367 *next = p; 368 *line += lines; 369 } 370 371 /* 372 * Parse out an operator (setprop) from the boot environment 373 */ 374 static char * 375 parse_cmd(benv_des_t *bd, char **next, int *line) 376 { 377 char *strbegin; 378 char *badeof = "unexpected EOF in %s line %d"; 379 char *syntax = "syntax error in %s line %d"; 380 char *c = *next; 381 382 /* 383 * Skip spaces or tabs. New lines increase the line count. 384 */ 385 while (isspace(*c)) { 386 if (*c++ == '\n') 387 (*line)++; 388 } 389 390 /* 391 * Check for a the setprop command. Currently that's all we 392 * seem to support. 393 * 394 * XXX need support for setbinprop? 395 */ 396 397 /* 398 * Check first for end of file. Finding one now would be okay. 399 * We should also bail if we are at the start of a comment. 400 */ 401 if (*c == '\0' || *c == COMM) { 402 *next = c; 403 return (NULL); 404 } 405 406 strbegin = c; 407 while (*c && !isspace(*c)) 408 c++; 409 410 /* 411 * Check again for end of file. Finding one now would NOT be okay. 412 */ 413 if (*c == '\0') { 414 exit(_error(NO_PERROR, badeof, bd->name, *line)); 415 } 416 417 *c++ = '\0'; 418 *next = c; 419 420 /* 421 * Last check is to make sure the command is a setprop! 422 */ 423 if (strcmp(strbegin, "setprop") != 0) { 424 exit(_error(NO_PERROR, syntax, bd->name, *line)); 425 /* NOTREACHED */ 426 } 427 return (strbegin); 428 } 429 430 /* 431 * Parse out the name (LHS) of a setprop from the boot environment 432 */ 433 static char * 434 parse_name(benv_des_t *bd, char **next, int *line) 435 { 436 char *strbegin; 437 char *badeof = "unexpected EOF in %s line %d"; 438 char *syntax = "syntax error in %s line %d"; 439 char *c = *next; 440 441 /* 442 * Skip spaces or tabs. No tolerance for new lines now. 443 */ 444 while (isspace(*c)) { 445 if (*c++ == '\n') 446 exit(_error(NO_PERROR, syntax, bd->name, *line)); 447 } 448 449 /* 450 * Grab a name for the property to set. 451 */ 452 453 /* 454 * Check first for end of file. Finding one now would NOT be okay. 455 */ 456 if (*c == '\0') { 457 exit(_error(NO_PERROR, badeof, bd->name, *line)); 458 } 459 460 strbegin = c; 461 while (*c && !isspace(*c)) 462 c++; 463 464 /* 465 * At this point in parsing we have 'setprop name'. What follows 466 * is a newline, other whitespace, or EOF. Most of the time we 467 * want to replace a white space character with a NULL to terminate 468 * the name, and then continue on processing. A newline here provides 469 * the most grief. If we just replace it with a null we'll 470 * potentially get the setprop on the next line as the value of this 471 * setprop! So, if the last thing we see is a newline we'll have to 472 * dup the string. 473 */ 474 if (isspace(*c)) { 475 if (*c == '\n') { 476 *c = '\0'; 477 strbegin = strdup(strbegin); 478 *c = '\n'; 479 } else { 480 *c++ = '\0'; 481 } 482 } 483 484 *next = c; 485 return (strbegin); 486 } 487 488 /* 489 * Parse out the value (RHS) of a setprop line from the boot environment 490 */ 491 static char * 492 parse_value(benv_des_t *bd, char **next, int *line) 493 { 494 char *strbegin; 495 char *badeof = "unexpected EOF in %s line %d"; 496 char *result; 497 char *c = *next; 498 char quote; 499 500 /* 501 * Skip spaces or tabs. A newline here would indicate a 502 * NULL property value. 503 */ 504 while (isspace(*c)) { 505 if (*c++ == '\n') { 506 (*line)++; 507 *next = c; 508 return (NULL); 509 } 510 } 511 512 /* 513 * Grab the value of the property to set. 514 */ 515 516 /* 517 * Check first for end of file. Finding one now would 518 * also indicate a NULL property. 519 */ 520 if (*c == '\0') { 521 *next = c; 522 return (NULL); 523 } 524 525 /* 526 * Value may be quoted, in which case we assume the end of the value 527 * comes with a closing quote. 528 * 529 * We also allow escaped quote characters inside the quoted value. 530 * 531 * For obvious reasons we do not attempt to parse variable references. 532 */ 533 if (*c == '"' || *c == '\'') { 534 quote = *c; 535 c++; 536 strbegin = c; 537 result = c; 538 while (*c != quote) { 539 if (*c == '\\') { 540 c++; 541 } 542 if (*c == '\0') { 543 break; 544 } 545 *result++ = *c++; 546 } 547 548 /* 549 * Throw fatal exception if no end quote found. 550 */ 551 if (*c != quote) { 552 exit(_error(NO_PERROR, badeof, bd->name, *line)); 553 } 554 555 *result = '\0'; /* Terminate the result */ 556 c++; /* and step past the close quote */ 557 } else { 558 strbegin = c; 559 while (*c && !isspace(*c)) 560 c++; 561 } 562 563 /* 564 * Check again for end of file. Finding one now is okay. 565 */ 566 if (*c == '\0') { 567 *next = c; 568 return (strbegin); 569 } 570 571 *c++ = '\0'; 572 *next = c; 573 return (strbegin); 574 } 575 576 /* 577 * Add a command to the benv list. 578 */ 579 static void 580 add_cmd(benv_des_t *bd, char *last, char **next, int *line) 581 { 582 char *cmd, *name, *val; 583 584 while (*next <= last && **next != COMM) { 585 if ((cmd = parse_cmd(bd, next, line)) == NULL) 586 break; 587 name = parse_name(bd, next, line); 588 val = parse_value(bd, next, line); 589 add_bent(bd->elist, NULL, cmd, name, val); 590 (*line)++; 591 }; 592 593 } 594 595 /* 596 * Parse the benv (bootenv.rc) file and break it into a benv 597 * list. List entries may be comment blocks or commands. 598 */ 599 static void 600 parse_benv(benv_des_t *bd) 601 { 602 int line; 603 char *pbase, *pend; 604 char *tok, *tnext; 605 606 line = 1; 607 pbase = (char *)bd->adr; 608 pend = pbase + bd->len; 609 610 for (tok = tnext = pbase; tnext < pend && '\0' != *tnext; tok = tnext) 611 if (*tok == COMM) 612 add_comm(bd, tok, pend, &tnext, &line); 613 else 614 add_cmd(bd, pend, &tnext, &line); 615 } 616 617 static void 618 write_benv(benv_des_t *bd) 619 { 620 FILE *fp; 621 eplist_t *list, *e; 622 benv_ent_t *bent; 623 char *name; 624 625 list = bd->elist; 626 627 if (list->next == list) 628 return; 629 630 if ((fp = fopen(bd->name, "w")) == NULL) 631 exit(_error(PERROR, "cannot open %s", bd->name)); 632 633 for (e = list->next; e != list; e = e->next) { 634 bent = (benv_ent_t *)e->item; 635 name = bent->name; 636 if (name) { 637 if (bent->val) { 638 (void) fprintf(fp, "%s %s ", 639 bent->cmd, bent->name); 640 put_quoted(fp, bent->val); 641 (void) fprintf(fp, "\n"); 642 } else { 643 (void) fprintf(fp, "%s %s\n", 644 bent->cmd, bent->name); 645 } 646 } else { 647 (void) fprintf(fp, "%s\n", bent->cmd); 648 } 649 } 650 651 (void) fclose(fp); 652 } 653 654 static char * 655 get_line(void) 656 { 657 int c; 658 char *nl; 659 static char line[256]; 660 661 if (fgets(line, sizeof (line), stdin) != NULL) { 662 /* 663 * Remove newline if present, 664 * otherwise discard rest of line. 665 */ 666 if (nl = strchr(line, '\n')) 667 *nl = 0; 668 else 669 while ((c = getchar()) != '\n' && c != EOF) 670 ; 671 return (line); 672 } else 673 return (NULL); 674 } 675 676 static int 677 add_pair(const char *name, const char *nvlist, const char *key, 678 const char *type, const char *value) 679 { 680 void *data, *nv; 681 size_t size; 682 int rv; 683 char *end; 684 685 rv = lzbe_nvlist_get(name, nvlist, &nv); 686 if (rv != 0) 687 return (rv); 688 689 data = NULL; 690 rv = EINVAL; 691 if (strcmp(type, "DATA_TYPE_STRING") == 0) { 692 data = (void *)(uintptr_t)value; 693 size = strlen(data) + 1; 694 rv = lzbe_add_pair(nv, key, type, data, size); 695 } else if (strcmp(type, "DATA_TYPE_UINT64") == 0) { 696 uint64_t v; 697 698 v = strtoull(value, &end, 0); 699 if (errno != 0 || *end != '\0') 700 goto done; 701 size = sizeof (v); 702 rv = lzbe_add_pair(nv, key, type, &v, size); 703 } else if (strcmp(type, "DATA_TYPE_INT64") == 0) { 704 int64_t v; 705 706 v = strtoll(value, &end, 0); 707 if (errno != 0 || *end != '\0') 708 goto done; 709 size = sizeof (v); 710 rv = lzbe_add_pair(nv, key, type, &v, size); 711 } else if (strcmp(type, "DATA_TYPE_UINT32") == 0) { 712 u_longlong_t lv; 713 uint32_t v; 714 715 lv = strtoull(value, &end, 0); 716 if (errno != 0 || *end != '\0') 717 goto done; 718 if (lv > UINT32_MAX) 719 goto done; 720 v = lv; 721 size = sizeof (v); 722 rv = lzbe_add_pair(nv, key, type, &v, size); 723 } else if (strcmp(type, "DATA_TYPE_INT32") == 0) { 724 longlong_t lv; 725 int32_t v; 726 727 lv = strtoll(value, &end, 0); 728 if (errno != 0 || *end != '\0') 729 goto done; 730 if (lv < INT32_MIN || lv > INT32_MAX) 731 goto done; 732 v = lv; 733 size = sizeof (v); 734 rv = lzbe_add_pair(nv, key, type, &v, size); 735 } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) { 736 uint32_t lv; 737 uint16_t v; 738 739 lv = strtoul(value, &end, 0); 740 if (errno != 0 || *end != '\0') 741 goto done; 742 if (lv > UINT16_MAX) 743 goto done; 744 v = lv; 745 size = sizeof (v); 746 rv = lzbe_add_pair(nv, key, type, &v, size); 747 } else if (strcmp(type, "DATA_TYPE_INT16") == 0) { 748 int32_t lv; 749 int16_t v; 750 751 v = strtol(value, &end, 0); 752 if (errno != 0 || *end != '\0') 753 goto done; 754 if (lv < INT16_MIN || lv > INT16_MAX) 755 goto done; 756 v = lv; 757 size = sizeof (v); 758 rv = lzbe_add_pair(nv, key, type, &v, size); 759 } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) { 760 uint32_t lv; 761 uint8_t v; 762 763 lv = strtoul(value, &end, 0); 764 if (errno != 0 || *end != '\0') 765 goto done; 766 if (lv > UINT8_MAX) 767 goto done; 768 v = lv; 769 size = sizeof (v); 770 rv = lzbe_add_pair(nv, key, type, &v, size); 771 } else if (strcmp(type, "DATA_TYPE_INT8") == 0) { 772 int32_t lv; 773 int8_t v; 774 775 lv = strtol(value, &end, 0); 776 if (errno != 0 || *end != '\0') 777 goto done; 778 if (lv < INT8_MIN || lv > INT8_MAX) 779 goto done; 780 v = lv; 781 size = sizeof (v); 782 rv = lzbe_add_pair(nv, key, type, &v, size); 783 } else if (strcmp(type, "DATA_TYPE_BYTE") == 0) { 784 uint32_t lv; 785 uint8_t v; 786 787 lv = strtoul(value, &end, 0); 788 if (errno != 0 || *end != '\0') 789 goto done; 790 if (lv > UINT8_MAX) 791 goto done; 792 v = lv; 793 size = sizeof (v); 794 rv = lzbe_add_pair(nv, key, type, &v, size); 795 } else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) { 796 int32_t v; 797 798 v = strtol(value, &end, 0); 799 if (errno != 0 || *end != '\0') { 800 if (strcasecmp(value, "YES") == 0) 801 v = 1; 802 else if (strcasecmp(value, "NO") == 0) 803 v = 0; 804 else if (strcasecmp(value, "true") == 0) 805 v = 1; 806 else if (strcasecmp(value, "false") == 0) 807 v = 0; 808 else goto done; 809 } 810 size = sizeof (v); 811 rv = lzbe_add_pair(nv, key, type, &v, size); 812 } 813 814 if (rv == 0) 815 rv = lzbe_nvlist_set(name, nvlist, nv); 816 817 done: 818 lzbe_nvlist_free(nv); 819 return (rv); 820 } 821 822 static int 823 delete_pair(const char *name, const char *nvlist, const char *key) 824 { 825 void *nv; 826 int rv; 827 828 rv = lzbe_nvlist_get(name, nvlist, &nv); 829 if (rv == 0) 830 rv = lzbe_remove_pair(nv, key); 831 832 if (rv == 0) 833 rv = lzbe_nvlist_set(name, nvlist, nv); 834 835 lzbe_nvlist_free(nv); 836 return (rv); 837 } 838 839 static int 840 usage(char *name) 841 { 842 char *usage = "Usage: %s [-v] [-f prom-device]" 843 " [variable[=value] ...]\n" 844 "%s [-z pool] [-d key] [-k key -t type -v value] [-p]\n" 845 "%s [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]\n"; 846 847 return (_error(NO_PERROR, usage, name, name, name)); 848 } 849 850 int 851 main(int argc, char **argv) 852 { 853 int c; 854 int updates = 0; 855 eplist_t *elist; 856 benv_des_t *bd; 857 char *file = NULL; 858 bool bootenv, bootenv_print, bootenv_delete; 859 char *name, *key, *type, *nvlist, *value; 860 lzbe_flags_t flag = lzbe_add; 861 862 nvlist = NULL; 863 name = "rpool"; 864 key = NULL; 865 type = NULL; 866 value = NULL; 867 bootenv = false; 868 bootenv_print = false; 869 bootenv_delete = false; 870 871 setpname(argv[0]); 872 873 while ((c = getopt(argc, argv, "bd:f:k:n:prt:v:z:")) != -1) 874 switch (c) { 875 case 'b': 876 bootenv = true; 877 break; 878 case 'd': 879 if (bootenv) { 880 bootenv_delete = true; 881 key = optarg; 882 } else { 883 exit(usage(argv[0])); 884 } 885 break; 886 case 'f': 887 file = optarg; 888 break; 889 case 'k': 890 if (bootenv) 891 key = optarg; 892 else 893 exit(usage(argv[0])); 894 break; 895 case 'n': 896 if (bootenv) 897 nvlist = optarg; 898 else 899 exit(usage(argv[0])); 900 break; 901 case 'p': 902 if (bootenv) 903 bootenv_print = true; 904 else 905 exit(usage(argv[0])); 906 break; 907 case 'r': 908 if (bootenv) 909 flag = lzbe_replace; 910 else 911 exit(usage(argv[0])); 912 break; 913 case 't': 914 if (bootenv) 915 type = optarg; 916 else 917 test++; 918 break; 919 case 'v': 920 if (bootenv) 921 value = optarg; 922 else 923 verbose++; 924 break; 925 case 'z': 926 if (bootenv) 927 name = optarg; 928 else 929 exit(usage(argv[0])); 930 break; 931 default: 932 exit(usage(argv[0])); 933 } 934 935 argc -= optind; 936 argv += optind; 937 938 if (bootenv) { 939 int rv = 0; 940 941 if (argc == 1) 942 value = argv[0]; 943 944 if (bootenv_print) 945 return (lzbe_bootenv_print(name, nvlist, stdout)); 946 947 if (key != NULL || value != NULL) { 948 if (type == NULL) 949 type = "DATA_TYPE_STRING"; 950 951 if (bootenv_delete) 952 rv = delete_pair(name, nvlist, key); 953 else if (key == NULL) 954 rv = lzbe_set_boot_device(name, flag, value); 955 else 956 rv = add_pair(name, nvlist, key, type, value); 957 958 if (rv == 0) 959 printf("zfs bootenv is successfully written\n"); 960 else 961 printf("error: %s\n", strerror(rv)); 962 } 963 return (rv); 964 } 965 966 (void) uname(&uts_buf); 967 bd = new_bd(); 968 init_benv(bd, file); 969 970 map_benv(bd); 971 if (bd->len) { 972 parse_benv(bd); 973 unmap_benv(bd); 974 } 975 976 elist = bd->elist; 977 978 if (optind >= argc) { 979 print_vars(elist); 980 return (0); 981 } else 982 while (optind < argc) { 983 /* 984 * If "-" specified, read variables from stdin; 985 * otherwise, process each argument as a variable 986 * print or set request. 987 */ 988 if (strcmp(argv[optind], "-") == 0) { 989 char *line; 990 991 while ((line = get_line()) != NULL) 992 updates += proc_var(line, elist); 993 clearerr(stdin); 994 } else 995 updates += proc_var(argv[optind], elist); 996 997 optind++; 998 } 999 1000 /* 1001 * don't write benv if we are processing delayed writes since 1002 * it is likely that the delayed writes changes bootenv.rc anyway... 1003 */ 1004 if (updates) 1005 write_benv(bd); 1006 close_kbenv(); 1007 1008 return (0); 1009 } 1010