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 *
strcats(char * s,...)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 *
new_list(void)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
add_item(void * item,eplist_t * list)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 *
new_bd(void)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 *
new_bent(char * comm,char * cmd,char * name,char * val)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
add_bent(eplist_t * list,char * comm,char * cmd,char * name,char * val)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 *
get_var(char * name,eplist_t * list)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
print_var(char * name,eplist_t * list)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
print_vars(eplist_t * list)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
put_quoted(FILE * fp,char * val)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
set_var(char * name,char * val,eplist_t * list)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
proc_var(char * name,eplist_t * list)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
init_benv(benv_des_t * bd,char * file)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
map_benv(benv_des_t * bd)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
unmap_benv(benv_des_t * bd)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
add_comm(benv_des_t * bd,char * base,char * last,char ** next,int * line)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 *
parse_cmd(benv_des_t * bd,char ** next,int * line)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 *
parse_name(benv_des_t * bd,char ** next,int * line)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 *
parse_value(benv_des_t * bd,char ** next,int * line)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
add_cmd(benv_des_t * bd,char * last,char ** next,int * line)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
parse_benv(benv_des_t * bd)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
write_benv(benv_des_t * bd)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 *
get_line(void)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
add_pair(const char * name,const char * nvlist,const char * key,const char * type,const char * value)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 lv = 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
delete_pair(const char * name,const char * nvlist,const char * key)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
usage(char * name)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
main(int argc,char ** argv)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 optind = 0;
938
939 if (bootenv) {
940 int rv = 0;
941
942 if (argc == 1)
943 value = argv[optind];
944
945 if (bootenv_print)
946 return (lzbe_bootenv_print(name, nvlist, stdout));
947
948 if (key != NULL || value != NULL) {
949 if (type == NULL)
950 type = "DATA_TYPE_STRING";
951
952 if (bootenv_delete)
953 rv = delete_pair(name, nvlist, key);
954 else if (key == NULL)
955 rv = lzbe_set_boot_device(name, flag, value);
956 else
957 rv = add_pair(name, nvlist, key, type, value);
958
959 if (rv == 0)
960 printf("zfs bootenv is successfully written\n");
961 else
962 printf("error: %s\n", strerror(rv));
963 }
964 return (rv);
965 }
966
967 (void) uname(&uts_buf);
968 bd = new_bd();
969 init_benv(bd, file);
970
971 map_benv(bd);
972 if (bd->len) {
973 parse_benv(bd);
974 unmap_benv(bd);
975 }
976
977 elist = bd->elist;
978
979 if (optind >= argc) {
980 print_vars(elist);
981 return (0);
982 } else {
983 while (optind < argc) {
984 /*
985 * If "-" specified, read variables from stdin;
986 * otherwise, process each argument as a variable
987 * print or set request.
988 */
989 if (strcmp(argv[optind], "-") == 0) {
990 char *line;
991
992 while ((line = get_line()) != NULL)
993 updates += proc_var(line, elist);
994 clearerr(stdin);
995 } else
996 updates += proc_var(argv[optind], elist);
997
998 optind++;
999 }
1000 }
1001
1002 /*
1003 * don't write benv if we are processing delayed writes since
1004 * it is likely that the delayed writes changes bootenv.rc anyway...
1005 */
1006 if (updates)
1007 write_benv(bd);
1008 close_kbenv();
1009
1010 return (0);
1011 }
1012