xref: /illumos-gate/usr/src/cmd/eeprom/i386/benv.c (revision 609febc9a48c79a089214cb5d882759a72a38513)
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 		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
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 	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